) => {
if (event.nativeEvent.isComposing) {
@@ -386,10 +441,11 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
[submitAnswer]
)
- // Letter shortcuts: A/B/C… pick the matching option, the trailing letter jumps
- // into "Other", and Enter confirms the current pick. Stands down whenever a
- // field is focused (you're typing, not navigating) so it never eats keystrokes
- // meant for the composer or the Other box.
+ // Arrow keys move a visual cursor, 1-9 and A/B/C… pick directly, and Enter
+ // confirms the current answer (or acts on the highlighted row). Stands down
+ // whenever a focusable control (a field, a choice button, the action bar) is
+ // focused, so it never eats keystrokes meant for the composer, the Other box,
+ // or a button the user tabbed to.
useEffect(() => {
if (!ready || !hasChoices || submitting) {
return
@@ -402,7 +458,32 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
const active = document.activeElement as HTMLElement | null
- if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)) {
+ if (
+ active &&
+ (active.isContentEditable || active.matches('a[href], button, input, select, textarea, [role="button"]'))
+ ) {
+ return
+ }
+
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
+ event.preventDefault()
+ moveActive(event.key === 'ArrowDown' ? 1 : -1)
+
+ return
+ }
+
+ if (/^[1-9]$/.test(event.key)) {
+ const index = Number(event.key) - 1
+
+ if (index < choices.length) {
+ event.preventDefault()
+ selectChoice(choices[index], index)
+ } else if (index === choices.length) {
+ event.preventDefault()
+ setActiveIndex(index)
+ textareaRef.current?.focus()
+ }
+
return
}
@@ -413,25 +494,26 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
if (index < choices.length) {
event.preventDefault()
- selectChoice(choices[index])
+ selectChoice(choices[index], index)
} else if (index === choices.length) {
event.preventDefault()
+ setActiveIndex(index)
textareaRef.current?.focus()
}
return
}
- if (event.key === 'Enter' && pendingAnswer) {
+ if (event.key === 'Enter') {
event.preventDefault()
- submitAnswer()
+ activateActive()
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
- }, [choices, hasChoices, pendingAnswer, ready, selectChoice, submitAnswer, submitting])
+ }, [activateActive, choices, hasChoices, moveActive, ready, selectChoice, submitting])
if (loading) {
return (
@@ -467,23 +549,39 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
{choices.map((choice, index) => (
selectChoice(choice)}
+ keyShortcuts={`${letterFor(index)} ${index + 1}`}
+ onClick={() => selectChoice(choice, index)}
selected={selectedChoice === choice}
/>
))}
-