feat(tui): surface session activity states

This commit is contained in:
Xubin Ren
2026-08-24 00:58:04 +08:00
parent 7e66375f59
commit 2850114eab
6 changed files with 255 additions and 24 deletions
+31 -2
View File
@@ -1,22 +1,26 @@
import {
BoxRenderable,
RGBA,
StyledText,
TextAttributes,
TextRenderable,
type CliRenderer,
type TextChunk,
} from "@opentui/core"
export interface PickerMenuTheme {
text: string
muted: string
border: string
accent?: string
warning?: string
selectedBackground?: string
}
interface PickerMenuOptions<T> {
id: string
searchText: (item: T) => string
render: (item: T) => string
render: (item: T, selected: boolean) => string | TextChunk[]
emptyText?: string
maxWidth?: number
onSelect?: (item: T) => void
@@ -68,6 +72,16 @@ export class PickerMenu<T> {
this.update(query, limit)
}
replace(items: T[]): void {
if (!this.visible) return
this.items = items
this.update(this.query, this.limit)
}
redraw(): void {
if (this.visible) this.render()
}
update(query: string, limit = this.limit): void {
if (!this.visible) return
const changed = query !== this.query
@@ -125,9 +139,16 @@ export class PickerMenu<T> {
}
for (const [index, item] of this.matches.entries()) {
const selected = index === this.selected
const rendered = this.options.render(item, selected)
const content = typeof rendered === "string"
? `${selected ? "" : " "} ${rendered}`
: new StyledText([
chunk(`${selected ? "" : " "} `, selected ? this.theme.text : this.theme.muted),
...rendered,
])
this.root.add(new TextRenderable(this.renderer, {
id: `${this.options.id}-${index}`,
content: `${selected ? "" : " "} ${this.options.render(item)}`,
content,
width: "100%",
height: 1,
wrapMode: "none",
@@ -161,3 +182,11 @@ export class PickerMenu<T> {
}
}
}
function chunk(text: string, color: string): TextChunk {
return {
__isChunk: true,
text,
fg: RGBA.fromHex(color),
}
}