style(tui): keep short transcripts and composer top-aligned

This commit is contained in:
Kail Tian
2026-08-25 16:21:44 +08:00
committed by chengyongru
parent 4b2965c8f3
commit e427c9eeae
2 changed files with 42 additions and 1 deletions
+40
View File
@@ -14,6 +14,7 @@ import type {
WorkspaceScopePayload, WorkspaceScopePayload,
} from "./protocol" } from "./protocol"
import type { HostAgentState, HostMetadata, TuiHost } from "./host" import type { HostAgentState, HostMetadata, TuiHost } from "./host"
import type { Transcript } from "./transcript"
const options: AppOptions = { const options: AppOptions = {
wsUrl: "ws://localhost.invalid/ws", wsUrl: "ws://localhost.invalid/ws",
@@ -123,6 +124,45 @@ describe("NanobotTui layout", () => {
const createRenderer = (options: Parameters<typeof createTestRenderer>[0]) => createTestRenderer(options) const createRenderer = (options: Parameters<typeof createTestRenderer>[0]) => createTestRenderer(options)
test("keeps short transcripts and the composer anchored at the top", async () => {
setup = await createRenderer({
width: 100,
height: 18,
screenMode: "alternate-screen",
consoleMode: "disabled",
})
const app = mount(setup)
const ui = app as unknown as {
transcript: Transcript
title: BoxRenderable
status: TextRenderable
}
const positions = () => ({
transcriptHeight: ui.transcript.root.height,
titleY: ui.title.y,
statusY: ui.status.y,
})
let anchoredPositions: ReturnType<typeof positions> | undefined
for (const height of [18, 30, 36]) {
setup.resize(100, height)
await setup.renderOnce()
expect(ui.title.y).toBe(ui.transcript.root.y + ui.transcript.root.height)
expect(ui.status.y + ui.status.height).toBeLessThan(setup.renderer.height)
if (!anchoredPositions) anchoredPositions = positions()
else expect(positions()).toEqual(anchoredPositions)
}
ui.transcript.user("A short prompt")
await setup.renderOnce()
const promptPositions = positions()
expect(promptPositions.titleY).toBeGreaterThan(anchoredPositions?.titleY || 0)
setup.resize(100, 40)
await setup.renderOnce()
expect(positions()).toEqual(promptPositions)
})
test("reflows a single retained layout across terminal resizes", async () => { test("reflows a single retained layout across terminal resizes", async () => {
setup = await createRenderer({ setup = await createRenderer({
width: 100, width: 100,
+2 -1
View File
@@ -91,7 +91,7 @@ export class Transcript {
id: "nanobot-tui-transcript", id: "nanobot-tui-transcript",
width: "100%", width: "100%",
minHeight: 0, minHeight: 0,
flexGrow: 1, flexGrow: 0,
scrollX: false, scrollX: false,
scrollY: true, scrollY: true,
stickyScroll: true, stickyScroll: true,
@@ -99,6 +99,7 @@ export class Transcript {
viewportCulling: true, viewportCulling: true,
contentOptions: { contentOptions: {
flexDirection: "column", flexDirection: "column",
minHeight: 0,
paddingTop: 1, paddingTop: 1,
paddingBottom: 1, paddingBottom: 1,
paddingLeft: 1, paddingLeft: 1,