import { For, Show, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js" import type { PermissionRequest, QuestionRequest, Todo } from "@opencode-ai/sdk/v2" import { useParams } from "@solidjs/router" import { Button } from "@opencode-ai/ui/button" import { DockPrompt } from "@opencode-ai/ui/dock-prompt" import { Icon } from "@opencode-ai/ui/icon" import { showToast } from "@opencode-ai/ui/toast" import { PromptInput } from "@/components/prompt-input" import { QuestionDock } from "@/components/question-dock" import { SessionTodoDock } from "@/components/session-todo-dock" import { useGlobalSync } from "@/context/global-sync" import { useLanguage } from "@/context/language" import { usePrompt } from "@/context/prompt" import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" import { getSessionHandoff, setSessionHandoff } from "@/pages/session/handoff" export function SessionPromptDock(props: { centered: boolean inputRef: (el: HTMLDivElement) => void newSessionWorktree: string onNewSessionWorktreeReset: () => void onSubmit: () => void setPromptDockRef: (el: HTMLDivElement) => void }) { const params = useParams() const sdk = useSDK() const sync = useSync() const globalSync = useGlobalSync() const prompt = usePrompt() const language = useLanguage() const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const handoffPrompt = createMemo(() => getSessionHandoff(sessionKey())?.prompt) const todos = createMemo((): Todo[] => { const id = params.id if (!id) return [] return globalSync.data.session_todo[id] ?? [] }) const questionRequest = createMemo((): QuestionRequest | undefined => { const sessionID = params.id if (!sessionID) return return sync.data.question[sessionID]?.[0] }) const permissionRequest = createMemo((): PermissionRequest | undefined => { const sessionID = params.id if (!sessionID) return return sync.data.permission[sessionID]?.[0] }) const blocked = createMemo(() => !!permissionRequest() || !!questionRequest()) const previewPrompt = () => prompt .current() .map((part) => { if (part.type === "file") return `[file:${part.path}]` if (part.type === "agent") return `@${part.name}` if (part.type === "image") return `[image:${part.filename}]` return part.content }) .join("") .trim() createEffect(() => { if (!prompt.ready()) return setSessionHandoff(sessionKey(), { prompt: previewPrompt() }) }) const [responding, setResponding] = createSignal() const permissionResponding = () => { const perm = permissionRequest() if (!perm) return false return responding() === perm.id } const decide = (response: "once" | "always" | "reject") => { const perm = permissionRequest() if (!perm) return if (responding() === perm.id) return setResponding(perm.id) sdk.client.permission .respond({ sessionID: perm.sessionID, permissionID: perm.id, response }) .catch((err: unknown) => { const message = err instanceof Error ? err.message : String(err) showToast({ title: language.t("common.requestFailed"), description: message }) }) .finally(() => { setResponding((id) => (id === perm.id ? undefined : id)) }) } const done = createMemo( () => todos().length > 0 && todos().every((todo) => todo.status === "completed" || todo.status === "cancelled"), ) const [dock, setDock] = createSignal(todos().length > 0) const [closing, setClosing] = createSignal(false) const [opening, setOpening] = createSignal(false) let timer: number | undefined let raf: number | undefined const scheduleClose = () => { if (timer) window.clearTimeout(timer) timer = window.setTimeout(() => { setDock(false) setClosing(false) timer = undefined }, 400) } createEffect( on( () => [todos().length, done()] as const, ([count, complete], prev) => { if (raf) cancelAnimationFrame(raf) raf = undefined if (count === 0) { if (timer) window.clearTimeout(timer) timer = undefined setDock(false) setClosing(false) setOpening(false) return } if (!complete) { if (timer) window.clearTimeout(timer) timer = undefined const wasHidden = !dock() || closing() setDock(true) setClosing(false) if (wasHidden) { setOpening(true) raf = requestAnimationFrame(() => { setOpening(false) raf = undefined }) return } setOpening(false) return } if (prev && prev[1]) { if (closing() && !timer) scheduleClose() return } setDock(true) setOpening(false) setClosing(true) scheduleClose() }, ), ) onCleanup(() => { if (!timer) return window.clearTimeout(timer) }) onCleanup(() => { if (!raf) return cancelAnimationFrame(raf) }) return (
{(req) => { return (
) }}
{(perm) => { const toolDescription = () => { const key = `settings.permissions.tool.${perm.permission}.description` const value = language.t(key as Parameters[0]) if (value === key) return "" return value } return (
{language.t("notification.permission.title")}
} footer={ <>
} >
0}>
) }}
{handoffPrompt() || language.t("prompt.loading")}
} >
) }