From f7a4024e5ba5508db621f8cfe8c5616a95b7910b Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Mon, 20 Oct 2025 23:55:30 +0200 Subject: [PATCH 01/25] textarea --- .../cmd/tui/component/prompt/autocomplete.tsx | 16 ++-- .../cli/cmd/tui/component/prompt/index.tsx | 73 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 45d19657e6..82d8338d96 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -1,4 +1,4 @@ -import type { ParsedKey, BoxRenderable, InputRenderable } from "@opentui/core" +import type { BoxRenderable, TextareaRenderable, KeyEvent } from "@opentui/core" import fuzzysort from "fuzzysort" import { firstBy } from "remeda" import { createMemo, createResource, createEffect, onMount, For, Show } from "solid-js" @@ -12,7 +12,7 @@ import type { PromptInfo } from "./history" export type AutocompleteRef = { onInput: (value: string) => void - onKeyDown: (e: ParsedKey) => void + onKeyDown: (e: KeyEvent) => void visible: false | "@" | "/" } @@ -28,7 +28,7 @@ export function Autocomplete(props: { sessionID?: string setPrompt: (input: (prompt: PromptInfo) => void) => void anchor: () => BoxRenderable - input: () => InputRenderable + input: () => TextareaRenderable ref: (ref: AutocompleteRef) => void }) { const sdk = useSDK() @@ -138,8 +138,9 @@ export function Autocomplete(props: { display: "/" + command.name, description: command.description, onSelect: () => { + console.log("commands.onSelect", command.name, Bun.stringWidth(props.input().value)) props.input().value = "/" + command.name + " " - props.input().cursorPosition = props.input().value.length + props.input().cursorOffset = Bun.stringWidth(props.input().value) }, }) } @@ -238,9 +239,10 @@ export function Autocomplete(props: { } function show(mode: "@" | "/") { + console.log("show", mode, props.input().visualCursor.offset) setStore({ visible: mode, - index: props.input().cursorPosition, + index: props.input().visualCursor.offset, position: { x: props.anchor().x, y: props.anchor().y, @@ -262,7 +264,7 @@ export function Autocomplete(props: { onInput(value: string) { if (store.visible && value.length <= store.index) hide() }, - onKeyDown(e: ParsedKey) { + onKeyDown(e: KeyEvent) { if (store.visible) { if (e.name === "up") move(-1) if (e.name === "down") move(1) @@ -278,7 +280,7 @@ export function Autocomplete(props: { } if (e.name === "/") { - if (props.input().cursorPosition === 0) show("/") + if (props.input().visualCursor.offset === 0) show("/") } } }, diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index c7cd50dd1c..78f5be221e 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -1,4 +1,4 @@ -import { InputRenderable, TextAttributes, BoxRenderable } from "@opentui/core" +import { TextAttributes, BoxRenderable, TextareaRenderable, MouseEvent, KeyEvent } from "@opentui/core" import { createEffect, createMemo, Match, Switch, type JSX } from "solid-js" import { useLocal } from "@tui/context/local" import { Theme } from "@tui/context/theme" @@ -34,7 +34,7 @@ export type PromptRef = { } export function Prompt(props: PromptProps) { - let input: InputRenderable + let input: TextareaRenderable let anchor: BoxRenderable let autocomplete: AutocompleteRef @@ -69,7 +69,8 @@ export function Prompt(props: PromptProps) { input: content, parts: [], }) - input.cursorPosition = content.length + console.log("editor.open", content, Bun.stringWidth(content)) + input.cursorOffset = Bun.stringWidth(content) } }, }, @@ -141,7 +142,8 @@ export function Prompt(props: PromptProps) { }, set(prompt) { setStore("prompt", prompt) - input.cursorPosition = prompt.input.length + console.log("prompt.set", prompt.input, Bun.stringWidth(prompt.input)) + input.cursorOffset = Bun.stringWidth(prompt.input) }, reset() { setStore("prompt", { @@ -243,7 +245,8 @@ export function Prompt(props: PromptProps) { input={() => input} setPrompt={(cb) => { setStore("prompt", produce(cb)) - input.cursorPosition = store.prompt.input.length + console.log("setPrompt", store.prompt.input, Bun.stringWidth(store.prompt.input)) + input.cursorOffset = Bun.stringWidth(store.prompt.input) }} value={store.prompt.input} /> @@ -252,18 +255,23 @@ export function Prompt(props: PromptProps) { flexDirection="row" {...SplitBorder} borderColor={keybind.leader ? Theme.accent : store.mode === "shell" ? Theme.secondary : undefined} + justifyContent="space-evenly" > - + {store.mode === "normal" ? ">" : "!"} - - { + +