diff --git a/tui/src/app.test.ts b/tui/src/app.test.ts index 5671dc680..d602b8ba8 100644 --- a/tui/src/app.test.ts +++ b/tui/src/app.test.ts @@ -766,7 +766,13 @@ describe("NanobotTui layout", () => { const internals = app as unknown as { palette: { referenceBackground: string; text: string; border: string } shell: { backgroundColor: { intent: string } } - composer: { backgroundColor: { intent: string }; textColor: { toInts(): number[] } } + composerFrame: { + backgroundColor: { intent: string; toInts(): number[] } + } + composer: { + backgroundColor: { intent: string; toInts(): number[] } + textColor: { toInts(): number[] } + } transcript: { markdown: Set<{ syntaxStyle: object }> frames: Set<{ borderColor: { toInts(): number[] } }> @@ -791,13 +797,44 @@ describe("NanobotTui layout", () => { border: "#D4D4D8", }) expect(internals.shell.backgroundColor.intent).toBe("default") - expect(internals.composer.backgroundColor.intent).toBe("default") + expect(internals.composerFrame.backgroundColor.toInts().slice(0, 3)).toEqual([240, 240, 240]) + expect(internals.composer.backgroundColor.toInts().slice(0, 3)).toEqual([240, 240, 240]) expect(internals.composer.textColor.toInts().slice(0, 3)).toEqual([24, 24, 27]) expect(sessionFrame?.borderColor.toInts().slice(0, 3)).toEqual([212, 212, 216]) expect(userRow?.backgroundColor.toInts().slice(0, 3)).toEqual([240, 240, 240]) expect(markdown?.syntaxStyle).not.toBe(darkSyntax) }) + test("uses a quiet surface instead of a boxed composer", async () => { + setup = await createRenderer({ width: 72, height: 20, screenMode: "alternate-screen" }) + const app = NanobotTui.mount( + setup.renderer, + { ...options, theme: "light" }, + client(), + new MockTreeSitterClient({ autoResolveTimeout: 0 }), + ) + const internals = app as unknown as { + ready: boolean + composerFrame: { + border: boolean | string[] + backgroundColor: { toInts(): number[] } + } + composer: { backgroundColor: { toInts(): number[] } } + } + + expect(internals.composerFrame.border).toBeFalse() + expect(internals.composerFrame.backgroundColor.toInts().slice(0, 3)).toEqual([240, 240, 240]) + expect(internals.composer.backgroundColor.toInts().slice(0, 3)).toEqual([240, 240, 240]) + + app.accept({ event: "attached", chat_id: "chat" }) + await waitUntil(() => internals.ready) + await setup.renderOnce() + + const composerLine = setup.captureCharFrame().split("\n") + .find((line) => line.includes("Ask nanobot anything")) || "" + expect(composerLine).not.toContain("│") + }) + test("uses asymmetric roles instead of chat bubbles", async () => { setup = await createRenderer({ width: 72, height: 24, screenMode: "alternate-screen" }) const app = NanobotTui.mount( diff --git a/tui/src/app.ts b/tui/src/app.ts index b1096a903..e137009b3 100644 --- a/tui/src/app.ts +++ b/tui/src/app.ts @@ -411,17 +411,16 @@ export class NanobotTui { }) this.title.add(this.titleText) this.title.add(this.modelText) + const composerSurface = this.composerSurface() this.composerFrame = new BoxRenderable(renderer, { id: "nanobot-tui-composer-frame", width: "100%", - minHeight: 3, + minHeight: 1, flexShrink: 0, - border: true, - borderStyle: "rounded", - borderColor: this.palette.border, + border: false, paddingLeft: 1, paddingRight: 1, - backgroundColor: RGBA.defaultBackground(), + backgroundColor: composerSurface, }) this.composer = new TextareaRenderable(renderer, { id: "nanobot-tui-composer", @@ -433,8 +432,8 @@ export class NanobotTui { placeholderColor: this.palette.faint, textColor: this.palette.text, focusedTextColor: this.palette.text, - backgroundColor: RGBA.defaultBackground(), - focusedBackgroundColor: RGBA.defaultBackground(), + backgroundColor: composerSurface, + focusedBackgroundColor: composerSurface, cursorColor: this.palette.accent, showCursor: true, keyBindings: [ @@ -1040,7 +1039,7 @@ export class NanobotTui { this.sessionMenu.setTheme(commandMenuTheme(this.palette)) this.contextPanel.setTheme(contextPanelTheme(this.palette)) this.diffViewer.setTheme(diffViewerTheme(this.palette, this.backgroundKnown)) - this.composerFrame.borderColor = this.palette.border + this.updateComposerAppearance() this.composer.textColor = this.palette.text this.composer.focusedTextColor = this.palette.text this.composer.cursorColor = this.palette.accent @@ -1113,7 +1112,20 @@ export class NanobotTui { private resizeComposer(): void { const maxHeight = Math.max(1, Math.min(12, Math.floor(this.renderer.height / 3))) this.composer.maxHeight = maxHeight - this.composerFrame.maxHeight = maxHeight + 2 + this.composerFrame.maxHeight = maxHeight + } + + private composerSurface(): RGBA { + return this.backgroundKnown + ? RGBA.fromHex(this.palette.userBackground) + : RGBA.defaultBackground() + } + + private updateComposerAppearance(): void { + const surface = this.composerSurface() + this.composerFrame.backgroundColor = surface + this.composer.backgroundColor = surface + this.composer.focusedBackgroundColor = surface } private syncComposerPlaceholder(): void {