mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
tui: improve prompt handling, session interactions, and dialog focus management
This commit is contained in:
parent
dd18b766f3
commit
873cccd37c
5 changed files with 28 additions and 14 deletions
|
|
@ -8,7 +8,8 @@
|
|||
"typecheck": "tsc --noEmit",
|
||||
"test": "bun test",
|
||||
"build": "./script/build.ts",
|
||||
"dev": "bun run --conditions=browser ./src/index.ts"
|
||||
"dev": "bun run --conditions=browser ./src/index.ts",
|
||||
"random": "echo 'This is a random script'"
|
||||
},
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode"
|
||||
|
|
|
|||
|
|
@ -224,15 +224,17 @@ export function Autocomplete(props: {
|
|||
if (e.name === "escape") hide()
|
||||
if (e.name === "return") select()
|
||||
}
|
||||
if (!store.visible && e.name === "@") {
|
||||
const last = props.value.at(-1)
|
||||
if (last === " " || last === undefined) {
|
||||
show("@")
|
||||
if (!store.visible) {
|
||||
if (e.name === "@") {
|
||||
const last = props.value.at(-1)
|
||||
if (last === " " || last === undefined) {
|
||||
show("@")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!store.visible && e.name === "/") {
|
||||
if (props.input().cursorPosition === 0) show("/")
|
||||
if (e.name === "/") {
|
||||
if (props.input().cursorPosition === 0) show("/")
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ export function Prompt(props: PromptProps) {
|
|||
let anchor: BoxRenderable
|
||||
let autocomplete: AutocompleteRef
|
||||
|
||||
|
||||
const keybind = useKeybind()
|
||||
const local = useLocal()
|
||||
const sdk = useSDK()
|
||||
|
|
@ -42,6 +41,11 @@ export function Prompt(props: PromptProps) {
|
|||
const status = createMemo(() => (props.sessionID ? sync.session.status(props.sessionID) : "idle"))
|
||||
const history = usePromptHistory()
|
||||
|
||||
createEffect(() => {
|
||||
if (props.disabled) input.cursorColor = Theme.backgroundElement
|
||||
if (!props.disabled) input.cursorColor = Theme.primary
|
||||
})
|
||||
|
||||
const [store, setStore] = createStore<PromptInfo>({
|
||||
input: "",
|
||||
parts: [],
|
||||
|
|
@ -129,6 +133,10 @@ export function Prompt(props: PromptProps) {
|
|||
}}
|
||||
value={store.input}
|
||||
onKeyDown={async (e) => {
|
||||
if (props.disabled) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
autocomplete.onKeyDown(e)
|
||||
if (!autocomplete.visible) {
|
||||
if (e.name === "up" || e.name === "down") {
|
||||
|
|
@ -166,6 +174,7 @@ export function Prompt(props: PromptProps) {
|
|||
}, 0)
|
||||
}}
|
||||
onSubmit={async () => {
|
||||
if (props.disabled) return
|
||||
if (autocomplete.visible) return
|
||||
if (!store.input) return
|
||||
const sessionID = props.sessionID
|
||||
|
|
|
|||
|
|
@ -177,7 +177,8 @@ export function Session() {
|
|||
value: "session.redo",
|
||||
keybind: "messages_redo",
|
||||
category: "Session",
|
||||
onSelect: () => {
|
||||
onSelect: (dialog) => {
|
||||
dialog.clear()
|
||||
const messageID = session().revert?.messageID
|
||||
if (!messageID) return
|
||||
const message = messages().find((x) => x.role === "user" && x.id > messageID)
|
||||
|
|
@ -295,7 +296,7 @@ export function Session() {
|
|||
</Match>
|
||||
<Match when={message.role === "user"}>
|
||||
<UserMessage
|
||||
onMouseDown={() =>
|
||||
onMouseUp={() =>
|
||||
dialog.replace(() => <DialogMessage messageID={message.id} sessionID={route.sessionID} />)
|
||||
}
|
||||
message={message as UserMessage}
|
||||
|
|
@ -327,6 +328,7 @@ export function Session() {
|
|||
<box flexShrink={0}>
|
||||
<Prompt
|
||||
ref={(r) => (prompt = r)}
|
||||
disabled={permissions().length > 0}
|
||||
onSubmit={() => {
|
||||
toBottom()
|
||||
}}
|
||||
|
|
@ -347,7 +349,7 @@ const MIME_BADGE: Record<string, string> = {
|
|||
"application/pdf": "pdf",
|
||||
}
|
||||
|
||||
function UserMessage(props: { message: UserMessage; parts: Part[]; onMouseDown: () => void }) {
|
||||
function UserMessage(props: { message: UserMessage; parts: Part[]; onMouseUp: () => void }) {
|
||||
const text = createMemo(() => props.parts.flatMap((x) => (x.type === "text" && !x.synthetic ? [x] : []))[0])
|
||||
const files = createMemo(() => props.parts.flatMap((x) => (x.type === "file" ? [x] : [])))
|
||||
const sync = useSync()
|
||||
|
|
@ -361,7 +363,7 @@ function UserMessage(props: { message: UserMessage; parts: Part[]; onMouseDown:
|
|||
onMouseOut={() => {
|
||||
setHover(false)
|
||||
}}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onMouseUp={props.onMouseUp}
|
||||
border={["left"]}
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ function init() {
|
|||
refocus()
|
||||
},
|
||||
replace(input: any, onClose?: () => void) {
|
||||
focus = renderer.currentFocusedRenderable
|
||||
if (store.stack.length === 0) focus = renderer.currentFocusedRenderable
|
||||
for (const item of store.stack) {
|
||||
if (item.onClose) item.onClose()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue