fix(tui): suppress terminal scrollbar glyphs

This commit is contained in:
Xubin Ren
2026-08-17 20:56:10 +08:00
parent 9514b9b909
commit 0035cd67cd
4 changed files with 42 additions and 4 deletions
+22
View File
@@ -20,6 +20,18 @@ const options: AppOptions = {
theme: "auto",
}
interface HiddenScrollBar {
visible: boolean
slider: { visible: boolean }
startArrow: { visible: boolean }
endArrow: { visible: boolean }
}
interface HiddenScrollBox {
verticalScrollBar: HiddenScrollBar
horizontalScrollBar: HiddenScrollBar
}
function occurrences(frame: string, value: string): number {
return frame.split(value).length - 1
}
@@ -718,6 +730,8 @@ describe("NanobotTui layout", () => {
shell: { backgroundColor: { intent: string } }
composerFrame: { backgroundColor: { intent: string } }
composer: { backgroundColor: { intent: string } }
transcript: { root: HiddenScrollBox }
diffViewer: { scroll: HiddenScrollBox }
}
const lines = setup.captureSpans().lines
const spans = lines.flatMap((line) => line.spans)
@@ -730,6 +744,14 @@ describe("NanobotTui layout", () => {
expect(internals.composer.backgroundColor.intent).toBe("default")
expect(spans.length).toBeGreaterThan(0)
expect(brandedRows).toHaveLength(0)
for (const scrollBox of [internals.transcript.root, internals.diffViewer.scroll]) {
for (const bar of [scrollBox.verticalScrollBar, scrollBox.horizontalScrollBar]) {
expect(bar.visible).toBeFalse()
expect(bar.slider.visible).toBeFalse()
expect(bar.startArrow.visible).toBeFalse()
expect(bar.endArrow.visible).toBeFalse()
}
}
})
test("rethemes the complete retained interface when the terminal appearance changes", async () => {
+3 -1
View File
@@ -12,6 +12,7 @@ import {
} from "@opentui/core"
import type { FileEditEvent, HistoryMessage } from "./protocol"
import { hideScrollbars } from "./scrollbox"
export interface DiffViewerTheme {
text: string
@@ -152,9 +153,10 @@ export class DiffViewer {
paddingTop: 1,
paddingBottom: 1,
},
verticalScrollbarOptions: { visible: true },
verticalScrollbarOptions: { visible: false },
horizontalScrollbarOptions: { visible: false },
})
hideScrollbars(this.scroll)
this.footer = new TextRenderable(renderer, {
id: "nanobot-tui-diff-footer",
content: "←/→ file · pgup/pgdn scroll · esc close",
+14
View File
@@ -0,0 +1,14 @@
import type { ScrollBoxRenderable } from "@opentui/core"
/** Keep scrolling functional without drawing terminal-dependent block glyphs. */
export function hideScrollbars(scrollBox: ScrollBoxRenderable): void {
for (const bar of [scrollBox.verticalScrollBar, scrollBox.horizontalScrollBar]) {
// ScrollBar can make itself visible again when content starts overflowing.
// Hiding its children as well keeps embedded terminals deterministic.
bar.visible = false
bar.showArrows = false
bar.slider.visible = false
bar.startArrow.visible = false
bar.endArrow.visible = false
}
}
+3 -3
View File
@@ -11,6 +11,7 @@ import {
} from "@opentui/core"
import type { FileEditEvent, HistoryMessage, ToolProgressEvent } from "./protocol"
import { hideScrollbars } from "./scrollbox"
export interface TranscriptTheme {
text: string
@@ -88,8 +89,7 @@ export class Transcript {
horizontalScrollbarOptions: { visible: false },
onMouseScroll: () => this.scheduleNavigationUpdate(),
})
this.root.verticalScrollBar.visible = false
this.root.horizontalScrollBar.visible = false
hideScrollbars(this.root)
}
setTheme(theme: TranscriptTheme): void {
@@ -151,7 +151,7 @@ export class Transcript {
this.wrote = false
this.nextId = 0
this.navigation = { awayFromBottom: false, unseenOutput: false }
this.root.verticalScrollBar.visible = false
hideScrollbars(this.root)
this.header(header)
this.emitNavigation()
}