chore: cleanup (#14113)

This commit is contained in:
Adam
2026-02-18 08:26:15 -06:00
committed by GitHub
parent e4b548fa76
commit 00c238777a
9 changed files with 944 additions and 899 deletions

View File

@@ -1,35 +1,105 @@
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
import type { QuestionRequest, Todo } from "@opencode-ai/sdk/v2"
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
questionRequest: () => QuestionRequest | undefined
permissionRequest: () => { patterns: string[]; permission: string } | undefined
blocked: boolean
todos: Todo[]
promptReady: boolean
handoffPrompt?: string
t: (key: string, vars?: Record<string, string | number | boolean>) => string
responding: boolean
onDecide: (response: "once" | "always" | "reject") => void
inputRef: (el: HTMLDivElement) => void
newSessionWorktree: string
onNewSessionWorktreeReset: () => void
onSubmit: () => void
setPromptDockRef: (el: HTMLDivElement) => void
}) {
const done = createMemo(
() =>
props.todos.length > 0 && props.todos.every((todo) => todo.status === "completed" || todo.status === "cancelled"),
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(false)
createEffect(
on(
() => permissionRequest()?.id,
() => setResponding(false),
{ defer: true },
),
)
const [dock, setDock] = createSignal(props.todos.length > 0)
const decide = (response: "once" | "always" | "reject") => {
const perm = permissionRequest()
if (!perm) return
if (responding()) return
setResponding(true)
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(false))
}
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
@@ -46,7 +116,7 @@ export function SessionPromptDock(props: {
createEffect(
on(
() => [props.todos.length, done()] as const,
() => [todos().length, done()] as const,
([count, complete], prev) => {
if (raf) cancelAnimationFrame(raf)
raf = undefined
@@ -113,7 +183,7 @@ export function SessionPromptDock(props: {
"md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered,
}}
>
<Show when={props.questionRequest()} keyed>
<Show when={questionRequest()} keyed>
{(req) => {
return (
<div>
@@ -123,11 +193,11 @@ export function SessionPromptDock(props: {
}}
</Show>
<Show when={props.permissionRequest()} keyed>
<Show when={permissionRequest()} keyed>
{(perm) => {
const toolDescription = () => {
const key = `settings.permissions.tool.${perm.permission}.description`
const value = props.t(key)
const value = language.t(key as Parameters<typeof language.t>[0])
if (value === key) return ""
return value
}
@@ -141,36 +211,26 @@ export function SessionPromptDock(props: {
<span data-slot="permission-icon">
<Icon name="warning" size="normal" />
</span>
<div data-slot="permission-header-title">{props.t("notification.permission.title")}</div>
<div data-slot="permission-header-title">{language.t("notification.permission.title")}</div>
</div>
}
footer={
<>
<div />
<div data-slot="permission-footer-actions">
<Button
variant="ghost"
size="normal"
onClick={() => props.onDecide("reject")}
disabled={props.responding}
>
{props.t("ui.permission.deny")}
<Button variant="ghost" size="normal" onClick={() => decide("reject")} disabled={responding()}>
{language.t("ui.permission.deny")}
</Button>
<Button
variant="secondary"
size="normal"
onClick={() => props.onDecide("always")}
disabled={props.responding}
onClick={() => decide("always")}
disabled={responding()}
>
{props.t("ui.permission.allowAlways")}
{language.t("ui.permission.allowAlways")}
</Button>
<Button
variant="primary"
size="normal"
onClick={() => props.onDecide("once")}
disabled={props.responding}
>
{props.t("ui.permission.allowOnce")}
<Button variant="primary" size="normal" onClick={() => decide("once")} disabled={responding()}>
{language.t("ui.permission.allowOnce")}
</Button>
</div>
</>
@@ -199,12 +259,12 @@ export function SessionPromptDock(props: {
}}
</Show>
<Show when={!props.blocked}>
<Show when={!blocked()}>
<Show
when={props.promptReady}
when={prompt.ready()}
fallback={
<div class="w-full min-h-32 md:min-h-40 rounded-md border border-border-weak-base bg-background-base/50 px-4 py-3 text-text-weak whitespace-pre-wrap pointer-events-none">
{props.handoffPrompt || props.t("prompt.loading")}
{handoffPrompt() || language.t("prompt.loading")}
</div>
}
>
@@ -219,10 +279,10 @@ export function SessionPromptDock(props: {
}}
>
<SessionTodoDock
todos={props.todos}
title={props.t("session.todo.title")}
collapseLabel={props.t("session.todo.collapse")}
expandLabel={props.t("session.todo.expand")}
todos={todos()}
title={language.t("session.todo.title")}
collapseLabel={language.t("session.todo.collapse")}
expandLabel={language.t("session.todo.expand")}
/>
</div>
</Show>