Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export type PromptProps = {
sessionID?: string
visible?: boolean
disabled?: boolean
inert?: boolean
onSubmit?: () => void
ref?: (ref: PromptRef | undefined) => void
hint?: JSX.Element
Expand Down Expand Up @@ -700,7 +701,7 @@ export function Prompt(props: PromptProps) {

createEffect(() => {
if (!input || input.isDestroyed) return
if (props.visible === false || dialog.stack.length > 0) {
if (props.visible === false || dialog.stack.length > 0 || props.inert) {
if (input.focused) input.blur()
return
}
Expand Down Expand Up @@ -1388,7 +1389,7 @@ export function Prompt(props: PromptProps) {
}

const highlight = createMemo(() => {
if (leader()) return theme.border
if (leader() || props.inert) return theme.border
if (store.mode === "shell") return theme.primary
const agent = local.agent.current()
if (!agent) return theme.border
Expand Down Expand Up @@ -1494,8 +1495,8 @@ export function Prompt(props: PromptProps) {
width="100%"
placeholder={placeholderText()}
placeholderColor={theme.textMuted}
textColor={leader() ? theme.textMuted : theme.text}
focusedTextColor={leader() ? theme.textMuted : theme.text}
textColor={leader() || props.inert ? theme.textMuted : theme.text}
focusedTextColor={leader() || props.inert ? theme.textMuted : theme.text}
minHeight={1}
maxHeight={maxHeight()}
onContentChange={() => {
Expand Down
16 changes: 16 additions & 0 deletions packages/opencode/src/cli/cmd/tui/config/keybind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ export const Definitions = {
messages_undo: keybind("<leader>u", "Undo message"),
messages_redo: keybind("<leader>r", "Redo message"),
messages_toggle_conceal: keybind("<leader>h", "Toggle code block concealment in messages"),
messages_focus_toggle: keybind("none", "Toggle keyboard focus on user messages"),
messages_focus_previous: keybind("up,k", "Focus the previous user message"),
messages_focus_next: keybind("down,j", "Focus the next user message"),
messages_focus_actions: keybind("return", "Open actions for the focused message"),
messages_focus_revert: keybind("r", "Revert to the focused message"),
messages_focus_copy: keybind("c", "Copy the focused message"),
messages_focus_fork: keybind("f", "Fork from the focused message"),
messages_focus_exit: keybind("i", "Exit user message focus"),
tool_details: keybind("none", "Toggle tool details visibility"),
display_thinking: keybind("none", "Toggle thinking blocks visibility"),

Expand Down Expand Up @@ -337,6 +345,14 @@ export const CommandMap = {
messages_undo: "session.undo",
messages_redo: "session.redo",
messages_toggle_conceal: "session.toggle.conceal",
messages_focus_toggle: "session.message.focus.toggle",
messages_focus_previous: "session.message.focus.previous",
messages_focus_next: "session.message.focus.next",
messages_focus_actions: "session.message.focus.actions",
messages_focus_revert: "session.message.focus.revert",
messages_focus_copy: "session.message.focus.copy",
messages_focus_fork: "session.message.focus.fork",
messages_focus_exit: "session.message.focus.exit",
tool_details: "session.toggle.actions",
display_thinking: "session.toggle.thinking",
prompt_submit: "prompt.submit",
Expand Down
67 changes: 14 additions & 53 deletions packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useSDK } from "@tui/context/sdk"
import { useRoute } from "@tui/context/route"
import * as Clipboard from "@tui/util/clipboard"
import type { PromptInfo } from "@tui/component/prompt/history"
import { strip } from "@tui/component/prompt/part"
import { MessageActions } from "./message-actions"

export function DialogMessage(props: {
messageID: string
Expand All @@ -26,29 +26,14 @@ export function DialogMessage(props: {
value: "session.revert",
description: "undo messages and file changes",
onSelect: (dialog) => {
const msg = message()
if (!msg) return

void sdk.client.session.revert({
if (!message()) return
MessageActions.revert({
sdk,
sync,
sessionID: props.sessionID,
messageID: msg.id,
messageID: props.messageID,
setPrompt: props.setPrompt,
})

if (props.setPrompt) {
const parts = sync.data.part[msg.id]
const promptInfo = parts.reduce(
(agg, part) => {
if (part.type === "text") {
if (!part.synthetic) agg.input += part.text
}
if (part.type === "file") agg.parts.push(strip(part))
return agg
},
{ input: "", parts: [] as PromptInfo["parts"] },
)
props.setPrompt(promptInfo)
}

dialog.clear()
},
},
Expand All @@ -57,18 +42,8 @@ export function DialogMessage(props: {
value: "message.copy",
description: "message text to clipboard",
onSelect: async (dialog) => {
const msg = message()
if (!msg) return

const parts = sync.data.part[msg.id]
const text = parts.reduce((agg, part) => {
if (part.type === "text" && !part.synthetic) {
agg += part.text
}
return agg
}, "")

await Clipboard.copy(text)
if (!message()) return
await Clipboard.copy(MessageActions.collectText(sync, props.messageID))
dialog.clear()
},
},
Expand All @@ -77,28 +52,14 @@ export function DialogMessage(props: {
value: "session.fork",
description: "create a new session",
onSelect: async (dialog) => {
const result = await sdk.client.session.fork({
if (!message()) return
await MessageActions.fork({
sdk,
sync,
navigate: route.navigate,
sessionID: props.sessionID,
messageID: props.messageID,
})
const msg = message()
const prompt = msg
? sync.data.part[msg.id].reduce(
(agg, part) => {
if (part.type === "text") {
if (!part.synthetic) agg.input += part.text
}
if (part.type === "file") agg.parts.push(part)
return agg
},
{ input: "", parts: [] as PromptInfo["parts"] },
)
: undefined
route.navigate({
sessionID: result.data!.id,
type: "session",
prompt,
})
dialog.clear()
},
},
Expand Down
Loading
Loading