mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-31 16:21:50 +03:00
fix(tui): restore session header card
This commit is contained in:
+8
-1
@@ -283,9 +283,11 @@ describe("NanobotTui layout", () => {
|
|||||||
composer: { backgroundColor: { intent: string }; textColor: { toInts(): number[] } }
|
composer: { backgroundColor: { intent: string }; textColor: { toInts(): number[] } }
|
||||||
transcript: {
|
transcript: {
|
||||||
markdown: Set<{ syntaxStyle: object }>
|
markdown: Set<{ syntaxStyle: object }>
|
||||||
|
frames: Set<{ borderColor: { toInts(): number[] } }>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const markdown = [...internals.transcript.markdown][0]
|
const markdown = [...internals.transcript.markdown][0]
|
||||||
|
const sessionFrame = [...internals.transcript.frames][0]
|
||||||
const darkSyntax = markdown?.syntaxStyle
|
const darkSyntax = markdown?.syntaxStyle
|
||||||
|
|
||||||
setup.renderer.emit(CliRenderEvents.THEME_MODE, "light")
|
setup.renderer.emit(CliRenderEvents.THEME_MODE, "light")
|
||||||
@@ -299,6 +301,7 @@ describe("NanobotTui layout", () => {
|
|||||||
expect(internals.shell.backgroundColor.intent).toBe("default")
|
expect(internals.shell.backgroundColor.intent).toBe("default")
|
||||||
expect(internals.composer.backgroundColor.intent).toBe("default")
|
expect(internals.composer.backgroundColor.intent).toBe("default")
|
||||||
expect(internals.composer.textColor.toInts().slice(0, 3)).toEqual([24, 24, 27])
|
expect(internals.composer.textColor.toInts().slice(0, 3)).toEqual([24, 24, 27])
|
||||||
|
expect(sessionFrame?.borderColor.toInts().slice(0, 3)).toEqual([212, 212, 216])
|
||||||
expect(markdown?.syntaxStyle).not.toBe(darkSyntax)
|
expect(markdown?.syntaxStyle).not.toBe(darkSyntax)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -316,10 +319,14 @@ describe("NanobotTui layout", () => {
|
|||||||
const userLine = frame.split("\n").find((line) => line.includes("User question")) || ""
|
const userLine = frame.split("\n").find((line) => line.includes("User question")) || ""
|
||||||
const agentLine = frame.split("\n").find((line) => line.includes("Agent **answer**")) || ""
|
const agentLine = frame.split("\n").find((line) => line.includes("Agent **answer**")) || ""
|
||||||
const headerLine = frame.split("\n").find((line) => line.includes(">_ nanobot")) || ""
|
const headerLine = frame.split("\n").find((line) => line.includes(">_ nanobot")) || ""
|
||||||
|
const headerBorder = frame.split("\n").find((line) => line.includes("╭")) || ""
|
||||||
|
|
||||||
expect(userLine).toContain("› User question")
|
expect(userLine).toContain("› User question")
|
||||||
expect(agentLine).toContain("• Agent **answer**")
|
expect(agentLine).toContain("• Agent **answer**")
|
||||||
expect(headerLine).not.toMatch(/[╭╮│]/u)
|
expect(userLine).not.toContain("│")
|
||||||
|
expect(agentLine).not.toContain("│")
|
||||||
|
expect(headerLine).toContain("│")
|
||||||
|
expect(headerBorder.trim().length).toBeLessThanOrEqual(62)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("keeps footer status and shortcuts visually separated", async () => {
|
test("keeps footer status and shortcuts visually separated", async () => {
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ function transcriptTheme(palette: Palette): TranscriptTheme {
|
|||||||
error: palette.error,
|
error: palette.error,
|
||||||
user: palette.user,
|
user: palette.user,
|
||||||
assistant: palette.accent,
|
assistant: palette.accent,
|
||||||
|
border: palette.border,
|
||||||
syntax: syntaxStyle(palette),
|
syntax: syntaxStyle(palette),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-1
@@ -17,6 +17,7 @@ export interface TranscriptTheme {
|
|||||||
error: string
|
error: string
|
||||||
user: string
|
user: string
|
||||||
assistant: string
|
assistant: string
|
||||||
|
border: string
|
||||||
syntax: SyntaxStyle
|
syntax: SyntaxStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ export class Transcript {
|
|||||||
}> = []
|
}> = []
|
||||||
private readonly markdown = new Set<MarkdownRenderable>()
|
private readonly markdown = new Set<MarkdownRenderable>()
|
||||||
private readonly activities = new Set<Activity>()
|
private readonly activities = new Set<Activity>()
|
||||||
|
private readonly frames = new Set<BoxRenderable>()
|
||||||
private wrote = false
|
private wrote = false
|
||||||
private nextId = 0
|
private nextId = 0
|
||||||
|
|
||||||
@@ -84,6 +86,7 @@ export class Transcript {
|
|||||||
this.theme = theme
|
this.theme = theme
|
||||||
for (const { renderable, tone } of this.styledText) renderable.fg = theme[tone]
|
for (const { renderable, tone } of this.styledText) renderable.fg = theme[tone]
|
||||||
for (const renderable of this.markdown) renderable.syntaxStyle = theme.syntax
|
for (const renderable of this.markdown) renderable.syntaxStyle = theme.syntax
|
||||||
|
for (const frame of this.frames) frame.borderColor = theme.border
|
||||||
// Markdown may still be rendering this frame. Release the prior native
|
// Markdown may still be rendering this frame. Release the prior native
|
||||||
// style only after the renderer reaches idle, matching OpenCode's retained
|
// style only after the renderer reaches idle, matching OpenCode's retained
|
||||||
// theme lifecycle and avoiding both leaks and use-after-free transitions.
|
// theme lifecycle and avoiding both leaks and use-after-free transitions.
|
||||||
@@ -91,15 +94,27 @@ export class Transcript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
header(options: TranscriptHeader): void {
|
header(options: TranscriptHeader): void {
|
||||||
const row = this.createRow("header")
|
const row = new BoxRenderable(this.renderer, {
|
||||||
|
id: this.id("header-row"),
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: 62,
|
||||||
|
flexDirection: "column",
|
||||||
|
border: true,
|
||||||
|
borderStyle: "rounded",
|
||||||
|
borderColor: this.theme.border,
|
||||||
|
paddingLeft: 1,
|
||||||
|
paddingRight: 1,
|
||||||
|
})
|
||||||
const title = this.createText(`>_ nanobot v${options.version}`, "text", true)
|
const title = this.createText(`>_ nanobot v${options.version}`, "text", true)
|
||||||
const context = this.createText([
|
const context = this.createText([
|
||||||
|
"",
|
||||||
`${options.model} · ${options.access}`,
|
`${options.model} · ${options.access}`,
|
||||||
options.workspace,
|
options.workspace,
|
||||||
].join("\n"), "muted")
|
].join("\n"), "muted")
|
||||||
row.add(title)
|
row.add(title)
|
||||||
row.add(context)
|
row.add(context)
|
||||||
this.root.add(row)
|
this.root.add(row)
|
||||||
|
this.frames.add(row)
|
||||||
this.wrote = true
|
this.wrote = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +128,7 @@ export class Transcript {
|
|||||||
this.styledText.length = 0
|
this.styledText.length = 0
|
||||||
this.markdown.clear()
|
this.markdown.clear()
|
||||||
this.activities.clear()
|
this.activities.clear()
|
||||||
|
this.frames.clear()
|
||||||
this.wrote = false
|
this.wrote = false
|
||||||
this.nextId = 0
|
this.nextId = 0
|
||||||
this.header(header)
|
this.header(header)
|
||||||
@@ -229,6 +245,7 @@ export class Transcript {
|
|||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.live = null
|
this.live = null
|
||||||
this.activity = null
|
this.activity = null
|
||||||
|
this.frames.clear()
|
||||||
this.theme.syntax.destroy()
|
this.theme.syntax.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user