fix(chat): run assistant typewriter only once per message
This commit is contained in:
parent
ec63e28887
commit
7ce2857aff
@ -177,6 +177,7 @@ function filterAgentRoutingNoise(text: string): string {
|
||||
const IMAGE_DONE_SUMMARY = "已完成本次生成,如需微调可继续告诉我。";
|
||||
const ASSISTANT_TYPEWRITER_SECTION_REGEX =
|
||||
/(?:###\s*需求理解|【\s*需求理解\s*】|###\s*图片分析|【\s*图片分析\s*】|###\s*执行计划|【\s*执行计划\s*】|###\s*输出设置|【\s*输出设置\s*】)/;
|
||||
const ASSISTANT_TYPEWRITER_STORAGE_KEY = "assistant-typewriter-completed-keys-v1";
|
||||
const MODEL_BADGE_LINE_REGEX =
|
||||
/^\s*[◈◇◆🔹⬡✦•·]\s*([A-Za-z][A-Za-z0-9 .:+\-_/()]{2,})\s*$/im;
|
||||
const MODEL_BADGE_LINE_GLOBAL_REGEX =
|
||||
@ -221,6 +222,60 @@ function shouldUseAssistantTypewriter(text: string): boolean {
|
||||
}
|
||||
|
||||
const completedTypewriterKeys = new Set<string>();
|
||||
let completedTypewriterKeysLoaded = false;
|
||||
|
||||
function loadCompletedTypewriterKeys() {
|
||||
if (completedTypewriterKeysLoaded) return;
|
||||
completedTypewriterKeysLoaded = true;
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
try {
|
||||
const raw = window.localStorage.getItem(ASSISTANT_TYPEWRITER_STORAGE_KEY);
|
||||
if (!raw) return;
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) return;
|
||||
parsed.forEach((key) => {
|
||||
if (typeof key === "string" && key.trim()) {
|
||||
completedTypewriterKeys.add(key);
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function persistCompletedTypewriterKeys() {
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
const keep = Array.from(completedTypewriterKeys).slice(-400);
|
||||
window.localStorage.setItem(
|
||||
ASSISTANT_TYPEWRITER_STORAGE_KEY,
|
||||
JSON.stringify(keep)
|
||||
);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function hasCompletedTypewriter(typingKey: string) {
|
||||
loadCompletedTypewriterKeys();
|
||||
return completedTypewriterKeys.has(typingKey);
|
||||
}
|
||||
|
||||
function markTypewriterCompleted(typingKey: string) {
|
||||
loadCompletedTypewriterKeys();
|
||||
completedTypewriterKeys.add(typingKey);
|
||||
if (completedTypewriterKeys.size > 450) {
|
||||
const overflow = completedTypewriterKeys.size - 400;
|
||||
const iterator = completedTypewriterKeys.values();
|
||||
for (let i = 0; i < overflow; i += 1) {
|
||||
const next = iterator.next();
|
||||
if (next.done) break;
|
||||
completedTypewriterKeys.delete(next.value);
|
||||
}
|
||||
}
|
||||
persistCompletedTypewriterKeys();
|
||||
}
|
||||
|
||||
function AssistantTypewriter({
|
||||
typingKey,
|
||||
@ -232,9 +287,10 @@ function AssistantTypewriter({
|
||||
children: (displayText: string) => ReactNode;
|
||||
}) {
|
||||
const content = String(text || "");
|
||||
const [displayText, setDisplayText] = useState(() =>
|
||||
completedTypewriterKeys.has(typingKey) ? content : ""
|
||||
);
|
||||
const [displayText, setDisplayText] = useState(() => {
|
||||
if (typeof window === "undefined") return content;
|
||||
return hasCompletedTypewriter(typingKey) ? content : "";
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!content) {
|
||||
@ -242,7 +298,7 @@ function AssistantTypewriter({
|
||||
return;
|
||||
}
|
||||
|
||||
if (completedTypewriterKeys.has(typingKey)) {
|
||||
if (hasCompletedTypewriter(typingKey)) {
|
||||
setDisplayText(content);
|
||||
return;
|
||||
}
|
||||
@ -259,7 +315,7 @@ function AssistantTypewriter({
|
||||
setDisplayText(content.slice(0, cursor));
|
||||
if (cursor >= content.length) {
|
||||
window.clearInterval(timer);
|
||||
completedTypewriterKeys.add(typingKey);
|
||||
markTypewriterCompleted(typingKey);
|
||||
}
|
||||
}, tickMs);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user