From 755ddbb223ad31d6bbfef78e63a2c5e41969436c Mon Sep 17 00:00:00 2001 From: Ariane Emory <97994360+ariane-emory@users.noreply.github.com> Date: Fri, 19 Dec 2025 22:45:33 -0500 Subject: [PATCH] feat(tui): reinsert forked message text in prompt text input box when forking session (resolves #5495) (#5545) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../opencode/src/cli/cmd/tui/context/route.tsx | 1 + .../routes/session/dialog-fork-from-timeline.tsx | 13 +++++++++++++ .../cmd/tui/routes/session/dialog-message.tsx | 16 ++++++++++++++++ .../src/cli/cmd/tui/routes/session/index.tsx | 7 +++++++ 4 files changed, 37 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/context/route.tsx b/packages/opencode/src/cli/cmd/tui/context/route.tsx index 22333a058..358461921 100644 --- a/packages/opencode/src/cli/cmd/tui/context/route.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/route.tsx @@ -10,6 +10,7 @@ export type HomeRoute = { export type SessionRoute = { type: "session" sessionID: string + initialPrompt?: PromptInfo } export type Route = HomeRoute | SessionRoute diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx index d47d1df3b..62154cce5 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx @@ -6,6 +6,7 @@ import { Locale } from "@/util/locale" import { useSDK } from "@tui/context/sdk" import { useRoute } from "@tui/context/route" import { useDialog } from "../../ui/dialog" +import type { PromptInfo } from "@tui/component/prompt/history" export function DialogForkFromTimeline(props: { sessionID: string; onMove: (messageID: string) => void }) { const sync = useSync() @@ -35,9 +36,21 @@ export function DialogForkFromTimeline(props: { sessionID: string; onMove: (mess sessionID: props.sessionID, messageID: message.id, }) + const parts = sync.data.part[message.id] ?? [] + const initialPrompt = parts.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"] }, + ) route.navigate({ sessionID: forked.data!.id, type: "session", + initialPrompt, }) dialog.clear() }, diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx index b9e6632ac..86317d62a 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx @@ -80,9 +80,25 @@ export function DialogMessage(props: { sessionID: props.sessionID, messageID: props.messageID, }) + const initialPrompt = (() => { + const msg = message() + if (!msg) return undefined + const parts = sync.data.part[msg.id] + return parts.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"] }, + ) + })() route.navigate({ sessionID: result.data!.id, type: "session", + initialPrompt, }) dialog.clear() }, diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 288504a72..4ad4dc0ca 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -168,6 +168,13 @@ export function Session() { const toast = useToast() const sdk = useSDK() + // Handle initial prompt from fork + createEffect(() => { + if (route.initialPrompt && prompt) { + prompt.set(route.initialPrompt) + } + }) + // Auto-navigate to whichever session currently needs permission input createEffect(() => { const currentSession = session()