diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/search.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/search.tsx new file mode 100644 index 00000000000..60dccbd6fb7 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/search.tsx @@ -0,0 +1,230 @@ +import { BoxRenderable, TextareaRenderable, type KeyBinding } from "@opentui/core" +import { createEffect, createMemo, createSignal, type JSX, onMount, Show } from "solid-js" +import { useTheme } from "@tui/context/theme" +import { EmptyBorder } from "@tui/component/border" +import { createStore } from "solid-js/store" +import { useKeybind } from "@tui/context/keybind" +import { Locale } from "@/util/locale" +import { useLocal } from "@tui/context/local" +import { RGBA } from "@opentui/core" +import { useSDK } from "@tui/context/sdk" +import { useSync } from "@tui/context/sync" +import { useExit } from "../../context/exit" + +export type SearchInputProps = { + disabled?: boolean + onSubmit?: (query: string) => void + onExit?: () => void + onInput?: (query: string) => void + onNext?: () => void + onPrevious?: () => void + matchInfo?: { current: number; total: number } + sessionID?: string + ref?: (ref: SearchInputRef) => void + placeholder?: string +} + +export type SearchInputRef = { + focused: boolean + reset(): void + blur(): void + focus(): void + getValue(): string +} + +export function SearchInput(props: SearchInputProps) { + let input: TextareaRenderable + let anchor: BoxRenderable + + const exit = useExit() + const keybind = useKeybind() + const local = useLocal() + const sdk = useSDK() + const sync = useSync() + const { theme } = useTheme() + + const highlight = createMemo(() => { + const agent = local.agent.current() + if (agent?.color) return RGBA.fromHex(agent.color) + const agents = local.agent.list() + const index = agents.findIndex((x) => x.name === "search") + const colors = [theme.secondary, theme.accent, theme.success, theme.warning, theme.primary, theme.error] + if (index === -1) return colors[0] + return colors[index % colors.length] + }) + + const textareaKeybindings = createMemo(() => { + const submitBindings = keybind.all.input_submit || [] + return [ + { name: "return", action: "submit" }, + ...submitBindings.map((binding) => ({ + name: binding.name, + ctrl: binding.ctrl || undefined, + meta: binding.meta || undefined, + shift: binding.shift || undefined, + action: "submit" as const, + })), + ] satisfies KeyBinding[] + }) + + const [store, setStore] = createStore<{ + input: string + }>({ + input: "", + }) + + createEffect(() => { + if (props.disabled) input.cursorColor = theme.backgroundElement + if (!props.disabled) input.cursorColor = theme.primary + }) + + props.ref?.({ + get focused() { + return input.focused + }, + focus() { + input.focus() + }, + blur() { + input.blur() + }, + reset() { + input.clear() + setStore("input", "") + }, + getValue() { + return store.input + }, + }) + + function submit() { + if (props.disabled) return + if (!store.input) return + props.onSubmit?.(store.input) + input.clear() + setStore("input", "") + } + + onMount(() => { + input.focus() + }) + + return ( + <> + (anchor = r)}> + + +