diff --git a/packages/app/src/components/session-task-indicator.tsx b/packages/app/src/components/session-task-indicator.tsx new file mode 100644 index 00000000000..f4f11ee92bc --- /dev/null +++ b/packages/app/src/components/session-task-indicator.tsx @@ -0,0 +1,49 @@ +import { createMemo, Show } from "solid-js" +import { Button } from "@opencode-ai/ui/button" +import { useSync } from "@/context/sync" +import { useLayout } from "@/context/layout" +import { useParams } from "@solidjs/router" + +export function SessionTaskIndicator() { + const sync = useSync() + const layout = useLayout() + const params = useParams() + + const taskStats = createMemo(() => { + const sessionID = params.id + if (!sessionID) return { completed: 0, total: 0, hasInProgress: false, hasIncomplete: false } + const todos = sync.data.todo[sessionID] ?? [] + const completed = todos.filter((t) => t.status === "completed").length + const hasInProgress = todos.some((t) => t.status === "in_progress") + const hasIncomplete = todos.some((t) => t.status !== "completed") + return { completed, total: todos.length, hasInProgress, hasIncomplete } + }) + + const handleClick = () => { + const sessionKey = `${params.dir}${params.id ? "/" + params.id : ""}` + const tabs = layout.tabs(sessionKey) + if (tabs.active() === "tasks") { + layout.review.close() + return + } + if (!layout.review.opened()) layout.review.open() + tabs.setActive("tasks") + } + + return ( + + + + ) +} diff --git a/packages/app/src/components/session-task-panel.tsx b/packages/app/src/components/session-task-panel.tsx new file mode 100644 index 00000000000..47158a955d5 --- /dev/null +++ b/packages/app/src/components/session-task-panel.tsx @@ -0,0 +1,90 @@ +import { createMemo, For, Show } from "solid-js" +import { useSync } from "@/context/sync" +import { useParams } from "@solidjs/router" +import { Icon } from "@opencode-ai/ui/icon" +import type { Todo } from "@opencode-ai/sdk/v2/client" + +function TaskItem(props: { todo: Todo }) { + return ( +
+
+ } + > + + + } + > + + + } + > + + +
+
+ + {props.todo.content} + +
+
+ ) +} + +export function SessionTaskPanel() { + const sync = useSync() + const params = useParams() + + const todos = createMemo(() => { + const sessionID = params.id + if (!sessionID) return [] + return sync.data.todo[sessionID] ?? [] + }) + + return ( +
+
+ +
+ + No tasks yet +
+
+ 0}> +
+ {(todo) => } +
+
+
+
+ ) +} diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 4958ad2c353..ee8c23a0d38 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -19,6 +19,7 @@ import { TextField } from "@opencode-ai/ui/text-field" import { DialogSelectServer } from "@/components/dialog-select-server" import { SessionLspIndicator } from "@/components/session-lsp-indicator" import { SessionMcpIndicator } from "@/components/session-mcp-indicator" +import { SessionTaskIndicator } from "@/components/session-task-indicator" import type { Session } from "@opencode-ai/sdk/v2/client" import { same } from "@/utils/same" @@ -44,6 +45,15 @@ export function SessionHeader() { const shareEnabled = createMemo(() => sync.data.config.share !== "disabled") const worktrees = createMemo(() => layout.projects.list().map((p) => p.worktree), [], { equals: same }) + const hasIncompleteTasks = createMemo(() => { + const sessionID = params.id + if (!sessionID) return false + const todos = sync.data.todo[sessionID] ?? [] + return todos.some((t) => t.status !== "completed") + }) + + const showSidebarToggle = createMemo(() => currentSession()?.summary?.files || hasIncompleteTasks()) + function navigateToProject(directory: string) { navigate(`/${base64Encode(directory)}`) } @@ -163,12 +173,13 @@ export function SessionHeader() { +
- +
+ + +
+ +
Tasks
+ {(() => { + const todos = sync.data.todo[params.id ?? ""] ?? [] + const completed = todos.filter((t) => t.status === "completed").length + return ( +
+ {completed}/{todos.length} +
+ ) + })()} +
+
+
{(tab) => } @@ -1061,6 +1089,11 @@ export default function Page() { + + + + + {(tab) => { let scroll: HTMLDivElement | undefined