feat: personality selector
This commit is contained in:
+20
-8
@@ -1037,9 +1037,13 @@ export function App({ gw }: { gw: GatewayClient }) {
|
||||
return
|
||||
}
|
||||
|
||||
if (completions.length && input && historyIdx === null && (key.upArrow || key.downArrow)) {
|
||||
if (completions.length && input && (key.upArrow || key.downArrow)) {
|
||||
setCompIdx(i => (key.upArrow ? (i - 1 + completions.length) % completions.length : (i + 1) % completions.length))
|
||||
|
||||
if (historyIdx !== null) {
|
||||
setHistoryIdx(null)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1828,13 +1832,16 @@ export function App({ gw }: { gw: GatewayClient }) {
|
||||
|
||||
case 'personality':
|
||||
if (arg) {
|
||||
rpc('config.set', { key: 'personality', value: arg }).then((r: any) =>
|
||||
sys(`personality: ${r.value || 'default'}`)
|
||||
)
|
||||
rpc('config.set', { session_id: sid, key: 'personality', value: arg }).then((r: any) => {
|
||||
if (r?.cleared) {
|
||||
setMessages([])
|
||||
setHistoryItems([])
|
||||
}
|
||||
|
||||
sys(`personality → ${r?.value}`)
|
||||
})
|
||||
} else {
|
||||
gw.request('slash.exec', { command: 'personality', session_id: sid })
|
||||
.then((r: any) => panel('Personality', [{ text: r?.output || '(no output)' }]))
|
||||
.catch(() => sys('personality command failed'))
|
||||
rpc('config.get', { key: 'personality' }).then((r: any) => sys(`personality: ${r?.value || 'default'}`))
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -2190,6 +2197,11 @@ export function App({ gw }: { gw: GatewayClient }) {
|
||||
|
||||
const submit = useCallback(
|
||||
(value: string) => {
|
||||
if (completions.length && completions[compIdx]) {
|
||||
value = value.slice(0, compReplace) + completions[compIdx].text
|
||||
setInput(value)
|
||||
}
|
||||
|
||||
if (!value.trim() && !inputBuf.length) {
|
||||
const now = Date.now()
|
||||
const dbl = now - lastEmptyAt.current < 450
|
||||
@@ -2247,7 +2259,7 @@ export function App({ gw }: { gw: GatewayClient }) {
|
||||
|
||||
dispatchSubmission([...inputBuf, value].join('\n'))
|
||||
},
|
||||
[dequeue, dispatchSubmission, inputBuf, sid]
|
||||
[compIdx, compReplace, completions, dequeue, dispatchSubmission, inputBuf, sid]
|
||||
)
|
||||
|
||||
// ── Derived ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -8,7 +8,7 @@ type InkExt = typeof Ink & {
|
||||
}
|
||||
|
||||
const ink = Ink as unknown as InkExt
|
||||
const { Box, Text, useStdin, useInput, stringWidth, useDeclaredCursor, useTerminalFocus } = ink
|
||||
const { Box, Text, useInput, stringWidth, useDeclaredCursor, useTerminalFocus } = ink
|
||||
|
||||
// ── ANSI escapes ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -17,7 +17,6 @@ const INV = `${ESC}[7m`
|
||||
const INV_OFF = `${ESC}[27m`
|
||||
const DIM = `${ESC}[2m`
|
||||
const DIM_OFF = `${ESC}[22m`
|
||||
const FWD_DEL_RE = new RegExp(`${ESC}\\[3(?:[~$^]|;)`)
|
||||
const PRINTABLE = /^[ -~\u00a0-\uffff]+$/
|
||||
const BRACKET_PASTE = new RegExp(`${ESC}?\\[20[01]~`, 'g')
|
||||
|
||||
@@ -121,31 +120,6 @@ function renderWithCursor(value: string, cursor: number) {
|
||||
return done ? out : out + invert(' ')
|
||||
}
|
||||
|
||||
// ── Forward-delete detection hook ────────────────────────────────────
|
||||
|
||||
function useFwdDelete(active: boolean) {
|
||||
const ref = useRef(false)
|
||||
const { inputEmitter: ee } = useStdin()
|
||||
|
||||
useEffect(() => {
|
||||
if (!active) {
|
||||
return
|
||||
}
|
||||
|
||||
const h = (d: string) => {
|
||||
ref.current = FWD_DEL_RE.test(d)
|
||||
}
|
||||
|
||||
ee.prependListener('input', h)
|
||||
|
||||
return () => {
|
||||
ee.removeListener('input', h)
|
||||
}
|
||||
}, [active, ee])
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface PasteEvent {
|
||||
@@ -170,7 +144,6 @@ interface Props {
|
||||
|
||||
export function TextInput({ columns = 80, value, onChange, onPaste, onSubmit, placeholder = '', focus = true }: Props) {
|
||||
const [cur, setCur] = useState(value.length)
|
||||
const fwdDel = useFwdDelete(focus)
|
||||
const termFocus = useTerminalFocus()
|
||||
|
||||
const curRef = useRef(cur)
|
||||
@@ -363,7 +336,7 @@ export function TextInput({ columns = 80, value, onChange, onPaste, onSubmit, pl
|
||||
}
|
||||
|
||||
// Deletion
|
||||
else if ((k.backspace || k.delete) && !fwdDel.current && c > 0) {
|
||||
else if (k.backspace && c > 0) {
|
||||
if (mod) {
|
||||
const t = wordLeft(v, c)
|
||||
v = v.slice(0, t) + v.slice(c)
|
||||
@@ -372,7 +345,7 @@ export function TextInput({ columns = 80, value, onChange, onPaste, onSubmit, pl
|
||||
v = v.slice(0, c - 1) + v.slice(c)
|
||||
c--
|
||||
}
|
||||
} else if (k.delete && fwdDel.current && c < v.length) {
|
||||
} else if (k.delete && c < v.length) {
|
||||
if (mod) {
|
||||
const t = wordRight(v, c)
|
||||
v = v.slice(0, c) + v.slice(t)
|
||||
|
||||
Reference in New Issue
Block a user