mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
fix(tui): approval flow + input ergonomics + selection perf
- tui_gateway: route approvals through gateway callback (HERMES_GATEWAY_SESSION/ HERMES_EXEC_ASK) so dangerous commands emit approval.request instead of silently falling through the CLI input() path and auto-denying - approval UX: dedicated PromptZone between transcript and composer, safer defaults (sel=0, numeric quick-picks, no Esc=deny), activity trail line, outcome footer under the cost row - text input: Ctrl+A select-all, real forward Delete, Ctrl+W always consumed (fixes Ctrl+Backspace at cursor 0 inserting literal w) - hermes-ink selection: swap synchronous onRender() for throttled scheduleRender() on drag, and only notify React subscribers on presence change — no more per-cell paint/subscribe spam - useConfigSync: silence config.get polling failures instead of surfacing 'error: timeout: config.get' in the transcript
This commit is contained in:
parent
0219da9626
commit
5b386ced71
15 changed files with 319 additions and 129 deletions
|
|
@ -12,31 +12,77 @@ import { ModelPicker } from './modelPicker.js'
|
|||
import { ApprovalPrompt, ClarifyPrompt } from './prompts.js'
|
||||
import { SessionPicker } from './sessionPicker.js'
|
||||
|
||||
export function AppOverlays({
|
||||
export function PromptZone({
|
||||
cols,
|
||||
onApprovalChoice,
|
||||
onClarifyAnswer,
|
||||
onSecretSubmit,
|
||||
onSudoSubmit
|
||||
}: Pick<AppOverlaysProps, 'cols' | 'onApprovalChoice' | 'onClarifyAnswer' | 'onSecretSubmit' | 'onSudoSubmit'>) {
|
||||
const overlay = useStore($overlayState)
|
||||
const ui = useStore($uiState)
|
||||
|
||||
if (overlay.approval) {
|
||||
return (
|
||||
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
||||
<ApprovalPrompt onChoice={onApprovalChoice} req={overlay.approval} t={ui.theme} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
if (overlay.clarify) {
|
||||
return (
|
||||
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
||||
<ClarifyPrompt
|
||||
cols={cols}
|
||||
onAnswer={onClarifyAnswer}
|
||||
onCancel={() => onClarifyAnswer('')}
|
||||
req={overlay.clarify}
|
||||
t={ui.theme}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
if (overlay.sudo) {
|
||||
return (
|
||||
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
||||
<MaskedPrompt cols={cols} icon="🔐" label="sudo password required" onSubmit={onSudoSubmit} t={ui.theme} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
if (overlay.secret) {
|
||||
return (
|
||||
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
||||
<MaskedPrompt
|
||||
cols={cols}
|
||||
icon="🔑"
|
||||
label={overlay.secret.prompt}
|
||||
onSubmit={onSecretSubmit}
|
||||
sub={`for ${overlay.secret.envVar}`}
|
||||
t={ui.theme}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function FloatingOverlays({
|
||||
cols,
|
||||
compIdx,
|
||||
completions,
|
||||
onApprovalChoice,
|
||||
onClarifyAnswer,
|
||||
onModelSelect,
|
||||
onPickerSelect,
|
||||
onSecretSubmit,
|
||||
onSudoSubmit,
|
||||
pagerPageSize
|
||||
}: AppOverlaysProps) {
|
||||
}: Pick<AppOverlaysProps, 'cols' | 'compIdx' | 'completions' | 'onModelSelect' | 'onPickerSelect' | 'pagerPageSize'>) {
|
||||
const { gw } = useGateway()
|
||||
const overlay = useStore($overlayState)
|
||||
const ui = useStore($uiState)
|
||||
|
||||
const hasAny =
|
||||
overlay.approval ||
|
||||
overlay.clarify ||
|
||||
overlay.modelPicker ||
|
||||
overlay.pager ||
|
||||
overlay.picker ||
|
||||
overlay.secret ||
|
||||
overlay.sudo ||
|
||||
completions.length
|
||||
const hasAny = overlay.modelPicker || overlay.pager || overlay.picker || completions.length
|
||||
|
||||
if (!hasAny) {
|
||||
return null
|
||||
|
|
@ -46,43 +92,6 @@ export function AppOverlays({
|
|||
|
||||
return (
|
||||
<Box alignItems="flex-start" bottom="100%" flexDirection="column" left={0} position="absolute" right={0}>
|
||||
{overlay.clarify && (
|
||||
<FloatBox color={ui.theme.color.bronze}>
|
||||
<ClarifyPrompt
|
||||
cols={cols}
|
||||
onAnswer={onClarifyAnswer}
|
||||
onCancel={() => onClarifyAnswer('')}
|
||||
req={overlay.clarify}
|
||||
t={ui.theme}
|
||||
/>
|
||||
</FloatBox>
|
||||
)}
|
||||
|
||||
{overlay.approval && (
|
||||
<FloatBox color={ui.theme.color.bronze}>
|
||||
<ApprovalPrompt onChoice={onApprovalChoice} req={overlay.approval} t={ui.theme} />
|
||||
</FloatBox>
|
||||
)}
|
||||
|
||||
{overlay.sudo && (
|
||||
<FloatBox color={ui.theme.color.bronze}>
|
||||
<MaskedPrompt cols={cols} icon="🔐" label="sudo password required" onSubmit={onSudoSubmit} t={ui.theme} />
|
||||
</FloatBox>
|
||||
)}
|
||||
|
||||
{overlay.secret && (
|
||||
<FloatBox color={ui.theme.color.bronze}>
|
||||
<MaskedPrompt
|
||||
cols={cols}
|
||||
icon="🔑"
|
||||
label={overlay.secret.prompt}
|
||||
onSubmit={onSecretSubmit}
|
||||
sub={`for ${overlay.secret.envVar}`}
|
||||
t={ui.theme}
|
||||
/>
|
||||
</FloatBox>
|
||||
)}
|
||||
|
||||
{overlay.picker && (
|
||||
<FloatBox color={ui.theme.color.bronze}>
|
||||
<SessionPicker
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue