import { Component, createMemo } from "solid-js" import { useNavigate, useParams } from "@solidjs/router" import { useSync } from "@/context/sync" import { useSDK } from "@/context/sdk" import { usePrompt } from "@/context/prompt" import { useDialog } from "@opencode-ai/ui/context/dialog" import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { showToast } from "@opencode-ai/ui/toast" import { extractPromptFromParts } from "@/utils/prompt" import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" import { base64Encode } from "@opencode-ai/util/encode" import { useLanguage } from "@/context/language" interface ForkableMessage { id: string text: string time: string } function formatTime(date: Date): string { return date.toLocaleTimeString(undefined, { timeStyle: "short" }) } export const DialogFork: Component = () => { const params = useParams() const navigate = useNavigate() const sync = useSync() const sdk = useSDK() const prompt = usePrompt() const dialog = useDialog() const language = useLanguage() const messages = createMemo((): ForkableMessage[] => { const sessionID = params.id if (!sessionID) return [] const msgs = sync.data.message[sessionID] ?? [] const result: ForkableMessage[] = [] for (const message of msgs) { if (message.role !== "user") continue const parts = sync.data.part[message.id] ?? [] const textPart = parts.find((x): x is SDKTextPart => x.type === "text" && !x.synthetic && !x.ignored) if (!textPart) continue result.push({ id: message.id, text: textPart.text.replace(/\n/g, " ").slice(0, 200), time: formatTime(new Date(message.time.created)), }) } return result.reverse() }) const handleSelect = (item: ForkableMessage | undefined) => { if (!item) return const sessionID = params.id if (!sessionID) return const parts = sync.data.part[item.id] ?? [] const restored = extractPromptFromParts(parts, { directory: sdk.directory, attachmentName: language.t("common.attachment"), }) sdk.client.session .fork({ sessionID, messageID: item.id }) .then((forked) => { if (!forked.data) { showToast({ title: language.t("common.requestFailed") }) return } dialog.close() navigate(`/${base64Encode(sdk.directory)}/session/${forked.data.id}`) requestAnimationFrame(() => { prompt.set(restored) }) }) .catch((err: unknown) => { const message = err instanceof Error ? err.message : String(err) showToast({ title: language.t("common.requestFailed"), description: message }) }) } return ( x.id} items={messages} filterKeys={["text"]} onSelect={handleSelect} > {(item) => (
{item.text} {item.time}
)}
) }