mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-04 10:11:46 +03:00
feat(tui): make follow-up queue explicit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { RGBA, StyledText, TextAttributes, type TextChunk } from "@opentui/core"
|
||||
|
||||
export interface FooterHint {
|
||||
key: string
|
||||
label: string
|
||||
tone?: "normal" | "danger"
|
||||
}
|
||||
|
||||
export interface FooterHintTheme {
|
||||
accent: string
|
||||
danger: string
|
||||
muted: string
|
||||
separator: string
|
||||
}
|
||||
|
||||
export type FooterMode =
|
||||
| "mention"
|
||||
| "active"
|
||||
| "branch"
|
||||
| "command"
|
||||
| "session"
|
||||
| "context"
|
||||
| "history"
|
||||
| "ready"
|
||||
|
||||
export function contextualFooterHints(
|
||||
mode: FooterMode,
|
||||
width: number,
|
||||
theme: FooterHintTheme,
|
||||
): StyledText {
|
||||
return footerHints(hintsFor(mode, width), theme)
|
||||
}
|
||||
|
||||
/** Give shortcuts visual hierarchy without turning the footer into a toolbar. */
|
||||
export function footerHints(hints: readonly FooterHint[], theme: FooterHintTheme): StyledText {
|
||||
const chunks: TextChunk[] = []
|
||||
hints.forEach((hint, index) => {
|
||||
if (index) chunks.push(chunk(" · ", theme.separator))
|
||||
const color = hint.tone === "danger" ? theme.danger : theme.accent
|
||||
chunks.push(chunk(hint.key, color, true))
|
||||
chunks.push(chunk(` ${hint.label}`, theme.muted))
|
||||
})
|
||||
return new StyledText(chunks)
|
||||
}
|
||||
|
||||
function hintsFor(mode: FooterMode, width: number): FooterHint[] {
|
||||
if (mode === "mention") return width >= 64
|
||||
? [hint("↑↓", "choose"), hint("tab/enter", "insert"), hint("esc", "close")]
|
||||
: [hint("enter", "insert"), hint("esc", "close")]
|
||||
if (mode === "active") return width >= 96
|
||||
? [hint("enter", "steer"), hint("tab", "queue"), hint("alt+↑", "edit"), stopHint()]
|
||||
: width >= 64 ? [hint("enter", "steer"), hint("tab", "queue"), stopHint()] : []
|
||||
if (mode === "branch") return width >= 64
|
||||
? [hint("type", "filter"), hint("↑↓", "choose"), hint("enter", "branch"), hint("esc", "close")]
|
||||
: [hint("enter", "branch"), hint("esc", "close")]
|
||||
if (mode === "command") return width >= 72
|
||||
? [hint("↑↓", "choose"), hint("tab", "complete"), hint("esc", "close")]
|
||||
: [hint("tab", "complete"), hint("esc", "close")]
|
||||
if (mode === "session") return width >= 64
|
||||
? [hint("type", "filter"), hint("↑↓", "choose"), hint("enter", "open"), hint("esc", "close")]
|
||||
: [hint("enter", "open"), hint("esc", "close")]
|
||||
if (mode === "context") return [hint("esc", "close"), hint("pgup/pgdn", "scroll")]
|
||||
if (mode === "history") return width >= 72
|
||||
? [hint("ctrl+end", "latest"), hint("pgup/pgdn", "scroll")]
|
||||
: width >= 48 ? [hint("ctrl+end", "latest")] : []
|
||||
if (width >= 112) return [
|
||||
hint("enter", "send"),
|
||||
hint("ctrl+j", "newline"),
|
||||
hint("pgup/pgdn", "scroll"),
|
||||
hint("ctrl+o", "tools"),
|
||||
stopHint(),
|
||||
]
|
||||
if (width >= 72) return [hint("enter", "send"), hint("ctrl+j", "newline"), stopHint()]
|
||||
return width >= 48 ? [hint("enter", "send"), hint("ctrl+j", "newline")] : []
|
||||
}
|
||||
|
||||
function hint(key: string, label: string): FooterHint {
|
||||
return { key, label }
|
||||
}
|
||||
|
||||
function stopHint(): FooterHint {
|
||||
return { key: "ctrl+c", label: "stop", tone: "danger" }
|
||||
}
|
||||
|
||||
function chunk(text: string, color: string, bold = false): TextChunk {
|
||||
return {
|
||||
__isChunk: true,
|
||||
text,
|
||||
fg: RGBA.fromHex(color),
|
||||
attributes: bold ? TextAttributes.BOLD : 0,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user