mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
Today there's no way to remove a queued message — ↑ loads it for edit, ctrl-K dispatches the head, but a draft you no longer want stays put forever. ctrl-C just clears the composer and exits edit mode without touching the queue. Two new bindings, both gated on queueEditIdx !== null so they're inert when the user isn't pointing at a queue item: - ctrl-X — delete the queue item being edited, clear composer, exit edit mode. "cut" matches the mental model and doesn't collide with any existing binding. - esc — cancel the edit (composer clears, item stays in queue). Mirrors ctrl-C's existing behavior so muscle memory has two paths. Header line now reads `queued (3) · editing 2 · ⌃X delete · esc cancel` when in edit mode, so the affordance is discoverable without /help. The /help hotkey table also gets a Ctrl+X entry. ctrl-C is intentionally unchanged: it should never destroy queued content. Cancel is non-destructive (esc / ctrl-C); only ctrl-X removes the item.
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { Box, Text } from '@hermes/ink'
|
|
|
|
import { compactPreview } from '../lib/text.js'
|
|
import type { Theme } from '../theme.js'
|
|
|
|
export const QUEUE_WINDOW = 3
|
|
|
|
export function getQueueWindow(queueLen: number, queueEditIdx: number | null) {
|
|
const start =
|
|
queueEditIdx === null ? 0 : Math.max(0, Math.min(queueEditIdx - 1, Math.max(0, queueLen - QUEUE_WINDOW)))
|
|
|
|
const end = Math.min(queueLen, start + QUEUE_WINDOW)
|
|
|
|
return { end, showLead: start > 0, showTail: end < queueLen, start }
|
|
}
|
|
|
|
export function QueuedMessages({ cols, queueEditIdx, queued, t }: QueuedMessagesProps) {
|
|
if (!queued.length) {
|
|
return null
|
|
}
|
|
|
|
const q = getQueueWindow(queued.length, queueEditIdx)
|
|
|
|
return (
|
|
<Box flexDirection="column" marginTop={1}>
|
|
<Text color={t.color.dim} dimColor>
|
|
queued ({queued.length})
|
|
{queueEditIdx !== null ? ` · editing ${queueEditIdx + 1} · ⌃X delete · esc cancel` : ''}
|
|
</Text>
|
|
|
|
{q.showLead && (
|
|
<Text color={t.color.dim} dimColor>
|
|
{' '}
|
|
…
|
|
</Text>
|
|
)}
|
|
|
|
{queued.slice(q.start, q.end).map((item, i) => {
|
|
const idx = q.start + i
|
|
const active = queueEditIdx === idx
|
|
|
|
return (
|
|
<Text color={active ? t.color.amber : t.color.dim} dimColor key={`${idx}-${item.slice(0, 16)}`}>
|
|
{active ? '▸' : ' '} {idx + 1}. {compactPreview(item, Math.max(16, cols - 10))}
|
|
</Text>
|
|
)
|
|
})}
|
|
|
|
{q.showTail && (
|
|
<Text color={t.color.dim} dimColor>
|
|
{' '}…and {queued.length - q.end} more
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
interface QueuedMessagesProps {
|
|
cols: number
|
|
queueEditIdx: number | null
|
|
queued: string[]
|
|
t: Theme
|
|
}
|