Skip to content

Commit bf30615

Browse files
committed
more intuitive navigation
1 parent 36dc554 commit bf30615

File tree

3 files changed

+61
-30
lines changed

3 files changed

+61
-30
lines changed

cli/src/chat.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ export const Chat = ({
9494
cursorPosition,
9595
lastEditDueToNav,
9696
setInputValue,
97-
setCursorPosition,
9897
inputFocused,
9998
setInputFocused,
10099
slashSelectedIndex,
@@ -127,7 +126,6 @@ export const Chat = ({
127126
cursorPosition: store.cursorPosition,
128127
lastEditDueToNav: store.lastEditDueToNav,
129128
setInputValue: store.setInputValue,
130-
setCursorPosition: store.setCursorPosition,
131129
inputFocused: store.inputFocused,
132130
setInputFocused: store.setInputFocused,
133131
slashSelectedIndex: store.slashSelectedIndex,
@@ -935,7 +933,6 @@ export const Chat = ({
935933
textAttributes={theme.messageTextAttributes}
936934
ref={inputRef}
937935
cursorPosition={cursorPosition}
938-
setCursorPosition={setCursorPosition}
939936
/>
940937
</box>
941938
<box

cli/src/components/multiline-input.tsx

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212

1313
import { useOpentuiPaste } from '../hooks/use-opentui-paste'
1414
import { useTheme } from '../hooks/use-theme'
15+
import { clamp } from '../utils/math'
1516
import { computeInputLayoutMetrics } from '../utils/text-layout'
1617

1718
import type { InputValue } from '../state/chat-store'
@@ -90,7 +91,6 @@ interface MultilineInputProps {
9091
width: number
9192
textAttributes?: number
9293
cursorPosition: number
93-
setCursorPosition: (position: number) => void
9494
}
9595

9696
export type MultilineInputHandle = {
@@ -112,7 +112,6 @@ export const MultilineInput = forwardRef<
112112
textAttributes,
113113
onKeyIntercept,
114114
cursorPosition,
115-
setCursorPosition,
116115
}: MultilineInputProps,
117116
forwardedRef,
118117
) {
@@ -143,12 +142,10 @@ export const MultilineInput = forwardRef<
143142

144143
// Sync cursor when value changes externally
145144
useEffect(() => {
146-
if (cursorPosition > value.length) {
147-
setCursorPosition(value.length)
148-
}
149-
if (cursorPosition < 0) {
150-
setCursorPosition(0)
151-
}
145+
onChange((prev) => ({
146+
...prev,
147+
cursorPosition: clamp(cursorPosition, 0, value.length),
148+
}))
152149
}, [value.length, cursorPosition])
153150

154151
useOpentuiPaste(
@@ -467,7 +464,11 @@ export const MultilineInput = forwardRef<
467464
(key.name === 'left' || lowerKeyName === 'b')
468465
) {
469466
preventKeyDefault(key)
470-
setCursorPosition(wordStart)
467+
onChange({
468+
text: value,
469+
cursorPosition: wordStart,
470+
lastEditDueToNav: false,
471+
})
471472
return
472473
}
473474

@@ -477,7 +478,11 @@ export const MultilineInput = forwardRef<
477478
(key.name === 'right' || lowerKeyName === 'f')
478479
) {
479480
preventKeyDefault(key)
480-
setCursorPosition(wordEnd)
481+
onChange({
482+
text: value,
483+
cursorPosition: wordEnd,
484+
lastEditDueToNav: false,
485+
})
481486
return
482487
}
483488

@@ -488,7 +493,11 @@ export const MultilineInput = forwardRef<
488493
(key.name === 'home' && !key.ctrl && !key.meta)
489494
) {
490495
preventKeyDefault(key)
491-
setCursorPosition(lineStart)
496+
onChange({
497+
text: value,
498+
cursorPosition: lineStart,
499+
lastEditDueToNav: false,
500+
})
492501
return
493502
}
494503

@@ -499,7 +508,11 @@ export const MultilineInput = forwardRef<
499508
(key.name === 'end' && !key.ctrl && !key.meta)
500509
) {
501510
preventKeyDefault(key)
502-
setCursorPosition(lineEnd)
511+
onChange({
512+
text: value,
513+
cursorPosition: lineEnd,
514+
lastEditDueToNav: false,
515+
})
503516
return
504517
}
505518

@@ -509,7 +522,7 @@ export const MultilineInput = forwardRef<
509522
(key.ctrl && key.name === 'home')
510523
) {
511524
preventKeyDefault(key)
512-
setCursorPosition(0)
525+
onChange({ text: value, cursorPosition: 0, lastEditDueToNav: false })
513526
return
514527
}
515528

@@ -519,48 +532,76 @@ export const MultilineInput = forwardRef<
519532
(key.ctrl && key.name === 'end')
520533
) {
521534
preventKeyDefault(key)
522-
setCursorPosition(value.length)
535+
onChange({
536+
text: value,
537+
cursorPosition: value.length,
538+
lastEditDueToNav: false,
539+
})
523540
return
524541
}
525542

526543
// Ctrl+B: Backward char (Emacs)
527544
if (key.ctrl && lowerKeyName === 'b' && !key.meta && !key.option) {
528545
preventKeyDefault(key)
529-
setCursorPosition(Math.max(0, cursorPosition - 1))
546+
onChange({
547+
text: value,
548+
cursorPosition: cursorPosition - 1,
549+
lastEditDueToNav: false,
550+
})
530551
return
531552
}
532553

533554
// Ctrl+F: Forward char (Emacs)
534555
if (key.ctrl && lowerKeyName === 'f' && !key.meta && !key.option) {
535556
preventKeyDefault(key)
536-
setCursorPosition(Math.min(value.length, cursorPosition + 1))
557+
onChange({
558+
text: value,
559+
cursorPosition: Math.min(value.length, cursorPosition + 1),
560+
lastEditDueToNav: false,
561+
})
537562
return
538563
}
539564

540565
// Left arrow (no modifiers)
541566
if (key.name === 'left' && !key.ctrl && !key.meta && !key.option) {
542567
preventKeyDefault(key)
543-
setCursorPosition(Math.max(0, cursorPosition - 1))
568+
onChange({
569+
text: value,
570+
cursorPosition: cursorPosition - 1,
571+
lastEditDueToNav: false,
572+
})
544573
return
545574
}
546575

547576
// Right arrow (no modifiers)
548577
if (key.name === 'right' && !key.ctrl && !key.meta && !key.option) {
549578
preventKeyDefault(key)
550-
setCursorPosition(Math.min(value.length, cursorPosition + 1))
579+
onChange({
580+
text: value,
581+
cursorPosition: cursorPosition + 1,
582+
lastEditDueToNav: false,
583+
})
551584
return
552585
}
553586

554587
// Up arrow (no modifiers)
555588
if (key.name === 'up' && !key.ctrl && !key.meta && !key.option) {
556589
preventKeyDefault(key)
557-
setCursorPosition(cursorPosition - getEffectiveCols())
590+
onChange({
591+
text: value,
592+
cursorPosition: cursorPosition - getEffectiveCols(),
593+
lastEditDueToNav: false,
594+
})
558595
}
559596

560597
// Down arrow (no modifiers)
561598
if (key.name === 'down' && !key.ctrl && !key.meta && !key.option) {
562599
preventKeyDefault(key)
563-
setCursorPosition(cursorPosition + getEffectiveCols())
600+
onChange({
601+
text: value,
602+
cursorPosition: cursorPosition + getEffectiveCols(),
603+
lastEditDueToNav: false,
604+
})
564605
}
565606

566607
// Regular character input

cli/src/state/chat-store.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ type ChatStoreActions = {
4545
setInputValue: (
4646
value: InputValue | ((prev: InputValue) => InputValue),
4747
) => void
48-
setCursorPosition: (value: number | ((prev: number) => number)) => void
4948
setInputFocused: (focused: boolean) => void
5049
setActiveSubagents: (
5150
value: Set<string> | ((prev: Set<string>) => Set<string>),
@@ -125,12 +124,6 @@ export const useChatStore = create<ChatStore>()(
125124
state.lastEditDueToNav = lastEditDueToNav
126125
}),
127126

128-
setCursorPosition: (value) =>
129-
set((state) => {
130-
state.cursorPosition =
131-
typeof value === 'function' ? value(state.cursorPosition) : value
132-
}),
133-
134127
setInputFocused: (focused) =>
135128
set((state) => {
136129
state.inputFocused = focused

0 commit comments

Comments
 (0)