From fe374aea46abf995a596c8a3a96ed7da601571c9 Mon Sep 17 00:00:00 2001 From: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Date: Tue, 12 May 2026 07:59:38 +0800 Subject: [PATCH] feat(app): persist todo dock collapsed state (#26953) --- packages/app/src/context/layout.tsx | 13 ++++++++++++ .../composer/session-composer-region.tsx | 5 +++++ .../session/composer/session-todo-dock.tsx | 21 +++++++++---------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index cacc875c54..0d37dd26af 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -43,6 +43,7 @@ type SessionView = { reviewOpen?: string[] pendingMessage?: string pendingMessageAt?: number + todoCollapsed?: boolean } type TabHandoff = { @@ -759,6 +760,18 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setScroll(tab: string, pos: SessionScroll) { scroll.setScroll(key(), tab, pos) }, + todoCollapsed: { + get: () => s().todoCollapsed ?? false, + set(collapsed: boolean) { + const session = key() + const current = store.sessionView[session] + if (!current) { + setStore("sessionView", session, { scroll: {}, todoCollapsed: collapsed }) + } else { + setStore("sessionView", session, "todoCollapsed", collapsed) + } + }, + }, terminal: { opened: terminalOpened, open() { 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 60447566ed..e6bfd05ec4 100644 --- a/packages/app/src/pages/session/composer/session-composer-region.tsx +++ b/packages/app/src/pages/session/composer/session-composer-region.tsx @@ -2,6 +2,7 @@ import { Show, createEffect, createMemo, onCleanup } from "solid-js" import { createStore } from "solid-js/store" import { useNavigate } from "@solidjs/router" import { useSpring } from "@opencode-ai/ui/motion-spring" +import { useLayout } from "@/context/layout" import { PromptInput } from "@/components/prompt-input" import { useLanguage } from "@/context/language" import { usePrompt } from "@/context/prompt" @@ -46,10 +47,12 @@ export function SessionComposerRegion(props: { setPromptDockRef: (el: HTMLDivElement) => void }) { const navigate = useNavigate() + const layout = useLayout() const prompt = usePrompt() const language = useLanguage() const route = useSessionKey() const sync = useSync() + const view = layout.view(route.sessionKey) const handoffPrompt = createMemo(() => getSessionHandoff(route.sessionKey())?.prompt) const info = createMemo(() => (route.params.id ? sync.session.get(route.params.id) : undefined)) @@ -207,6 +210,8 @@ export function SessionComposerRegion(props: { view.todoCollapsed.set(!view.todoCollapsed.get())} collapseLabel={language.t("session.todo.collapse")} expandLabel={language.t("session.todo.expand")} dockProgress={value()} 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 fa8c177343..fccbeec177 100644 --- a/packages/app/src/pages/session/composer/session-todo-dock.tsx +++ b/packages/app/src/pages/session/composer/session-todo-dock.tsx @@ -42,18 +42,17 @@ function dot(status: Todo["status"]) { export function SessionTodoDock(props: { sessionID?: string todos: Todo[] + collapsed: boolean + onToggle: () => void collapseLabel: string expandLabel: string dockProgress: number }) { const language = useLanguage() const [store, setStore] = createStore({ - collapsed: false, height: 320, }) - const toggle = () => setStore("collapsed", (value) => !value) - const total = createMemo(() => props.todos.length) const done = createMemo(() => props.todos.filter((todo) => todo.status === "completed").length) const label = createMemo(() => language.t("session.todo.progress", { done: done(), total: total() })) @@ -72,7 +71,7 @@ export function SessionTodoDock(props: { ) const preview = createMemo(() => active()?.content ?? "") - const collapse = useSpring(() => (store.collapsed ? 1 : 0), { visualDuration: 0.3, bounce: 0 }) + const collapse = useSpring(() => (props.collapsed ? 1 : 0), { visualDuration: 0.3, bounce: 0 }) const dock = createMemo(() => Math.max(0, Math.min(1, props.dockProgress))) const shut = createMemo(() => 1 - dock()) const value = createMemo(() => Math.max(0, Math.min(1, collapse()))) @@ -107,11 +106,11 @@ export function SessionTodoDock(props: { class="pl-3 pr-2 py-2 flex items-center gap-2 overflow-visible" role="button" tabIndex={0} - onClick={toggle} + onClick={props.onToggle} onKeyDown={(event) => { if (event.key !== "Enter" && event.key !== " ") return event.preventDefault() - toggle() + props.onToggle() }} > { event.stopPropagation() - toggle() + props.onToggle() }} - aria-label={store.collapsed ? props.expandLabel : props.collapseLabel} + aria-label={props.collapsed ? props.expandLabel : props.collapseLabel} />
0.1, }}