From fa0eea427f1fd9b677516c4750624b6b9ca7b4b4 Mon Sep 17 00:00:00 2001 From: liujunGH <46551088+liujunGH@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:31:39 +0800 Subject: [PATCH 1/3] perf(web): bound DOM growth in long sessions - ToolOutputBlock: outputs beyond 80 lines render tail-biased behind an expand-to-full button (i18n) instead of thousands of divs - ToolRow/ToolGroup: unmount collapsible bodies when closed (they stayed mounted under grid 0fr); ToolGroup defaults to collapsed and auto-opens while tools are running - scrollKey: track output growth via line count + tail-line length instead of joining every tool output per change - drop characterData from the pane MutationObserver so per-token text updates no longer fire follow/hit-test work at delta rate --- .../src/components/chat/ConversationPane.vue | 14 ++++++- .../src/components/chat/ToolGroup.vue | 10 +++-- apps/kimi-web/src/components/chat/ToolRow.vue | 4 +- .../chat/tool-calls/ToolOutputBlock.vue | 42 +++++++++++++++++-- apps/kimi-web/src/i18n/locales/en/tools.ts | 3 ++ apps/kimi-web/src/i18n/locales/zh/tools.ts | 3 ++ 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/apps/kimi-web/src/components/chat/ConversationPane.vue b/apps/kimi-web/src/components/chat/ConversationPane.vue index 1b6b1a9e79..ea78d4f23e 100644 --- a/apps/kimi-web/src/components/chat/ConversationPane.vue +++ b/apps/kimi-web/src/components/chat/ConversationPane.vue @@ -805,7 +805,14 @@ const scrollKey = computed(() => { const thinkingLen = last?.thinking?.length ?? 0; const toolsLen = last?.tools?.reduce( - (n, tool) => n + tool.name.length + (tool.arg?.length ?? 0) + (tool.output?.join('').length ?? 0), + // Track output growth via line count + tail-line length instead of + // joining every tool output on each change. + (n, tool) => + n + + tool.name.length + + (tool.arg?.length ?? 0) + + (tool.output?.length ?? 0) + + (tool.output?.at(-1)?.length ?? 0), 0, ) ?? 0; return { @@ -1126,7 +1133,10 @@ function rebindScrollObservers(): void { updatePanesScrollbarWidth(); if (contentObserver) { contentObserver.disconnect(); - if (el) contentObserver.observe(el, { childList: true, subtree: true, characterData: true }); + // Drop characterData: per-token text updates during streaming fired + // this observer at delta rate; structural changes are enough, and the + // scroll follow is driven by the reactive scrollKey watcher anyway. + if (el) contentObserver.observe(el, { childList: true, subtree: true }); } if (resizeObserver) { resizeObserver.disconnect(); diff --git a/apps/kimi-web/src/components/chat/ToolGroup.vue b/apps/kimi-web/src/components/chat/ToolGroup.vue index 8a30af9bea..977bdc9ad1 100644 --- a/apps/kimi-web/src/components/chat/ToolGroup.vue +++ b/apps/kimi-web/src/components/chat/ToolGroup.vue @@ -25,14 +25,17 @@ const emit = defineEmits<{ openAgent: [toolCallId: string]; }>(); -const open = ref(true); - const count = computed(() => props.tools.length); const aggregateStatus = computed<'running' | 'error' | 'done'>(() => { if (props.tools.some((t) => t.tool.status === 'running')) return 'running'; if (props.tools.some((t) => t.tool.status === 'error')) return 'error'; return 'done'; }); + +// kimi-ui: groups default to collapsed (they used to default open, mounting +// every tool call body of every past turn). A group with tools still running +// opens itself so live execution stays visible. +const open = ref(aggregateStatus.value === 'running'); const { t } = useI18n(); const statusLabel = computed(() => { @@ -70,7 +73,8 @@ function onHeadClick(): void {
-
+ +
-
+ +
diff --git a/apps/kimi-web/src/components/chat/tool-calls/ToolOutputBlock.vue b/apps/kimi-web/src/components/chat/tool-calls/ToolOutputBlock.vue index 3f7059556e..55e5833fc2 100644 --- a/apps/kimi-web/src/components/chat/tool-calls/ToolOutputBlock.vue +++ b/apps/kimi-web/src/components/chat/tool-calls/ToolOutputBlock.vue @@ -1,24 +1,42 @@ + viewport while preserving the tool card's normal typography. + kimi-ui: outputs beyond TRUNCATE_LINE_COUNT render tail-biased (the last + N lines, where errors/results live) behind an expand button, so huge logs + no longer put thousands of divs in the DOM. --> @@ -39,4 +57,20 @@ const outputStyle = { '--tool-output-visible-lines': String(OUTPUT_SCROLL_LINE_C color: var(--color-text-muted); font-style: italic; } +.truncate-toggle { + display: block; + width: 100%; + margin: 0 0 var(--space-2); + padding: var(--space-1) 0; + border: none; + border-bottom: 1px dashed var(--color-line); + background: transparent; + color: var(--color-accent); + font: var(--text-xs) var(--font-ui); + cursor: pointer; + text-align: center; +} +.truncate-toggle:hover { + text-decoration: underline; +} diff --git a/apps/kimi-web/src/i18n/locales/en/tools.ts b/apps/kimi-web/src/i18n/locales/en/tools.ts index 13cc18211f..3148b81917 100644 --- a/apps/kimi-web/src/i18n/locales/en/tools.ts +++ b/apps/kimi-web/src/i18n/locales/en/tools.ts @@ -53,6 +53,9 @@ export default { error: 'failed', done: 'done', }, + output: { + showAll: 'Show all {count} lines', + }, ask: { dismissed: 'Dismissed', answer: '{count} answer', diff --git a/apps/kimi-web/src/i18n/locales/zh/tools.ts b/apps/kimi-web/src/i18n/locales/zh/tools.ts index f9560e3da9..8561299410 100644 --- a/apps/kimi-web/src/i18n/locales/zh/tools.ts +++ b/apps/kimi-web/src/i18n/locales/zh/tools.ts @@ -53,6 +53,9 @@ export default { error: '有失败', done: '已完成', }, + output: { + showAll: '显示全部 {count} 行', + }, ask: { dismissed: '已忽略', answer: '{count} 个回答', From 5aca89f7aa867ea758d28d95384dc1994145f6c2 Mon Sep 17 00:00:00 2001 From: liujunGH <46551088+liujunGH@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:40:18 +0800 Subject: [PATCH 2/3] chore: add changeset --- .changeset/web-bound-dom-growth.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/web-bound-dom-growth.md diff --git a/.changeset/web-bound-dom-growth.md b/.changeset/web-bound-dom-growth.md new file mode 100644 index 0000000000..927f545728 --- /dev/null +++ b/.changeset/web-bound-dom-growth.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +web: Bound DOM growth in long sessions — tail-biased truncation with expand-to-full for long tool outputs, collapsible tool bodies unmount when closed, tool groups default to collapsed (auto-open while running), and lighter scroll-follow hot paths. From 27b68347f869e3cdcd9036cd52b883a7a29e1e67 Mon Sep 17 00:00:00 2001 From: liujunGH <46551088+liujunGH@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:46:01 +0800 Subject: [PATCH 3/3] fix(web): reopen collapsed tool groups when tools start running Address Codex review: open was initialized once at mount, so a stack that mounted completed (collapsed) and later gained a running tool kept the live rows unmounted. Watch aggregateStatus and reopen on transitions to running. --- apps/kimi-web/src/components/chat/ToolGroup.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/kimi-web/src/components/chat/ToolGroup.vue b/apps/kimi-web/src/components/chat/ToolGroup.vue index 977bdc9ad1..4f051814c3 100644 --- a/apps/kimi-web/src/components/chat/ToolGroup.vue +++ b/apps/kimi-web/src/components/chat/ToolGroup.vue @@ -1,6 +1,6 @@