diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-variant.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-variant.tsx index 28ee1b2825..12c1d08d42 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-variant.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-variant.tsx @@ -34,6 +34,8 @@ export function DialogVariant() { title={"Select variant"} current={local.model.variant.selected()} flat={true} + hideFilter={true} + numbered={true} /> ) } diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 34c6ee8787..13d8b53030 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -21,6 +21,8 @@ export interface DialogSelectProps { onFilter?: (query: string) => void onSelect?: (option: DialogSelectOption) => void skipFilter?: boolean + hideFilter?: boolean + numbered?: boolean keybind?: { keybind?: Keybind.Info title: string @@ -194,6 +196,21 @@ export function DialogSelect(props: DialogSelectProps) { if (evt.name === "home") moveTo(0) if (evt.name === "end") moveTo(flat().length - 1) + if (props.numbered && !evt.ctrl && !evt.alt && !evt.meta) { + const num = parseInt(evt.name, 10) + if (num >= 1 && num <= 9 && num <= flat().length) { + evt.preventDefault() + evt.stopPropagation() + const index = num - 1 + const option = flat()[index] + if (option) { + if (option.onSelect) option.onSelect(dialog) + props.onSelect?.(option) + } + return + } + } + if (evt.name === "return") { const option = selected() if (option) { @@ -240,29 +257,31 @@ export function DialogSelect(props: DialogSelectProps) { esc - - { - batch(() => { - setStore("filter", e) - props.onFilter?.(e) - }) - }} - focusedBackgroundColor={theme.backgroundPanel} - cursorColor={theme.primary} - focusedTextColor={theme.textMuted} - ref={(r) => { - input = r - setTimeout(() => { - if (!input) return - if (input.isDestroyed) return - input.focus() - }, 1) - }} - placeholder={props.placeholder ?? "Search"} - placeholderColor={theme.textMuted} - /> - + + + { + batch(() => { + setStore("filter", e) + props.onFilter?.(e) + }) + }} + focusedBackgroundColor={theme.backgroundPanel} + cursorColor={theme.primary} + focusedTextColor={theme.textMuted} + ref={(r) => { + input = r + setTimeout(() => { + if (!input) return + if (input.isDestroyed) return + input.focus() + }, 1) + }} + placeholder={props.placeholder ?? "Search"} + placeholderColor={theme.textMuted} + /> + + 0} @@ -293,6 +312,25 @@ export function DialogSelect(props: DialogSelectProps) { {(option) => { const active = createMemo(() => isDeepEqual(option.value, selected()?.value)) const current = createMemo(() => isDeepEqual(option.value, props.current)) + const flatIndex = createMemo(() => flat().findIndex((x) => isDeepEqual(x.value, option.value))) + const numberLabel = createMemo(() => { + if (!props.numbered) return undefined + const idx = flatIndex() + return idx >= 0 && idx < 9 ? `${idx + 1}` : undefined + }) + const gutter = createMemo(() => { + if (numberLabel()) { + return ( + + {numberLabel()} + + ) + } + return option.gutter + }) return ( (props: DialogSelectProps) { moveTo(index) }} backgroundColor={active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)} - paddingLeft={current() || option.gutter ? 1 : 3} + paddingLeft={current() || gutter() ? 1 : 3} paddingRight={3} gap={1} > @@ -326,7 +364,7 @@ export function DialogSelect(props: DialogSelectProps) { description={option.description !== category ? option.description : undefined} active={active()} current={current()} - gutter={option.gutter} + gutter={gutter()} /> )