diff options
| author | Luke Parker <[email protected]> | 2026-03-13 12:21:50 +1000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-13 12:21:50 +1000 |
| commit | 96b1d8f639991e896bc8d31afe64d6309bf3ccd2 (patch) | |
| tree | d51a398a9e13046b634a5cbe517f5ecbe72db765 /packages/app/src/pages | |
| parent | dcb17c6a678918ce0786640729fcc8cd8adb1746 (diff) | |
| download | opencode-96b1d8f639991e896bc8d31afe64d6309bf3ccd2.tar.gz opencode-96b1d8f639991e896bc8d31afe64d6309bf3ccd2.zip | |
fix(app): stabilize todo dock e2e with composer probe (#17267)
Diffstat (limited to 'packages/app/src/pages')
3 files changed, 79 insertions, 6 deletions
diff --git a/packages/app/src/pages/session/composer/session-composer-region.tsx b/packages/app/src/pages/session/composer/session-composer-region.tsx index 2034fbead..84f77ea4a 100644 --- a/packages/app/src/pages/session/composer/session-composer-region.tsx +++ b/packages/app/src/pages/session/composer/session-composer-region.tsx @@ -44,9 +44,9 @@ export function SessionComposerRegion(props: { }) { const prompt = usePrompt() const language = useLanguage() - const { sessionKey } = useSessionKey() + const route = useSessionKey() - const handoffPrompt = createMemo(() => getSessionHandoff(sessionKey())?.prompt) + const handoffPrompt = createMemo(() => getSessionHandoff(route.sessionKey())?.prompt) const previewPrompt = () => prompt @@ -62,7 +62,7 @@ export function SessionComposerRegion(props: { createEffect(() => { if (!prompt.ready()) return - setSessionHandoff(sessionKey(), { prompt: previewPrompt() }) + setSessionHandoff(route.sessionKey(), { prompt: previewPrompt() }) }) const [store, setStore] = createStore({ @@ -85,7 +85,7 @@ export function SessionComposerRegion(props: { } createEffect(() => { - sessionKey() + route.sessionKey() const ready = props.ready const delay = 140 @@ -194,6 +194,7 @@ export function SessionComposerRegion(props: { > <div ref={(el) => setStore("body", el)}> <SessionTodoDock + sessionID={route.params.id} todos={props.state.todos()} title={language.t("session.todo.title")} collapseLabel={language.t("session.todo.collapse")} diff --git a/packages/app/src/pages/session/composer/session-composer-state.ts b/packages/app/src/pages/session/composer/session-composer-state.ts index 525766dcf..0884f4cc6 100644 --- a/packages/app/src/pages/session/composer/session-composer-state.ts +++ b/packages/app/src/pages/session/composer/session-composer-state.ts @@ -1,4 +1,4 @@ -import { createEffect, createMemo, on, onCleanup } from "solid-js" +import { createEffect, createMemo, on, onCleanup, onMount } from "solid-js" import { createStore } from "solid-js/store" import type { PermissionRequest, QuestionRequest, Todo } from "@opencode-ai/sdk/v2" import { useParams } from "@solidjs/router" @@ -8,6 +8,7 @@ import { useLanguage } from "@/context/language" import { usePermission } from "@/context/permission" import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" +import { composerDriver, composerEnabled, composerEvent } from "@/testing/session-composer" import { sessionPermissionRequest, sessionQuestionRequest } from "./session-request-tree" export const todoState = (input: { @@ -47,7 +48,50 @@ export function createSessionComposerState(options?: { closeMs?: number | (() => return !!permissionRequest() || !!questionRequest() }) + const [test, setTest] = createStore({ + on: false, + live: undefined as boolean | undefined, + todos: undefined as Todo[] | undefined, + }) + + const pull = () => { + const id = params.id + if (!id) { + setTest({ on: false, live: undefined, todos: undefined }) + return + } + + const next = composerDriver(id) + if (!next) { + setTest({ on: false, live: undefined, todos: undefined }) + return + } + + setTest({ + on: true, + live: next.live, + todos: next.todos?.map((todo) => ({ ...todo })), + }) + } + + onMount(() => { + if (!composerEnabled()) return + + pull() + createEffect(on(() => params.id, pull, { defer: true })) + + const onEvent = (event: Event) => { + const detail = (event as CustomEvent<{ sessionID?: string }>).detail + if (detail?.sessionID !== params.id) return + pull() + } + + window.addEventListener(composerEvent, onEvent) + onCleanup(() => window.removeEventListener(composerEvent, onEvent)) + }) + const todos = createMemo((): Todo[] => { + if (test.on && test.todos !== undefined) return test.todos const id = params.id if (!id) return [] return globalSync.data.session_todo[id] ?? [] @@ -64,7 +108,10 @@ export function createSessionComposerState(options?: { closeMs?: number | (() => }) const busy = createMemo(() => status().type !== "idle") - const live = createMemo(() => busy() || blocked()) + const live = createMemo(() => { + if (test.on && test.live !== undefined) return test.live + return busy() || blocked() + }) const [store, setStore] = createStore({ responding: undefined as string | undefined, @@ -116,6 +163,10 @@ export function createSessionComposerState(options?: { closeMs?: number | (() => // Keep stale turn todos from reopening if the model never clears them. const clear = () => { + if (test.on && test.todos !== undefined) { + setTest("todos", []) + return + } const id = params.id if (!id) return globalSync.todo.set(id, []) diff --git a/packages/app/src/pages/session/composer/session-todo-dock.tsx b/packages/app/src/pages/session/composer/session-todo-dock.tsx index c7907bb54..5500de97a 100644 --- a/packages/app/src/pages/session/composer/session-todo-dock.tsx +++ b/packages/app/src/pages/session/composer/session-todo-dock.tsx @@ -8,6 +8,7 @@ import { TextReveal } from "@opencode-ai/ui/text-reveal" import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough" import { Index, createEffect, createMemo, on, onCleanup } from "solid-js" import { createStore } from "solid-js/store" +import { composerEnabled, composerProbe } from "@/testing/session-composer" function dot(status: Todo["status"]) { if (status !== "in_progress") return undefined @@ -35,6 +36,7 @@ function dot(status: Todo["status"]) { } export function SessionTodoDock(props: { + sessionID?: string todos: Todo[] title: string collapseLabel: string @@ -69,6 +71,8 @@ export function SessionTodoDock(props: { const off = createMemo(() => hide() > 0.98) const turn = createMemo(() => Math.max(0, Math.min(1, value()))) const full = createMemo(() => Math.max(78, store.height)) + const e2e = composerEnabled() + const probe = composerProbe(props.sessionID) let contentRef: HTMLDivElement | undefined createEffect(() => { @@ -83,6 +87,23 @@ export function SessionTodoDock(props: { onCleanup(() => observer.disconnect()) }) + createEffect(() => { + if (!e2e) return + + probe.set({ + mounted: true, + collapsed: store.collapsed, + hidden: store.collapsed || off(), + count: props.todos.length, + states: props.todos.map((todo) => todo.status), + }) + }) + + onCleanup(() => { + if (!e2e) return + probe.drop() + }) + return ( <DockTray data-component="session-todo-dock" |
