Download packages/app/src/components/dialog-fork.tsx from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 3.41 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/components/dialog-fork.tsx
- Command line
-
hf download hf://SaylorTwift/opencode/packages/app/src/components/dialog-fork.tsx
-
curl -L -o dialog-fork.tsx https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/components/dialog-fork.tsx
3.41 kB
| 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 "@/utils/toast" | |
| import { extractPromptFromParts } from "@/utils/prompt" | |
| import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" | |
| import { base64Encode } from "@opencode-ai/core/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"), | |
| }) | |
| const dir = base64Encode(sdk().directory) | |
| sdk() | |
| .api.session.fork({ sessionID, messageID: item.id }) | |
| .then((forked) => { | |
| dialog.close() | |
| prompt.set(restored, undefined, { dir, id: forked.id }) | |
| navigate(`/${dir}/session/${forked.id}`) | |
| }) | |
| .catch((err: unknown) => { | |
| const message = err instanceof Error ? err.message : String(err) | |
| showToast({ title: language.t("common.requestFailed"), description: message }) | |
| }) | |
| } | |
| return ( | |
| <Dialog title={language.t("command.session.fork")}> | |
| <List | |
| class="flex-1 px-3 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0" | |
| search={{ placeholder: language.t("common.search.placeholder"), autofocus: true }} | |
| emptyMessage={language.t("dialog.fork.empty")} | |
| key={(x) => x.id} | |
| items={messages} | |
| filterKeys={["text"]} | |
| onSelect={handleSelect} | |
| > | |
| {(item) => ( | |
| <div class="w-full flex items-center gap-2"> | |
| <span class="truncate flex-1 min-w-0 text-left font-normal">{item.text}</span> | |
| <span class="text-text-weak shrink-0 font-normal">{item.time}</span> | |
| </div> | |
| )} | |
| </List> | |
| </Dialog> | |
| ) | |
| } | |