From 2a5b4397824a55170f12d4ffe79feb0b70550084 Mon Sep 17 00:00:00 2001 From: liruifengv Date: Mon, 29 Jun 2026 13:55:08 +0800 Subject: [PATCH 1/2] fix(tui): keep working tips out of the agent swarm progress line The activity loader is shared between the activity pane and the agent swarm progress status line. Tips were written into the loader, so they leaked into the swarm progress line and got squeezed against the bar. Keep the inline spinner text used by the swarm progress line free of tips, while the loader's own row in the activity pane still shows them. --- .changeset/fix-working-tips-swarm.md | 5 +++ .../src/tui/components/chrome/moon-loader.ts | 9 +++++- .../tui/components/chrome/moon-loader.test.ts | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-working-tips-swarm.md create mode 100644 apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts diff --git a/.changeset/fix-working-tips-swarm.md b/.changeset/fix-working-tips-swarm.md new file mode 100644 index 0000000000..75e838ce09 --- /dev/null +++ b/.changeset/fix-working-tips-swarm.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix working tips getting squeezed against the agent swarm progress bar. diff --git a/apps/kimi-code/src/tui/components/chrome/moon-loader.ts b/apps/kimi-code/src/tui/components/chrome/moon-loader.ts index 9b03bff70a..93ab6e7e7d 100644 --- a/apps/kimi-code/src/tui/components/chrome/moon-loader.ts +++ b/apps/kimi-code/src/tui/components/chrome/moon-loader.ts @@ -20,6 +20,12 @@ export class MoonLoader extends Text { private colorFn?: (s: string) => string; private label: string; private displayText = ''; + // Inline text used when the spinner is embedded into another line (e.g. the + // agent-swarm progress status line). It intentionally excludes the tip: the + // tip is only rendered when the loader sits on its own row in the activity + // pane, otherwise it would get squeezed against whatever follows the inline + // spinner (like the swarm progress bar). + private inlineText = ''; private tip: string = ''; private availableWidth = 0; @@ -75,13 +81,14 @@ export class MoonLoader extends Text { } renderInline(): string { - return this.displayText; + return this.inlineText; } private updateDisplay(): void { const frame = this.frames[this.currentFrame]!; const coloredFrame = this.colorFn ? this.colorFn(frame) : frame; const baseText = this.label ? `${coloredFrame} ${this.label}` : coloredFrame; + this.inlineText = baseText; let text = baseText; if (this.tip) { const withTip = baseText + currentTheme.fg('textDim', this.tip); diff --git a/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts b/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts new file mode 100644 index 0000000000..2699983c59 --- /dev/null +++ b/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts @@ -0,0 +1,31 @@ +import type { TUI } from '@earendil-works/pi-tui'; +import { describe, expect, it } from 'vitest'; + +import { MoonLoader } from '#/tui/components/chrome/moon-loader'; + +function createLoader(): MoonLoader { + const ui = { requestRender() {} } as unknown as TUI; + return new MoonLoader(ui, 'moon'); +} + +describe('MoonLoader', () => { + it('keeps the tip out of renderInline so it does not squeeze against the swarm progress bar', () => { + const loader = createLoader(); + loader.setTip(' · Tip: ctrl+s: steer mid-turn'); + loader.setAvailableWidth(80); + + const inline = loader.renderInline(); + expect(inline).not.toContain('Tip'); + expect(inline).not.toContain('steer'); + expect(inline.trim().length).toBeGreaterThan(0); + }); + + it('still shows the tip on its own row when width allows', () => { + const loader = createLoader(); + loader.setTip(' · Tip: ctrl+s: steer mid-turn'); + loader.setAvailableWidth(80); + + const row = loader.render(80).join('\n'); + expect(row).toContain('Tip: ctrl+s: steer mid-turn'); + }); +}); From 3f0e9d3ad9e65f342f6040ecf0d103ca1ee03c0e Mon Sep 17 00:00:00 2001 From: liruifengv Date: Mon, 29 Jun 2026 14:03:51 +0800 Subject: [PATCH 2/2] test(tui): stop moon loader timers in tests MoonLoader starts a real setInterval in its constructor. Stop every loader created in the tests via afterEach so no live timer leaks past the file. --- .../tui/components/chrome/moon-loader.test.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts b/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts index 2699983c59..e8bea6504b 100644 --- a/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts +++ b/apps/kimi-code/test/tui/components/chrome/moon-loader.test.ts @@ -1,13 +1,24 @@ import type { TUI } from '@earendil-works/pi-tui'; -import { describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it } from 'vitest'; import { MoonLoader } from '#/tui/components/chrome/moon-loader'; +// MoonLoader starts a real setInterval in its constructor, so every loader +// created in these tests must be stopped to avoid leaving live timers behind. +const loaders: MoonLoader[] = []; + function createLoader(): MoonLoader { const ui = { requestRender() {} } as unknown as TUI; - return new MoonLoader(ui, 'moon'); + const loader = new MoonLoader(ui, 'moon'); + loaders.push(loader); + return loader; } +afterEach(() => { + for (const loader of loaders) loader.stop(); + loaders.length = 0; +}); + describe('MoonLoader', () => { it('keeps the tip out of renderInline so it does not squeeze against the swarm progress bar', () => { const loader = createLoader();