import type { Todo } from "@opencode-ai/sdk/v2" import { AnimatedNumber } from "@opencode-ai/ui/animated-number" import { Checkbox } from "@opencode-ai/ui/checkbox" import { DockTray } from "@opencode-ai/ui/dock-surface" import { IconButton } from "@opencode-ai/ui/icon-button" import { useSpring } from "@opencode-ai/ui/motion-spring" import { TextReveal } from "@opencode-ai/ui/text-reveal" import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough" import { createResizeObserver } from "@solid-primitives/resize-observer" import { Index, createEffect, createMemo } from "solid-js" import { createStore } from "solid-js/store" import { useLanguage } from "@/context/language" const doneToken = "\u0000done\u0000" const totalToken = "\u0000total\u0000" function dot(status: Todo["status"]) { if (status !== "in_progress") return undefined return ( ) } export function SessionTodoDock(props: { sessionID?: string todos: Todo[] 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() })) const progress = createMemo(() => language .t("session.todo.progress", { done: doneToken, total: totalToken }) .split(/(\u0000done\u0000|\u0000total\u0000)/), ) const active = createMemo( () => props.todos.find((todo) => todo.status === "in_progress") ?? props.todos.find((todo) => todo.status === "pending") ?? props.todos.filter((todo) => todo.status === "completed").at(-1) ?? props.todos[0], ) const preview = createMemo(() => active()?.content ?? "") const collapse = useSpring(() => (store.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()))) const hide = createMemo(() => Math.max(value(), shut())) const off = createMemo(() => hide() > 0.98) const turn = createMemo(() => Math.max(0, Math.min(1, value()))) const full = createMemo(() => Math.max(78, store.height)) let contentRef: HTMLDivElement | undefined createEffect(() => { const el = contentRef if (!el) return const update = () => { setStore("height", el.getBoundingClientRect().height) } update() createResizeObserver(el, update) }) return (
{ if (event.key !== "Enter" && event.key !== " ") return event.preventDefault() toggle() }} > {(item) => item() === doneToken ? ( ) : item() === totalToken ? ( ) : ( {item()} ) }
{ event.preventDefault() event.stopPropagation() }} onClick={(event) => { event.stopPropagation() toggle() }} aria-label={store.collapsed ? props.expandLabel : props.collapseLabel} />
0.1, }} style={{ visibility: off() ? "hidden" : "visible", opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`, }} >
) } function TodoList(props: { todos: Todo[] }) { const [store, setStore] = createStore({ stuck: false, }) return (
{ setStore("stuck", e.currentTarget.scrollTop > 0) }} > {(todo) => ( )}
) }