From f60feec03a33749df1b9ad3cf81bb81c0d6d8f95 Mon Sep 17 00:00:00 2001 From: kimi-agent-bot Date: Sat, 29 Aug 2026 10:08:09 +0000 Subject: [PATCH] fix(cli): use the unicode ellipsis in user-facing interface text --- .changeset/tui-unicode-ellipsis.md | 6 +++ apps/kimi-code/src/cli/sub/login-flow.ts | 2 +- apps/kimi-code/src/tui/commands/plugins.ts | 2 +- .../src/tui/components/dialogs/compaction.ts | 2 +- .../tui/components/dialogs/question-dialog.ts | 2 +- .../tui/components/dialogs/session-picker.ts | 2 +- .../messages/agent-swarm-progress.ts | 10 ++-- .../src/tui/components/messages/shell-run.ts | 2 +- .../src/tui/components/messages/thinking.ts | 4 +- .../src/tui/components/messages/tool-call.ts | 8 +-- .../messages/tool-renderers/truncated.ts | 6 +-- .../src/tui/components/panes/btw-panel.ts | 4 +- apps/kimi-code/src/tui/kimi-tui.ts | 2 +- .../src/tui/utils/background-agent-status.ts | 2 +- .../src/tui/utils/background-task-status.ts | 2 +- apps/kimi-code/test/tui/activity-pane.test.ts | 6 +-- .../tui/components/dialogs/compaction.test.ts | 8 +-- .../messages/agent-swarm-progress.test.ts | 50 +++++++++---------- .../messages/shell-execution.test.ts | 8 +-- .../tui/components/messages/shell-run.test.ts | 8 +-- .../tui/components/messages/thinking.test.ts | 10 ++-- .../tui/components/messages/tool-call.test.ts | 2 +- .../messages/tool-renderers/registry.test.ts | 6 +-- .../messages/tool-renderers/truncated.test.ts | 6 +-- .../test/tui/kimi-tui-message-flow.test.ts | 26 +++++----- .../kimi-code/test/tui/message-replay.test.ts | 2 +- .../webview-ui/src/components/ActionMenu.tsx | 2 +- .../webview-ui/src/components/ChatMessage.tsx | 2 +- .../src/components/CompactionCard.tsx | 2 +- .../src/components/ConfigErrorScreen.tsx | 2 +- .../src/components/FilePickerMenu.tsx | 8 +-- .../webview-ui/src/components/LoginScreen.tsx | 2 +- .../src/components/MCPServersModal.tsx | 4 +- .../src/components/QuestionDialog.tsx | 4 +- .../webview-ui/src/components/SessionList.tsx | 4 +- .../src/components/StreamingConfirmDialog.tsx | 2 +- .../src/components/ToolRenderers.tsx | 2 +- .../src/components/inputarea/InputArea.tsx | 2 +- .../webview-ui/src/components/ui/command.tsx | 2 +- 39 files changed, 116 insertions(+), 110 deletions(-) create mode 100644 .changeset/tui-unicode-ellipsis.md diff --git a/.changeset/tui-unicode-ellipsis.md b/.changeset/tui-unicode-ellipsis.md new file mode 100644 index 00000000000..f7507d5db6e --- /dev/null +++ b/.changeset/tui-unicode-ellipsis.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/kimi-code": patch +"kimi-code": patch +--- + +Use the Unicode ellipsis "…" in user-facing TUI and VS Code webview text. diff --git a/apps/kimi-code/src/cli/sub/login-flow.ts b/apps/kimi-code/src/cli/sub/login-flow.ts index 498f83421fd..d10864a428d 100644 --- a/apps/kimi-code/src/cli/sub/login-flow.ts +++ b/apps/kimi-code/src/cli/sub/login-flow.ts @@ -53,7 +53,7 @@ export async function runLoginFlow(options: { region?: KimiRegion } = {}): Promi data.expiresIn !== null && data.expiresIn !== undefined ? `Code expires in ${data.expiresIn}s.` : undefined, - 'Waiting for authorization to complete...', + 'Waiting for authorization to complete…', '', ] .filter((line): line is string => line !== undefined) diff --git a/apps/kimi-code/src/tui/commands/plugins.ts b/apps/kimi-code/src/tui/commands/plugins.ts index 80c8931a8aa..6ee5b731f94 100644 --- a/apps/kimi-code/src/tui/commands/plugins.ts +++ b/apps/kimi-code/src/tui/commands/plugins.ts @@ -634,7 +634,7 @@ async function installFromPanel( if (official) { panel.setInstalling(truncateForStatus(label)); } else { - host.showStatus(`Installing or updating ${label} from marketplace...`); + host.showStatus(`Installing or updating ${label} from marketplace…`); } host.state.ui.requestRender(); try { diff --git a/apps/kimi-code/src/tui/components/dialogs/compaction.ts b/apps/kimi-code/src/tui/components/dialogs/compaction.ts index 9ade9350c78..52d8bb8ffda 100644 --- a/apps/kimi-code/src/tui/components/dialogs/compaction.ts +++ b/apps/kimi-code/src/tui/components/dialogs/compaction.ts @@ -165,7 +165,7 @@ export class CompactionComponent extends Container { return `${bullet}${label}`; } const bullet = this.blinkOn ? currentTheme.fg('text', STATUS_BULLET) : ' '; - const label = currentTheme.boldFg('primary', 'Compacting context...'); + const label = currentTheme.boldFg('primary', 'Compacting context…'); const tip = this.tip ? currentTheme.fg('textDim', ` · Tip: ${this.tip}`) : ''; return `${bullet}${label}${tip}`; } diff --git a/apps/kimi-code/src/tui/components/dialogs/question-dialog.ts b/apps/kimi-code/src/tui/components/dialogs/question-dialog.ts index 68187349c13..e1d5a41bc9e 100644 --- a/apps/kimi-code/src/tui/components/dialogs/question-dialog.ts +++ b/apps/kimi-code/src/tui/components/dialogs/question-dialog.ts @@ -469,7 +469,7 @@ export class QuestionDialogComponent extends Container implements Focusable { appendWrapped(lines, ' ', ' ', bodyLine, renderWidth, dim); } if (bodyLines.length > visibleBodyLines.length) { - lines.push(dim(` ... ${String(bodyLines.length - visibleBodyLines.length)} more lines`)); + lines.push(dim(` … ${String(bodyLines.length - visibleBodyLines.length)} more lines`)); } } diff --git a/apps/kimi-code/src/tui/components/dialogs/session-picker.ts b/apps/kimi-code/src/tui/components/dialogs/session-picker.ts index 75c86687ffc..0fc77224f40 100644 --- a/apps/kimi-code/src/tui/components/dialogs/session-picker.ts +++ b/apps/kimi-code/src/tui/components/dialogs/session-picker.ts @@ -265,7 +265,7 @@ export class SessionPickerComponent extends Container implements Focusable { if (this.loading) { lines.push(currentTheme.boldFg('primary', truncateToWidth(title, width, ELLIPSIS))); lines.push( - currentTheme.fg('textMuted', truncateToWidth('Loading sessions...', width, ELLIPSIS)), + currentTheme.fg('textMuted', truncateToWidth('Loading sessions…', width, ELLIPSIS)), ); lines.push(currentTheme.fg('primary', '─'.repeat(width))); return lines; diff --git a/apps/kimi-code/src/tui/components/messages/agent-swarm-progress.ts b/apps/kimi-code/src/tui/components/messages/agent-swarm-progress.ts index 41fab4a6c1f..c9d3909f2f1 100644 --- a/apps/kimi-code/src/tui/components/messages/agent-swarm-progress.ts +++ b/apps/kimi-code/src/tui/components/messages/agent-swarm-progress.ts @@ -33,15 +33,15 @@ const AGENT_SWARM_LEFT_INDENT = ' '; const AGENT_SWARM_RIGHT_GAP = 1; const AGENT_SWARM_NON_GRID_LINES = 6; const COMPACT_TERMINAL_MARK_WIDTH = 1; -const ORCHESTRATING_LABEL = 'Orchestrating...'; -const PROMPTING_LABEL = 'Prompting...'; -const WORKING_LABEL = 'Working...'; +const ORCHESTRATING_LABEL = 'Orchestrating…'; +const PROMPTING_LABEL = 'Prompting…'; +const WORKING_LABEL = 'Working…'; const COMPLETED_LABEL = 'Completed.'; const FAILED_LABEL = 'Failed.'; const ABORTED_LABEL = 'Aborted.'; const CANCELLED_LABEL = 'Cancelled.'; -const QUEUED_LABEL = 'Queued...'; -const SUSPENDED_LABEL = 'Rate limited...'; +const QUEUED_LABEL = 'Queued…'; +const SUSPENDED_LABEL = 'Rate limited…'; const RESUMED_ITEM_LABEL = '(resumed)'; const CANCELLED_LABEL_DARKEN_FACTOR = 0.72; const AGENT_SWARM_TITLE_ACCENT_BIAS = 1.3; diff --git a/apps/kimi-code/src/tui/components/messages/shell-run.ts b/apps/kimi-code/src/tui/components/messages/shell-run.ts index 747bc3860a1..88c02c19e5f 100644 --- a/apps/kimi-code/src/tui/components/messages/shell-run.ts +++ b/apps/kimi-code/src/tui/components/messages/shell-run.ts @@ -17,7 +17,7 @@ const TIMER_INTERVAL_MS = 1000; const MAX_COMBINED_CHARS = 256 * 1024; const KEEP_COMBINED_CHARS = 64 * 1024; -const TRUNCATED_RUNNING_NOTICE = '... (output truncated)'; +const TRUNCATED_RUNNING_NOTICE = '… (output truncated)'; /** * Live view for a user-initiated `!` shell command. Two phases: diff --git a/apps/kimi-code/src/tui/components/messages/thinking.ts b/apps/kimi-code/src/tui/components/messages/thinking.ts index 23a038c7071..e1208b55fe4 100644 --- a/apps/kimi-code/src/tui/components/messages/thinking.ts +++ b/apps/kimi-code/src/tui/components/messages/thinking.ts @@ -111,7 +111,7 @@ export class ThinkingComponent implements Component { ); rendered = [ '', - spinner + currentTheme.fg('textDim', 'thinking...'), + spinner + currentTheme.fg('textDim', 'thinking…'), ...visibleLines.map((line) => MESSAGE_INDENT + line), ]; } else { @@ -127,7 +127,7 @@ export class ThinkingComponent implements Component { // Leading blank + first PREVIEW_LINES content lines + hint line. const truncated = lines.slice(0, 1 + THINKING_PREVIEW_LINES); const remaining = contentLines.length - THINKING_PREVIEW_LINES; - const hint = `... (${String(remaining)} more lines, ctrl+o to expand)`; + const hint = `… (${String(remaining)} more lines, ctrl+o to expand)`; const indentWidth = Math.min(MESSAGE_INDENT.length, Math.max(0, width)); const hintWidth = Math.max(0, width - indentWidth); truncated.push( diff --git a/apps/kimi-code/src/tui/components/messages/tool-call.ts b/apps/kimi-code/src/tui/components/messages/tool-call.ts index 4ca5541bbbc..449b2c9abf7 100644 --- a/apps/kimi-code/src/tui/components/messages/tool-call.ts +++ b/apps/kimi-code/src/tui/components/messages/tool-call.ts @@ -381,7 +381,7 @@ function truncateArgValue(key: string, value: string): string { // still tell which file is being touched. return '…' + value.slice(value.length - (MAX_ARG_LENGTH - 1)); } - return value.slice(0, MAX_ARG_LENGTH - 3) + '...'; + return value.slice(0, MAX_ARG_LENGTH - 1) + '…'; } function makeWorkspaceRelativePath(filePath: string, workspaceDir: string | undefined): string { @@ -1403,7 +1403,7 @@ export class ToolCallComponent extends Container { const existingOutput = options?.replace === true ? '' : (activity?.output ?? ''); let output = existingOutput + text; if (output.length > MAX_LIVE_OUTPUT_CHARS) { - output = `[...truncated]\n${output.slice(output.length - MAX_LIVE_OUTPUT_CHARS)}`; + output = `[…truncated]\n${output.slice(output.length - MAX_LIVE_OUTPUT_CHARS)}`; } this.upsertSubToolActivity(id, name, args, activity?.phase ?? 'ongoing', output); this.rebuildContent(); @@ -1654,7 +1654,7 @@ export class ToolCallComponent extends Container { const suffix = this.hiddenSubCallCount > 1 ? 's' : ''; this.addChild( new Text( - currentTheme.italic(currentTheme.dim(` ${String(this.hiddenSubCallCount)} more tool call${suffix} ...`)), + currentTheme.italic(currentTheme.dim(` ${String(this.hiddenSubCallCount)} more tool call${suffix} …`)), 0, 0, ), @@ -2024,7 +2024,7 @@ export class ToolCallComponent extends Container { this.addChild( new Text( currentTheme.dim( - `... (${String(remaining)} more lines, ${String(allLines.length)} total, ctrl+o to expand)`, + `… (${String(remaining)} more lines, ${String(allLines.length)} total, ctrl+o to expand)`, ), 2, 0, diff --git a/apps/kimi-code/src/tui/components/messages/tool-renderers/truncated.ts b/apps/kimi-code/src/tui/components/messages/tool-renderers/truncated.ts index dc1066bece0..f9b4c7183db 100644 --- a/apps/kimi-code/src/tui/components/messages/tool-renderers/truncated.ts +++ b/apps/kimi-code/src/tui/components/messages/tool-renderers/truncated.ts @@ -87,15 +87,15 @@ export class TruncatedOutputComponent implements Component { if (this.tail) { const shown = contentLines.slice(contentLines.length - this.maxLines); return [ - this.renderHint(width, `... (${String(remaining)} earlier lines)`), + this.renderHint(width, `… (${String(remaining)} earlier lines)`), ...shown, ]; } const shown = contentLines.slice(0, this.maxLines); const hint = this.expandHint - ? `... (${String(remaining)} more lines, ctrl+o to expand)` - : `... (${String(remaining)} more lines)`; + ? `… (${String(remaining)} more lines, ctrl+o to expand)` + : `… (${String(remaining)} more lines)`; return [...shown, this.renderHint(width, hint)]; } } diff --git a/apps/kimi-code/src/tui/components/panes/btw-panel.ts b/apps/kimi-code/src/tui/components/panes/btw-panel.ts index 3dbb88508c0..1982b5d1bd8 100644 --- a/apps/kimi-code/src/tui/components/panes/btw-panel.ts +++ b/apps/kimi-code/src/tui/components/panes/btw-panel.ts @@ -145,7 +145,7 @@ export class BtwPanelComponent implements Component { lines.push(...this.renderTurn(turn, width)); } if (this.turns.length === 0) { - lines.push(chalk.hex(currentTheme.palette.textDim)('Ready for a side question...')); + lines.push(chalk.hex(currentTheme.palette.textDim)('Ready for a side question…')); } lines.push(...this.renderTransientNotices(width)); return this.fitBodyLines(lines); @@ -213,7 +213,7 @@ export class BtwPanelComponent implements Component { : thinkingLines; lines.push(...visibleThinking); } else if (turn.error === undefined) { - lines.push(chalk.hex(currentTheme.palette.textDim)('Waiting for answer...')); + lines.push(chalk.hex(currentTheme.palette.textDim)('Waiting for answer…')); } if (turn.error !== undefined) { const error = chalk.hex(currentTheme.palette.error)(turn.error); diff --git a/apps/kimi-code/src/tui/kimi-tui.ts b/apps/kimi-code/src/tui/kimi-tui.ts index c82c6e7d3d3..da68c171c7a 100644 --- a/apps/kimi-code/src/tui/kimi-tui.ts +++ b/apps/kimi-code/src/tui/kimi-tui.ts @@ -3340,7 +3340,7 @@ export class KimiTUI { break; } case 'composing': { - const spinner = this.ensureActivitySpinner('braille', 'working...', (s) => + const spinner = this.ensureActivitySpinner('braille', 'working…', (s) => currentTheme.fg('primary', s), ); this.syncAgentSwarmActivitySpinner(undefined); diff --git a/apps/kimi-code/src/tui/utils/background-agent-status.ts b/apps/kimi-code/src/tui/utils/background-agent-status.ts index aa740fc6c6b..c2322e6335c 100644 --- a/apps/kimi-code/src/tui/utils/background-agent-status.ts +++ b/apps/kimi-code/src/tui/utils/background-agent-status.ts @@ -11,7 +11,7 @@ function normalizeBackgroundField(value: string | undefined): string | undefined const collapsed = value.trim().replaceAll(/\s+/g, ' '); if (collapsed.length === 0) return undefined; if (collapsed.length <= MAX_BACKGROUND_FIELD_LENGTH) return collapsed; - return `${collapsed.slice(0, MAX_BACKGROUND_FIELD_LENGTH - 3)}...`; + return `${collapsed.slice(0, MAX_BACKGROUND_FIELD_LENGTH - 1)}…`; } export function formatBackgroundAgentTranscript( diff --git a/apps/kimi-code/src/tui/utils/background-task-status.ts b/apps/kimi-code/src/tui/utils/background-task-status.ts index 10f579ad9f3..b9e1764965c 100644 --- a/apps/kimi-code/src/tui/utils/background-task-status.ts +++ b/apps/kimi-code/src/tui/utils/background-task-status.ts @@ -20,7 +20,7 @@ function truncate(value: string | undefined): string | undefined { const collapsed = value.trim().replaceAll(/\s+/g, ' '); if (collapsed.length === 0) return undefined; if (collapsed.length <= MAX_DETAIL_LENGTH) return collapsed; - return `${collapsed.slice(0, MAX_DETAIL_LENGTH - 3)}...`; + return `${collapsed.slice(0, MAX_DETAIL_LENGTH - 1)}…`; } export type BackgroundTaskTranscriptPhase = 'started' | 'updated' | 'terminal'; diff --git a/apps/kimi-code/test/tui/activity-pane.test.ts b/apps/kimi-code/test/tui/activity-pane.test.ts index 3d8b2ed3823..1501d65e44c 100644 --- a/apps/kimi-code/test/tui/activity-pane.test.ts +++ b/apps/kimi-code/test/tui/activity-pane.test.ts @@ -179,7 +179,7 @@ describe('updateActivityPane terminal progress', () => { expect(setProgress).toHaveBeenLastCalledWith(true); expect(state.activitySpinner).not.toBeNull(); expect(state.activityContainer.children).toHaveLength(0); - expect(strip(progress.render(80).join('\n'))).toContain('🌑 Working...'); + expect(strip(progress.render(80).join('\n'))).toContain('🌑 Working…'); state.activitySpinner?.instance.stop(); driver.sessionEventHandler.clearAgentSwarmProgress(); @@ -209,8 +209,8 @@ describe('updateActivityPane terminal progress', () => { expect(state.activitySpinner).not.toBeNull(); expect(state.activityContainer.children).toHaveLength(1); const output = strip(progress.render(80).join('\n')); - expect(output).toContain(' Working...'); - expect(output).not.toContain('🌑 Working...'); + expect(output).toContain(' Working…'); + expect(output).not.toContain('🌑 Working…'); state.activitySpinner?.instance.stop(); driver.sessionEventHandler.clearAgentSwarmProgress(); diff --git a/apps/kimi-code/test/tui/components/dialogs/compaction.test.ts b/apps/kimi-code/test/tui/components/dialogs/compaction.test.ts index 4f415bc322d..eb213cfa2a0 100644 --- a/apps/kimi-code/test/tui/components/dialogs/compaction.test.ts +++ b/apps/kimi-code/test/tui/components/dialogs/compaction.test.ts @@ -20,7 +20,7 @@ describe('CompactionComponent', () => { const lines = component.render(120).map(strip); const text = lines.join('\n'); - expect(text).toContain('Compacting context...'); + expect(text).toContain('Compacting context…'); expect(text).toContain(' keep the recent files only'); } finally { component.dispose(); @@ -34,7 +34,7 @@ describe('CompactionComponent', () => { const lines = component.render(120).map(strip); const text = lines.join('\n'); - expect(text).toContain('Compacting context... · Tip: ctrl+s: steer mid-turn'); + expect(text).toContain('Compacting context… · Tip: ctrl+s: steer mid-turn'); } finally { component.dispose(); } @@ -65,7 +65,7 @@ describe('CompactionComponent', () => { const text = lines.join('\n'); expect(text).toContain('Compaction cancelled'); - expect(text).not.toContain('Compacting context...'); + expect(text).not.toContain('Compacting context…'); } finally { component.dispose(); } @@ -157,7 +157,7 @@ describe('CompactionComponent', () => { try { const headerOf = (): string => { - const line = component.render(120).find((l) => strip(l).includes('Compacting context...')); + const line = component.render(120).find((l) => strip(l).includes('Compacting context…')); if (line === undefined) throw new Error('header line not found'); return line; }; diff --git a/apps/kimi-code/test/tui/components/messages/agent-swarm-progress.test.ts b/apps/kimi-code/test/tui/components/messages/agent-swarm-progress.test.ts index bb460ce0e6e..63d0219ab0a 100644 --- a/apps/kimi-code/test/tui/components/messages/agent-swarm-progress.test.ts +++ b/apps/kimi-code/test/tui/components/messages/agent-swarm-progress.test.ts @@ -163,7 +163,7 @@ describe('AgentSwarmProgressComponent', () => { expect(output).toContain('Agent Swarm'); expect(output).toContain('Review changed files'); - expect(output).toContain('Orchestrating...'); + expect(output).toContain('Orchestrating…'); expect(output).not.toContain('01'); }); @@ -248,8 +248,8 @@ describe('AgentSwarmProgressComponent', () => { const output = renderText(component); - expect(output).toContain('001 Queued...'); - expect(output).toContain('002 Queued...'); + expect(output).toContain('001 Queued…'); + expect(output).toContain('002 Queued…'); expect(output).not.toContain('001 ['); expect(output).not.toContain('002 ['); expect(output).not.toContain('agents=2'); @@ -261,11 +261,11 @@ describe('AgentSwarmProgressComponent', () => { registerSubagents(component, 3); const lines = renderLines(component, 97); - const queuedLine = lines.find((line) => line.includes('001 Queued...')); + const queuedLine = lines.find((line) => line.includes('001 Queued…')); expect(queuedLine).toBeDefined(); - expect(queuedLine).toContain('002 Queued...'); - expect(queuedLine).toContain('003 Queued...'); + expect(queuedLine).toContain('002 Queued…'); + expect(queuedLine).toContain('003 Queued…'); }); it('omits subagent text when the compact grid is needed to fit available height', () => { @@ -363,7 +363,7 @@ describe('AgentSwarmProgressComponent', () => { let output = renderText(component); expect(output).toContain('001 ['); expect(output).toContain('Running'); - expect(output).toContain('002 Queued...'); + expect(output).toContain('002 Queued…'); expect(output).not.toContain('002 ['); component.markCompleted('agent-1'); @@ -412,8 +412,8 @@ describe('AgentSwarmProgressComponent', () => { }); let output = renderText(component); - expect(output).toContain('Rate limited...'); - expect(output).not.toContain('Queued...'); + expect(output).toContain('Rate limited…'); + expect(output).not.toContain('Queued…'); expect(output).not.toContain('Provider rate limit'); expect(output).not.toContain('Failed'); @@ -421,7 +421,7 @@ describe('AgentSwarmProgressComponent', () => { output = renderText(component); expect(output).toContain('Running'); - expect(output).not.toContain('Rate limited...'); + expect(output).not.toContain('Rate limited…'); }); it('renders rate-limited subagents as cancelled when cancelled', () => { @@ -440,7 +440,7 @@ describe('AgentSwarmProgressComponent', () => { expect(cellLine).toBeDefined(); expect(cellLine).toContain('⊘ Cancelled.'); - expect(cellLine).not.toContain('Rate limited...'); + expect(cellLine).not.toContain('Rate limited…'); }); it('renders failure details from AgentSwarm result output', () => { @@ -605,7 +605,7 @@ describe('AgentSwarmProgressComponent', () => { }); const promptLine = renderLines(prompting, 80) - .find((line) => line.includes('Prompting...')); + .find((line) => line.includes('Prompting…')); expect(promptLine).toBeDefined(); const working = createComponent(); @@ -613,15 +613,15 @@ describe('AgentSwarmProgressComponent', () => { startSubagents(working, 1); const workingLine = renderLines(working, 80) - .find((line) => line.includes('Working...')); + .find((line) => line.includes('Working…')); expect(workingLine).toBeDefined(); const promptTextIndex = promptLine?.indexOf('Review the changed') ?? -1; const progressBarIndex = workingLine?.indexOf('━') ?? -1; expect(promptTextIndex).toBeGreaterThan(0); expect(progressBarIndex).toBeGreaterThan(0); - expect(promptTextIndex).toBe(visibleWidth(' Prompting... ')); - expect(progressBarIndex).toBe(visibleWidth(' Working... ')); + expect(promptTextIndex).toBe(visibleWidth(' Prompting… ')); + expect(progressBarIndex).toBe(visibleWidth(' Working… ')); }); it('renders the activity spinner before the total status line', () => { @@ -632,10 +632,10 @@ describe('AgentSwarmProgressComponent', () => { component.setActivitySpinnerText(() => '🌗'); const statusLine = renderLines(component, 80) - .find((line) => line.includes('Working...')); + .find((line) => line.includes('Working…')); expect(statusLine).toBeDefined(); - expect(statusLine?.startsWith(' 🌗 Working...')).toBe(true); + expect(statusLine?.startsWith(' 🌗 Working…')).toBe(true); }); it('keeps a two-cell placeholder after the AgentSwarm tool call ends', () => { @@ -648,10 +648,10 @@ describe('AgentSwarmProgressComponent', () => { component.setActivitySpinnerText(() => '🌘'); const statusLine = renderLines(component, 80) - .find((line) => line.includes('Working...')); + .find((line) => line.includes('Working…')); expect(statusLine).toBeDefined(); - expect(statusLine?.startsWith(' Working...')).toBe(true); + expect(statusLine?.startsWith(' Working…')).toBe(true); expect(statusLine).not.toContain('🌗'); expect(statusLine).not.toContain('🌘'); }); @@ -694,7 +694,7 @@ describe('AgentSwarmProgressComponent', () => { }); const promptLine = renderLines(prompting, 50) - .find((line) => line.includes('Prompting...')); + .find((line) => line.includes('Prompting…')); expect(promptLine).toBeDefined(); expect(visibleWidth(promptLine ?? '')).toBeLessThan(50); @@ -818,7 +818,7 @@ describe('AgentSwarmProgressComponent', () => { registerSubagents(component, 1); let output = renderText(component); - expect(output).toContain('001 Queued...'); + expect(output).toContain('001 Queued…'); expect(output).not.toContain('001 ['); expect(output).not.toContain('002'); @@ -827,15 +827,15 @@ describe('AgentSwarmProgressComponent', () => { description: `${DEFAULT_DESCRIPTION} #2 (coder)`, }); output = renderText(component); - expect(output).toContain('001 Queued...'); - expect(output).toContain('002 Queued...'); + expect(output).toContain('001 Queued…'); + expect(output).toContain('002 Queued…'); expect(output).not.toContain('001 ['); expect(output).not.toContain('002 ['); component.markInputComplete(); output = renderText(component); - expect(output).toContain('001 Queued...'); - expect(output).toContain('002 Queued...'); + expect(output).toContain('001 Queued…'); + expect(output).toContain('002 Queued…'); expect(output).not.toContain('001 ['); }); diff --git a/apps/kimi-code/test/tui/components/messages/shell-execution.test.ts b/apps/kimi-code/test/tui/components/messages/shell-execution.test.ts index bb501c05b0b..132737c8655 100644 --- a/apps/kimi-code/test/tui/components/messages/shell-execution.test.ts +++ b/apps/kimi-code/test/tui/components/messages/shell-execution.test.ts @@ -35,7 +35,7 @@ describe('ShellExecutionComponent', () => { expect(collapsedOutput).toContain('line1'); expect(collapsedOutput).toContain('line3'); expect(collapsedOutput).not.toContain('line4'); - expect(collapsedOutput).toContain('... (2 more lines, ctrl+o to expand)'); + expect(collapsedOutput).toContain('… (2 more lines, ctrl+o to expand)'); const expanded = new ShellExecutionComponent({ result: { @@ -76,7 +76,7 @@ describe('ShellExecutionComponent', () => { const output = component.render(100).map(strip).join('\n'); expect(output).toContain('hello'); - expect(output).not.toContain('... (2 more lines'); + expect(output).not.toContain('… (2 more lines'); }); it('preserves internal empty lines while trimming only trailing ones', () => { @@ -91,7 +91,7 @@ describe('ShellExecutionComponent', () => { const output = component.render(100).map(strip).join('\n'); expect(output).toContain('a'); expect(output).toContain('b'); - expect(output).not.toContain('... (2 more lines'); + expect(output).not.toContain('… (2 more lines'); }); it('truncates long single-line output by wrapped visual lines', () => { @@ -106,7 +106,7 @@ describe('ShellExecutionComponent', () => { const out = strip(component.render(20).join('\n')); expect(out).toContain('x'); expect(out).not.toContain('x'.repeat(500)); - expect(out).toContain('... ('); + expect(out).toContain('… ('); }); describe('shellExecutionResultRenderer', () => { diff --git a/apps/kimi-code/test/tui/components/messages/shell-run.test.ts b/apps/kimi-code/test/tui/components/messages/shell-run.test.ts index be89c6f034e..96a4bc110dc 100644 --- a/apps/kimi-code/test/tui/components/messages/shell-run.test.ts +++ b/apps/kimi-code/test/tui/components/messages/shell-run.test.ts @@ -90,7 +90,7 @@ describe('ShellRunComponent finished collapse', () => { const c = create(); c.finish(rows(30), '', false); const rendered = stripTheme(c.render(80).join('\n')); - expect(rendered).toContain('... (20 more lines, ctrl+o to expand)'); + expect(rendered).toContain('… (20 more lines, ctrl+o to expand)'); expect(rendered).toContain('row-01'); expect(rendered).toContain('row-10'); expect(rendered).not.toContain('row-11'); @@ -116,7 +116,7 @@ describe('ShellRunComponent finished collapse', () => { c.setExpanded(false); const collapsed = stripTheme(c.render(80).join('\n')); - expect(collapsed).toContain('... (20 more lines, ctrl+o to expand)'); + expect(collapsed).toContain('… (20 more lines, ctrl+o to expand)'); expect(collapsed).not.toContain('row-11'); }); @@ -153,7 +153,7 @@ describe('ShellRunComponent finished collapse', () => { c.append('x'.repeat(300 * 1024)); c.setExpanded(true); const rendered = stripTheme(c.render(80).join('\n')); - expect(rendered).toContain('... (output truncated)'); + expect(rendered).toContain('… (output truncated)'); }); it('keeps the backgrounded view when toggled', () => { @@ -168,7 +168,7 @@ describe('ShellRunComponent finished collapse', () => { const c = create(); c.finish(rows(30), 'boom', true); const collapsed = stripTheme(c.render(80).join('\n')); - expect(collapsed).toContain('... (21 more lines, ctrl+o to expand)'); + expect(collapsed).toContain('… (21 more lines, ctrl+o to expand)'); c.setExpanded(true); const expanded = stripTheme(c.render(80).join('\n')); diff --git a/apps/kimi-code/test/tui/components/messages/thinking.test.ts b/apps/kimi-code/test/tui/components/messages/thinking.test.ts index e615d7f5cd1..7e04dbd1f7e 100644 --- a/apps/kimi-code/test/tui/components/messages/thinking.test.ts +++ b/apps/kimi-code/test/tui/components/messages/thinking.test.ts @@ -15,8 +15,8 @@ describe('ThinkingComponent', () => { const component = new ThinkingComponent('working it out', true, 'live'); const out = strip(component.render(80).join('\n')); - expect(out).toContain('⠋ thinking...'); - expect(out).not.toContain(' ⠋ thinking...'); + expect(out).toContain('⠋ thinking…'); + expect(out).not.toContain(' ⠋ thinking…'); expect(out).not.toContain(`${STATUS_BULLET}⠋`); expect(out).toContain(' working it out'); }); @@ -40,11 +40,11 @@ describe('ThinkingComponent', () => { requestRender, } as unknown as TUI); - expect(strip(component.render(80).join('\n'))).toContain('⠋ thinking...'); + expect(strip(component.render(80).join('\n'))).toContain('⠋ thinking…'); vi.advanceTimersByTime(80); expect(requestRender).toHaveBeenCalled(); - expect(strip(component.render(80).join('\n'))).toContain('⠙ thinking...'); + expect(strip(component.render(80).join('\n'))).toContain('⠙ thinking…'); component.finalize(); requestRender.mockClear(); @@ -63,7 +63,7 @@ describe('ThinkingComponent', () => { expect(out).toContain('line2'); expect(out).not.toContain('line3'); expect(out).not.toContain('line4'); - expect(out).toContain('... (5 more lines, ctrl+o to expand)'); + expect(out).toContain('… (5 more lines, ctrl+o to expand)'); }); it('expands and collapses after finalization', () => { diff --git a/apps/kimi-code/test/tui/components/messages/tool-call.test.ts b/apps/kimi-code/test/tui/components/messages/tool-call.test.ts index 367e62a56c9..b86141e496f 100644 --- a/apps/kimi-code/test/tui/components/messages/tool-call.test.ts +++ b/apps/kimi-code/test/tui/components/messages/tool-call.test.ts @@ -162,7 +162,7 @@ describe('ToolCallComponent', () => { expect(collapsed).toContain('line2'); expect(collapsed).toContain('line3'); expect(collapsed).not.toContain('line4'); - expect(collapsed).toContain('... (2 more lines, ctrl+o to expand)'); + expect(collapsed).toContain('… (2 more lines, ctrl+o to expand)'); component.setExpanded(true); diff --git a/apps/kimi-code/test/tui/components/messages/tool-renderers/registry.test.ts b/apps/kimi-code/test/tui/components/messages/tool-renderers/registry.test.ts index cff95e628e4..1c4d3b27329 100644 --- a/apps/kimi-code/test/tui/components/messages/tool-renderers/registry.test.ts +++ b/apps/kimi-code/test/tui/components/messages/tool-renderers/registry.test.ts @@ -65,14 +65,14 @@ describe('tool-result registry', () => { expect(out).toContain('b'); expect(out).toContain('c'); expect(out).not.toContain('\nd'); - expect(out).toContain('... (2 more lines, ctrl+o to expand)'); + expect(out).toContain('… (2 more lines, ctrl+o to expand)'); }); it('uses truncated renderer for Bash to preserve raw output UX', () => { const renderer = pickResultRenderer('Bash'); const out = strip(joinRender(renderer(call('Bash'), result('one\ntwo\nthree\nfour'), ctx))); expect(out).toContain('one'); - expect(out).toContain('... (1 more lines, ctrl+o to expand)'); + expect(out).toContain('… (1 more lines, ctrl+o to expand)'); }); it('Read renders no body when collapsed (header chip carries the count)', () => { @@ -248,7 +248,7 @@ describe('tool-result registry', () => { const out = strip(joinRender(renderer(call('SomethingUnknown'), result(longLine), ctx), 20)); expect(out).toContain('x'); expect(out).not.toContain(longLine); - expect(out).toContain('... ('); + expect(out).toContain('… ('); }); const waitForCompletedOutput = [ diff --git a/apps/kimi-code/test/tui/components/messages/tool-renderers/truncated.test.ts b/apps/kimi-code/test/tui/components/messages/tool-renderers/truncated.test.ts index 4c90c039258..3f6471f46c5 100644 --- a/apps/kimi-code/test/tui/components/messages/tool-renderers/truncated.test.ts +++ b/apps/kimi-code/test/tui/components/messages/tool-renderers/truncated.test.ts @@ -20,7 +20,7 @@ describe('TruncatedOutputComponent', () => { const lines = strip(component.render(80).join('\n')).split('\n'); expect(lines[0]?.startsWith(' a')).toBe(true); expect(lines[1]?.startsWith(' b')).toBe(true); - expect(lines[2]).toBe(' ... (3 more lines, ctrl+o to expand)'); + expect(lines[2]).toBe(' … (3 more lines, ctrl+o to expand)'); }); it('defaults to a two-space indent for both content and hint', () => { @@ -32,7 +32,7 @@ describe('TruncatedOutputComponent', () => { const lines = strip(component.render(80).join('\n')).split('\n'); expect(lines[0]?.startsWith(' x')).toBe(true); - expect(lines[1]).toBe(' ... (2 more lines, ctrl+o to expand)'); + expect(lines[1]).toBe(' … (2 more lines, ctrl+o to expand)'); }); it('omits the ctrl+o promise when expandHint is false', () => { @@ -45,7 +45,7 @@ describe('TruncatedOutputComponent', () => { }); const lines = strip(component.render(80).join('\n')).split('\n'); - expect(lines[2]).toBe(' ... (2 more lines)'); + expect(lines[2]).toBe(' … (2 more lines)'); }); it('renders all lines without a hint when expanded', () => { diff --git a/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts b/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts index 9774c535fbf..1368610d484 100644 --- a/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts +++ b/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts @@ -4308,7 +4308,7 @@ command = "vim" driver.handleUserInput('seq 30'); await vi.waitFor(() => { const transcript = stripSgr(driver.state.transcriptContainer.render(120).join('\n')); - expect(transcript).toContain('... (20 more lines, ctrl+o to expand)'); + expect(transcript).toContain('… (20 more lines, ctrl+o to expand)'); }); let transcript = stripSgr(driver.state.transcriptContainer.render(120).join('\n')); @@ -4322,7 +4322,7 @@ command = "vim" driver.state.editor.onToggleToolExpand?.(); transcript = stripSgr(driver.state.transcriptContainer.render(120).join('\n')); - expect(transcript).toContain('... (20 more lines, ctrl+o to expand)'); + expect(transcript).toContain('… (20 more lines, ctrl+o to expand)'); expect(transcript).not.toContain('row-11'); }); @@ -4844,7 +4844,7 @@ command = "vim" expect(session.startBtw).toHaveBeenCalledWith(); }); expect(session.prompt).not.toHaveBeenCalled(); - expect(stripSgr(renderBtwPanel(driver))).toContain('Ready for a side question...'); + expect(stripSgr(renderBtwPanel(driver))).toContain('Ready for a side question…'); driver.handleUserInput('What are you working on right now?'); @@ -4885,7 +4885,7 @@ command = "vim" await vi.waitFor(() => { expect(session.startBtw).toHaveBeenCalledWith(); }); - expect(stripSgr(renderBtwPanel(driver))).toContain('Ready for a side question...'); + expect(stripSgr(renderBtwPanel(driver))).toContain('Ready for a side question…'); driver.handleUserInput('check /skill:review'); @@ -5179,7 +5179,7 @@ command = "vim" const lines = getMountedBtwPanel(driver).render(80).map(stripSgr); expect(lines).toHaveLength(3); expect(lines.join('\n')).toContain('Q: side question'); - expect(lines.join('\n')).toContain('Waiting for answer...'); + expect(lines.join('\n')).toContain('Waiting for answer…'); }); it('keeps /btw panel height stable when final output is shorter than thinking', async () => { @@ -6152,7 +6152,7 @@ command = "vim" transcript = stripSgr(renderTranscript(driver)); expect(transcript).toContain('001 ['); - expect(transcript).toContain('Queued...'); + expect(transcript).toContain('Queued…'); expect(transcript).not.toContain('Provider rate limit'); expect(transcript).not.toContain('Failed'); @@ -6191,7 +6191,7 @@ command = "vim" expect(transcript).toContain('001 ['); expect(transcript).toContain('Reviewing src/a.ts'); expect(transcript).not.toContain('Completed'); - expect(transcript).toContain('002 Queued...'); + expect(transcript).toContain('002 Queued…'); expect(transcript).not.toContain('002 ['); driver.sessionEventHandler.handleEvent( @@ -6513,7 +6513,7 @@ command = "vim" const renderSwarm = (): string => stripSgr(swarmProgress.render(transcriptWidth).join('\n')); - expect(renderSwarm()).toContain('001 Queued...'); + expect(renderSwarm()).toContain('001 Queued…'); driver.sessionEventHandler.handleEvent( { @@ -6539,7 +6539,7 @@ command = "vim" .reduce((sum, child) => sum + child.render(transcriptWidth).length, 0); expect(rowsAfterSwarmInTranscript).toBeGreaterThan(0); - expect(renderSwarm()).toContain('001 Queued...'); + expect(renderSwarm()).toContain('001 Queued…'); const transcript = stripSgr( driver.state.transcriptContainer.render(terminalColumns).join('\n'), ); @@ -6612,7 +6612,7 @@ command = "vim" let transcript = stripSgr(renderTranscript(driver)); expect(transcript).toContain('Agent Swarm'); - expect(transcript).toContain('Orchestrating...'); + expect(transcript).toContain('Orchestrating…'); expect(transcript).not.toContain('01'); driver.sessionEventHandler.handleEvent( @@ -6649,7 +6649,7 @@ command = "vim" ); transcript = stripSgr(renderTranscript(driver)); - expect(transcript).toContain('001 Queued...'); + expect(transcript).toContain('001 Queued…'); expect(transcript).not.toContain('001 ['); expect(transcript).toContain('002 src/b'); @@ -6671,8 +6671,8 @@ command = "vim" ); transcript = stripSgr(renderTranscript(driver)); - expect(transcript).toContain('001 Queued...'); - expect(transcript).toContain('002 Queued...'); + expect(transcript).toContain('001 Queued…'); + expect(transcript).toContain('002 Queued…'); expect(transcript).not.toContain('001 ['); expect(transcript).not.toContain('002 ['); }); diff --git a/apps/kimi-code/test/tui/message-replay.test.ts b/apps/kimi-code/test/tui/message-replay.test.ts index 80b4e2be6e4..57ca82ea8d8 100644 --- a/apps/kimi-code/test/tui/message-replay.test.ts +++ b/apps/kimi-code/test/tui/message-replay.test.ts @@ -402,7 +402,7 @@ describe('KimiTUI resume message replay', () => { ]); const transcript = stripAnsi(driver.state.transcriptContainer.render(140).join('\n')); - expect(transcript).toContain('... (20 more lines, ctrl+o to expand)'); + expect(transcript).toContain('… (20 more lines, ctrl+o to expand)'); expect(transcript).toContain('row-01'); expect(transcript).not.toContain('row-11'); }); diff --git a/apps/vscode/webview-ui/src/components/ActionMenu.tsx b/apps/vscode/webview-ui/src/components/ActionMenu.tsx index f6ee7a2c9e5..0e4b79b4def 100644 --- a/apps/vscode/webview-ui/src/components/ActionMenu.tsx +++ b/apps/vscode/webview-ui/src/components/ActionMenu.tsx @@ -140,7 +140,7 @@ export function ActionMenu({ className, onAuthAction }: ActionMenuProps) { danger={isLoggedIn} > {loading ? : isLoggedIn ? : } - {loading ? "Processing..." : isLoggedIn ? "Sign out" : "Sign in"} + {loading ? "Processing…" : isLoggedIn ? "Sign out" : "Sign in"} diff --git a/apps/vscode/webview-ui/src/components/ChatMessage.tsx b/apps/vscode/webview-ui/src/components/ChatMessage.tsx index 08d27c159c4..f189be6dc56 100644 --- a/apps/vscode/webview-ui/src/components/ChatMessage.tsx +++ b/apps/vscode/webview-ui/src/components/ChatMessage.tsx @@ -30,7 +30,7 @@ function ThinkingIndicator() { return (
- Processing... + Processing…
); } diff --git a/apps/vscode/webview-ui/src/components/CompactionCard.tsx b/apps/vscode/webview-ui/src/components/CompactionCard.tsx index 43e6d85e6b9..7355baf6efe 100644 --- a/apps/vscode/webview-ui/src/components/CompactionCard.tsx +++ b/apps/vscode/webview-ui/src/components/CompactionCard.tsx @@ -15,7 +15,7 @@ export function CompactionCard() { )}
-
{isCompacting ? "Compacting context..." : "Context compacted"}
+
{isCompacting ? "Compacting context…" : "Context compacted"}
diff --git a/apps/vscode/webview-ui/src/components/ConfigErrorScreen.tsx b/apps/vscode/webview-ui/src/components/ConfigErrorScreen.tsx index a70bf6a9b5f..877c93f769c 100644 --- a/apps/vscode/webview-ui/src/components/ConfigErrorScreen.tsx +++ b/apps/vscode/webview-ui/src/components/ConfigErrorScreen.tsx @@ -106,7 +106,7 @@ export function ConfigErrorScreen({ type, errorMessage, onRefresh, onBackToLogin
- Starting Kimi Code... + Starting Kimi Code…
diff --git a/apps/vscode/webview-ui/src/components/FilePickerMenu.tsx b/apps/vscode/webview-ui/src/components/FilePickerMenu.tsx index 7f6d8159f13..649f8884fa5 100644 --- a/apps/vscode/webview-ui/src/components/FilePickerMenu.tsx +++ b/apps/vscode/webview-ui/src/components/FilePickerMenu.tsx @@ -29,7 +29,7 @@ interface FilePickerMenuProps { function truncateMiddle(str: string, maxLen: number): string { if (str.length <= maxLen) return str; - const ellipsis = "..."; + const ellipsis = "…"; const charsToShow = maxLen - ellipsis.length; const frontChars = Math.ceil(charsToShow / 2); const backChars = Math.floor(charsToShow / 2); @@ -85,7 +85,7 @@ export function FilePickerMenu({ className={cn("w-full px-2 py-1.5 text-left flex items-center gap-2 border-b border-border", selectedIndex === 0 ? "bg-accent" : "hover:bg-accent/50")} > - Select images or videos... + Select images or videos… )} ) : ( @@ -131,7 +131,7 @@ export function FilePickerMenu({ )}
{isLoading ? ( -
Loading...
+
Loading…
) : items.length === 0 ? (
{mode === "search" ? "No files found" : "Empty folder"}
) : ( diff --git a/apps/vscode/webview-ui/src/components/LoginScreen.tsx b/apps/vscode/webview-ui/src/components/LoginScreen.tsx index 8826e362de2..60a1c5f490d 100644 --- a/apps/vscode/webview-ui/src/components/LoginScreen.tsx +++ b/apps/vscode/webview-ui/src/components/LoginScreen.tsx @@ -94,7 +94,7 @@ export function LoginScreen({ onLoginSuccess, onSkip }: LoginScreenProps) {
- Waiting for authentication... + Waiting for authentication…

A browser window should open automatically. Complete the sign-in process there.

diff --git a/apps/vscode/webview-ui/src/components/MCPServersModal.tsx b/apps/vscode/webview-ui/src/components/MCPServersModal.tsx index 991f8705545..5a6d70a1585 100644 --- a/apps/vscode/webview-ui/src/components/MCPServersModal.tsx +++ b/apps/vscode/webview-ui/src/components/MCPServersModal.tsx @@ -222,7 +222,7 @@ function ServerForm({ <>
- set("url", e.target.value)} placeholder="https://..." className="h-7 text-xs font-mono" /> + set("url", e.target.value)} placeholder="https://…" className="h-7 text-xs font-mono" />