@workerdeck/ui 1.4.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/build/{SessionPanel-DQp2oStr.mjs → SessionPanel-DrMj8Yv0.mjs} +150 -106
- package/build/SessionPanel-DrMj8Yv0.mjs.map +1 -0
- package/build/{SessionPanel-BSwUJDwg.d.mts → SessionPanel-My98j9RZ.d.mts} +6 -5
- package/build/index.d.mts +32 -31
- package/build/index.mjs +24 -41
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +7 -4
- package/build/workspace.d.mts +3 -3
- package/build/workspace.mjs +3 -3
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/SessionBrowser.tsx +0 -6
- package/src/components/agent/SessionItem.tsx +2 -9
- package/src/components/agent/SessionPanel.tsx +34 -11
- package/src/components/agent/SessionSteps.tsx +18 -32
- package/src/components/agent/SessionWorkspace.tsx +3 -3
- package/src/components/agent/StatusBar.tsx +14 -1
- package/src/components/agent/TaskList.tsx +89 -0
- package/src/components/agent/TasksDialog.tsx +46 -0
- package/src/components/terminal/todos.ts +7 -38
- package/src/index.ts +2 -1
- package/build/SessionPanel-DQp2oStr.mjs.map +0 -1
- package/src/components/agent/held-sends.tsx +0 -73
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArrowRight, Check, ChevronDown, ChevronRight, CircleAlert
|
|
1
|
+
import { ArrowRight, Check, ChevronDown, ChevronRight, CircleAlert } from 'lucide-react'
|
|
2
2
|
import { isAgentRecord, subagentLabel } from '@workerdeck/protocol'
|
|
3
3
|
import type { SessionInfo, SubagentInfo } from '@workerdeck/protocol'
|
|
4
4
|
import { Spinner } from '../ui/Spinner.tsx'
|
|
@@ -8,28 +8,22 @@ export type Step = {
|
|
|
8
8
|
key: string
|
|
9
9
|
label: string
|
|
10
10
|
noun: string
|
|
11
|
-
|
|
12
|
-
state: 'done' | 'running' | 'pending' | 'failed'
|
|
11
|
+
state: 'done' | 'running' | 'failed'
|
|
13
12
|
detail?: string
|
|
14
13
|
title: string
|
|
15
14
|
onSelect: () => void
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
export function sessionSteps(info: SessionInfo, onSelect: (toolUseId: string
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
title: `${subagentLabel(sub)} · ${sub.toolCount} tool${sub.toolCount === 1 ? '' : 's'}`,
|
|
29
|
-
onSelect: () => onSelect(sub.toolUseId, kind),
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
return [...steps.filter((s) => s.kind === 'agent'), ...steps.filter((s) => s.kind === 'task')]
|
|
17
|
+
export function sessionSteps(info: SessionInfo, onSelect: (toolUseId: string) => void): Step[] {
|
|
18
|
+
return (info.subagents ?? []).filter(isAgentRecord).map((sub) => ({
|
|
19
|
+
key: sub.toolUseId,
|
|
20
|
+
label: subagentLabel(sub),
|
|
21
|
+
noun: 'agent',
|
|
22
|
+
state: stepState(sub.status),
|
|
23
|
+
detail: sub.toolCount > 0 ? String(sub.toolCount) : undefined,
|
|
24
|
+
title: `${subagentLabel(sub)} · ${sub.toolCount} tool${sub.toolCount === 1 ? '' : 's'}`,
|
|
25
|
+
onSelect: () => onSelect(sub.toolUseId),
|
|
26
|
+
}))
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
function stepState(status: SubagentInfo['status']): Step['state'] {
|
|
@@ -89,14 +83,12 @@ export function StepToggle({
|
|
|
89
83
|
}
|
|
90
84
|
|
|
91
85
|
export function StepRow({ step, active = false, onSelect }: { step: Step; active?: boolean; onSelect: () => void }) {
|
|
92
|
-
const
|
|
93
|
-
const selected = active && agent
|
|
94
|
-
const body = step.state === 'failed' ? 'text-danger' : agent ? 'text-success' : 'text-fg-4'
|
|
86
|
+
const body = step.state === 'failed' ? 'text-danger' : 'text-success'
|
|
95
87
|
return (
|
|
96
88
|
<button
|
|
97
89
|
type="button"
|
|
98
90
|
title={step.title}
|
|
99
|
-
aria-current={
|
|
91
|
+
aria-current={active || undefined}
|
|
100
92
|
onClick={(e) => {
|
|
101
93
|
e.stopPropagation()
|
|
102
94
|
onSelect()
|
|
@@ -104,22 +96,19 @@ export function StepRow({ step, active = false, onSelect }: { step: Step; active
|
|
|
104
96
|
className={cn(
|
|
105
97
|
'flex w-full items-center gap-1.5 rounded-[4px] py-1 pr-2.5 pl-3.5',
|
|
106
98
|
'text-left text-micro outline-none',
|
|
107
|
-
|
|
99
|
+
active ? 'bg-row-selected' : 'hover:bg-row-active',
|
|
108
100
|
body,
|
|
109
101
|
)}
|
|
110
102
|
>
|
|
111
|
-
<StepIcon state={step.state}
|
|
103
|
+
<StepIcon state={step.state} />
|
|
112
104
|
<span className="min-w-0 flex-1 truncate">{step.label}</span>
|
|
113
105
|
{step.detail ? <span className="shrink-0 tabular-nums text-fg-4">{step.detail}</span> : null}
|
|
114
|
-
|
|
106
|
+
<ArrowRight className="size-3.5 shrink-0 text-fg-4" />
|
|
115
107
|
</button>
|
|
116
108
|
)
|
|
117
109
|
}
|
|
118
110
|
|
|
119
|
-
function StepIcon({ state
|
|
120
|
-
if (kind === 'task' && state !== 'running' && state !== 'failed') {
|
|
121
|
-
return <Dot className="size-[11px] shrink-0" />
|
|
122
|
-
}
|
|
111
|
+
function StepIcon({ state }: { state: Step['state'] }) {
|
|
123
112
|
switch (state) {
|
|
124
113
|
case 'running': {
|
|
125
114
|
return <Spinner className="size-[11px] shrink-0" />
|
|
@@ -127,9 +116,6 @@ function StepIcon({ state, kind }: { state: Step['state']; kind: Step['kind'] })
|
|
|
127
116
|
case 'failed': {
|
|
128
117
|
return <CircleAlert className="size-[11px] shrink-0" />
|
|
129
118
|
}
|
|
130
|
-
case 'pending': {
|
|
131
|
-
return <PauseCircle className="size-[11px] shrink-0" />
|
|
132
|
-
}
|
|
133
119
|
default: {
|
|
134
120
|
return <Check className="size-[11px] shrink-0" />
|
|
135
121
|
}
|
|
@@ -19,13 +19,13 @@ export interface SessionWorkspaceProps {
|
|
|
19
19
|
transcriptVariant?: SessionPanelProps['transcriptVariant']
|
|
20
20
|
transcriptDensity?: SessionPanelProps['transcriptDensity']
|
|
21
21
|
transcriptFont?: SessionPanelProps['transcriptFont']
|
|
22
|
-
midTurnSend?: SessionPanelProps['midTurnSend']
|
|
23
22
|
scrubber?: SessionPanelProps['scrubber']
|
|
24
23
|
bookmarks?: SessionPanelProps['bookmarks']
|
|
25
24
|
onToggleBookmark?: SessionPanelProps['onToggleBookmark']
|
|
26
25
|
stickyPrompt?: SessionPanelProps['stickyPrompt']
|
|
27
26
|
openSubagent?: SessionPanelProps['openSubagent']
|
|
28
27
|
reveal?: SessionPanelProps['reveal']
|
|
28
|
+
subagents?: SessionPanelProps['subagents']
|
|
29
29
|
onSubagentChange?: SessionPanelProps['onSubagentChange']
|
|
30
30
|
statusPlacement?: SessionPanelProps['statusPlacement']
|
|
31
31
|
controlsSurface?: SessionPanelProps['controlsSurface']
|
|
@@ -52,13 +52,13 @@ export function SessionWorkspace({
|
|
|
52
52
|
transcriptVariant,
|
|
53
53
|
transcriptDensity,
|
|
54
54
|
transcriptFont,
|
|
55
|
-
midTurnSend,
|
|
56
55
|
scrubber,
|
|
57
56
|
bookmarks,
|
|
58
57
|
onToggleBookmark,
|
|
59
58
|
stickyPrompt,
|
|
60
59
|
openSubagent,
|
|
61
60
|
reveal,
|
|
61
|
+
subagents,
|
|
62
62
|
onSubagentChange,
|
|
63
63
|
statusPlacement,
|
|
64
64
|
controlsSurface,
|
|
@@ -211,13 +211,13 @@ export function SessionWorkspace({
|
|
|
211
211
|
transcriptVariant={transcriptVariant}
|
|
212
212
|
transcriptDensity={transcriptDensity}
|
|
213
213
|
transcriptFont={transcriptFont}
|
|
214
|
-
midTurnSend={midTurnSend}
|
|
215
214
|
scrubber={scrubber}
|
|
216
215
|
bookmarks={bookmarks}
|
|
217
216
|
onToggleBookmark={onToggleBookmark}
|
|
218
217
|
stickyPrompt={stickyPrompt}
|
|
219
218
|
openSubagent={openSubagent}
|
|
220
219
|
reveal={reveal}
|
|
220
|
+
subagents={subagents}
|
|
221
221
|
onSubagentChange={onSubagentChange}
|
|
222
222
|
controlsSurface={controlsSurface}
|
|
223
223
|
fontSize={fontSize}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState, type ReactNode } from 'react'
|
|
2
2
|
import type { ConnectionState, TranscriptState } from '@workerdeck/react'
|
|
3
|
-
import type { ContextUsage, ProfileEngine, RateLimitInfo } from '@workerdeck/protocol'
|
|
3
|
+
import type { ContextUsage, ProfileEngine, RateLimitInfo, SessionTask } from '@workerdeck/protocol'
|
|
4
|
+
import { taskCountLabel, taskSummary } from '@workerdeck/protocol'
|
|
4
5
|
import { RefreshCw, WifiOff } from 'lucide-react'
|
|
5
6
|
import { Badge } from '../ui/Badge.tsx'
|
|
6
7
|
import { ProgressRing } from '../ui/ProgressRing.tsx'
|
|
@@ -23,6 +24,8 @@ export interface StatusBarProps {
|
|
|
23
24
|
onOpenStatus?: () => void
|
|
24
25
|
onOpenContext?: () => void
|
|
25
26
|
onOpenUsage?: () => void
|
|
27
|
+
tasks?: readonly SessionTask[]
|
|
28
|
+
onOpenTasks?: () => void
|
|
26
29
|
controls?: ReactNode
|
|
27
30
|
actions?: ReactNode
|
|
28
31
|
placement?: 'top' | 'bottom'
|
|
@@ -134,6 +137,8 @@ export function StatusBar({
|
|
|
134
137
|
onOpenStatus,
|
|
135
138
|
onOpenContext,
|
|
136
139
|
onOpenUsage,
|
|
140
|
+
tasks,
|
|
141
|
+
onOpenTasks,
|
|
137
142
|
controls,
|
|
138
143
|
actions,
|
|
139
144
|
placement = 'top',
|
|
@@ -146,6 +151,7 @@ export function StatusBar({
|
|
|
146
151
|
const weekly = windows?.seven_day
|
|
147
152
|
const link: ConnectionState = connection ?? (connected === false ? 'reconnecting' : 'live')
|
|
148
153
|
const terminal = useTranscriptVariant() === 'terminal'
|
|
154
|
+
const taskCount = tasks && tasks.length > 0 ? taskSummary(tasks) : undefined
|
|
149
155
|
return (
|
|
150
156
|
<div
|
|
151
157
|
data-slot="status-bar"
|
|
@@ -185,6 +191,13 @@ export function StatusBar({
|
|
|
185
191
|
</span>
|
|
186
192
|
</Slot>
|
|
187
193
|
) : null}
|
|
194
|
+
{taskCount ? (
|
|
195
|
+
<Slot onClick={onOpenTasks} hint="Tasks">
|
|
196
|
+
<span className="text-label text-fg-3">
|
|
197
|
+
<span className={cn('tabular-nums', taskCount.running > 0 ? 'text-info' : undefined)}>{taskCountLabel(taskCount)}</span> tasks
|
|
198
|
+
</span>
|
|
199
|
+
</Slot>
|
|
200
|
+
) : null}
|
|
188
201
|
{controls ? <span className="self-center">{controls}</span> : null}
|
|
189
202
|
<span className="flex-1" />
|
|
190
203
|
<span className="font-mono text-label text-fg-3">{formatCost(state.totalCostUsd)}</span>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { ArrowRight, Check, CircleAlert, Circle } from 'lucide-react'
|
|
2
|
+
import { taskSummary, visibleTasks } from '@workerdeck/protocol'
|
|
3
|
+
import type { SessionTask } from '@workerdeck/protocol'
|
|
4
|
+
import { Spinner } from '../ui/Spinner.tsx'
|
|
5
|
+
import { cn } from '../../lib/utils.ts'
|
|
6
|
+
|
|
7
|
+
export interface TaskListProps {
|
|
8
|
+
tasks: readonly SessionTask[]
|
|
9
|
+
showCompleted: boolean
|
|
10
|
+
// Absent where the toggle lives outside this list — VS Code puts it in the view's title bar.
|
|
11
|
+
onShowCompletedChange?: (showCompleted: boolean) => void
|
|
12
|
+
onSelectTask?: (task: SessionTask) => void
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function TaskList({ tasks, showCompleted, onShowCompletedChange, onSelectTask, className }: TaskListProps) {
|
|
17
|
+
const summary = taskSummary(tasks)
|
|
18
|
+
const shown = visibleTasks(tasks, showCompleted)
|
|
19
|
+
const hidden = tasks.length - shown.length
|
|
20
|
+
return (
|
|
21
|
+
<div className={cn('flex flex-col', className)}>
|
|
22
|
+
<div className="flex items-baseline justify-between gap-4 pb-1.5">
|
|
23
|
+
<span className="text-label text-fg-3">
|
|
24
|
+
{summary.done} of {summary.total} done
|
|
25
|
+
{summary.failed > 0 ? ` · ${summary.failed} failed` : ''}
|
|
26
|
+
</span>
|
|
27
|
+
{summary.done > 0 && onShowCompletedChange ? (
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
onClick={() => onShowCompletedChange(!showCompleted)}
|
|
31
|
+
className="shrink-0 rounded-[4px] px-1 py-0.5 text-label text-fg-4 outline-none hover:bg-row-hover hover:text-fg-2"
|
|
32
|
+
>
|
|
33
|
+
{showCompleted ? 'Hide completed' : `Show completed (${summary.done})`}
|
|
34
|
+
</button>
|
|
35
|
+
) : null}
|
|
36
|
+
</div>
|
|
37
|
+
{shown.length === 0 ? (
|
|
38
|
+
<p className="py-6 text-center text-body-sm text-fg-4">
|
|
39
|
+
{tasks.length === 0 ? 'No tasks yet — a checklist appears once the agent plans one.' : `${hidden} completed, all hidden.`}
|
|
40
|
+
</p>
|
|
41
|
+
) : (
|
|
42
|
+
<div className="flex flex-col">
|
|
43
|
+
{shown.map((task) => (
|
|
44
|
+
<TaskRow key={task.key} task={task} onSelect={onSelectTask} />
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function TaskRow({ task, onSelect }: { task: SessionTask; onSelect?: (task: SessionTask) => void }) {
|
|
53
|
+
const pressable = onSelect !== undefined && task.toolUseId !== undefined
|
|
54
|
+
const body = task.state === 'failed' ? 'text-danger' : task.state === 'done' ? 'text-fg-4' : 'text-fg-2'
|
|
55
|
+
const row = (
|
|
56
|
+
<>
|
|
57
|
+
<TaskIcon state={task.state} />
|
|
58
|
+
<span className={cn('min-w-0 flex-1 truncate text-left', task.state === 'done' ? 'line-through' : undefined)}>{task.label}</span>
|
|
59
|
+
{task.detail ? <span className="shrink-0 tabular-nums text-fg-4">{task.detail}</span> : null}
|
|
60
|
+
{pressable ? <ArrowRight className="size-3.5 shrink-0 text-fg-4" /> : null}
|
|
61
|
+
</>
|
|
62
|
+
)
|
|
63
|
+
const className = cn('flex w-full items-center gap-1.5 rounded-[4px] px-1.5 py-1 text-body-sm outline-none', body)
|
|
64
|
+
if (!pressable) {
|
|
65
|
+
return <div className={className}>{row}</div>
|
|
66
|
+
}
|
|
67
|
+
return (
|
|
68
|
+
<button type="button" title={task.label} onClick={() => onSelect(task)} className={cn(className, 'hover:bg-row-active')}>
|
|
69
|
+
{row}
|
|
70
|
+
</button>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function TaskIcon({ state }: { state: SessionTask['state'] }) {
|
|
75
|
+
switch (state) {
|
|
76
|
+
case 'running': {
|
|
77
|
+
return <Spinner className="size-[13px] shrink-0" />
|
|
78
|
+
}
|
|
79
|
+
case 'failed': {
|
|
80
|
+
return <CircleAlert className="size-[13px] shrink-0" />
|
|
81
|
+
}
|
|
82
|
+
case 'done': {
|
|
83
|
+
return <Check className="size-[13px] shrink-0" />
|
|
84
|
+
}
|
|
85
|
+
default: {
|
|
86
|
+
return <Circle className="size-[13px] shrink-0 text-fg-4" />
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SessionTask } from '@workerdeck/protocol'
|
|
2
|
+
import { Dialog, DialogBody, DialogContent, DialogHeader } from '../ui/Dialog.tsx'
|
|
3
|
+
import { TaskList } from './TaskList.tsx'
|
|
4
|
+
|
|
5
|
+
export interface TasksDialogProps {
|
|
6
|
+
tasks: readonly SessionTask[]
|
|
7
|
+
showCompleted: boolean
|
|
8
|
+
onShowCompletedChange: (showCompleted: boolean) => void
|
|
9
|
+
onSelectTask?: (task: SessionTask) => void
|
|
10
|
+
open: boolean
|
|
11
|
+
onOpenChange: (open: boolean) => void
|
|
12
|
+
className?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function TasksDialog({
|
|
16
|
+
tasks,
|
|
17
|
+
showCompleted,
|
|
18
|
+
onShowCompletedChange,
|
|
19
|
+
onSelectTask,
|
|
20
|
+
open,
|
|
21
|
+
onOpenChange,
|
|
22
|
+
className,
|
|
23
|
+
}: TasksDialogProps) {
|
|
24
|
+
return (
|
|
25
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
26
|
+
<DialogContent className={className}>
|
|
27
|
+
<DialogHeader title="Tasks" description="The agent's checklist and the tasks it has spawned" />
|
|
28
|
+
<DialogBody>
|
|
29
|
+
<TaskList
|
|
30
|
+
tasks={tasks}
|
|
31
|
+
showCompleted={showCompleted}
|
|
32
|
+
onShowCompletedChange={onShowCompletedChange}
|
|
33
|
+
onSelectTask={
|
|
34
|
+
onSelectTask
|
|
35
|
+
? (task) => {
|
|
36
|
+
onOpenChange(false)
|
|
37
|
+
onSelectTask(task)
|
|
38
|
+
}
|
|
39
|
+
: undefined
|
|
40
|
+
}
|
|
41
|
+
/>
|
|
42
|
+
</DialogBody>
|
|
43
|
+
</DialogContent>
|
|
44
|
+
</Dialog>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ChecklistItem, ChecklistStatus } from '@workerdeck/protocol'
|
|
2
|
+
import { parseTodoWriteInput } from '@workerdeck/protocol'
|
|
2
3
|
|
|
3
|
-
export type
|
|
4
|
+
export type TodoStatus = ChecklistStatus
|
|
5
|
+
|
|
6
|
+
export type TodoEntry = ChecklistItem
|
|
4
7
|
|
|
5
8
|
export type TodoPreview = { summary: string; shown: TodoEntry[]; more?: string }
|
|
6
9
|
|
|
@@ -8,46 +11,12 @@ const PREVIEW_TODOS = 8
|
|
|
8
11
|
|
|
9
12
|
const GLYPH: Record<TodoStatus, string> = { pending: '☐', in_progress: '◐', completed: '☒' }
|
|
10
13
|
|
|
11
|
-
function todoEntry(value: unknown): TodoEntry | undefined {
|
|
12
|
-
if (typeof value !== 'object' || value === null) {
|
|
13
|
-
return undefined
|
|
14
|
-
}
|
|
15
|
-
const raw = value as { content?: unknown; status?: unknown; activeForm?: unknown }
|
|
16
|
-
const status = raw.status
|
|
17
|
-
if (status !== 'pending' && status !== 'in_progress' && status !== 'completed') {
|
|
18
|
-
return undefined
|
|
19
|
-
}
|
|
20
|
-
const content = typeof raw.content === 'string' ? raw.content.trim() : ''
|
|
21
|
-
if (content === '') {
|
|
22
|
-
return undefined
|
|
23
|
-
}
|
|
24
|
-
const active = typeof raw.activeForm === 'string' ? raw.activeForm.trim() : ''
|
|
25
|
-
return { status, text: status === 'in_progress' && active !== '' ? active : content }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Whole-or-nothing: a malformed entry (streaming, partial input) falls back to the generic preview rather than a half-drawn checklist.
|
|
29
|
-
export function parseTodos(input: unknown): TodoEntry[] | undefined {
|
|
30
|
-
const todos = (input as { todos?: unknown } | null)?.todos
|
|
31
|
-
if (!Array.isArray(todos) || todos.length === 0) {
|
|
32
|
-
return undefined
|
|
33
|
-
}
|
|
34
|
-
const out: TodoEntry[] = []
|
|
35
|
-
for (const value of todos) {
|
|
36
|
-
const entry = todoEntry(value)
|
|
37
|
-
if (entry === undefined) {
|
|
38
|
-
return undefined
|
|
39
|
-
}
|
|
40
|
-
out.push(entry)
|
|
41
|
-
}
|
|
42
|
-
return out
|
|
43
|
-
}
|
|
44
|
-
|
|
45
14
|
export function todoPreview(name: string, input: unknown): TodoPreview | undefined {
|
|
46
15
|
if (name !== 'TodoWrite') {
|
|
47
16
|
return undefined
|
|
48
17
|
}
|
|
49
|
-
const todos =
|
|
50
|
-
if (todos === undefined) {
|
|
18
|
+
const todos = parseTodoWriteInput(input)
|
|
19
|
+
if (todos === undefined || todos.length === 0) {
|
|
51
20
|
return undefined
|
|
52
21
|
}
|
|
53
22
|
const done = todos.reduce((n, todo) => n + (todo.status === 'completed' ? 1 : 0), 0)
|
package/src/index.ts
CHANGED
|
@@ -75,7 +75,6 @@ export { Reasoning, type ReasoningProps } from './components/agent/Reasoning.tsx
|
|
|
75
75
|
export { Loader } from './components/agent/Loader.tsx'
|
|
76
76
|
export { ToolCallCard, type ToolCallCardProps, type ToolCallItem } from './components/agent/ToolCallCard.tsx'
|
|
77
77
|
export { ToolTitleProvider, useToolTitle } from './components/agent/tool-titles.tsx'
|
|
78
|
-
export { HeldSendsBar, useHeldSends, type HeldSend, type HeldSends } from './components/agent/held-sends.tsx'
|
|
79
78
|
export { FileCard, type FileCardProps, type FileDeliveredItem } from './components/agent/FileCard.tsx'
|
|
80
79
|
export { PermissionPrompt, type PermissionPromptProps } from './components/agent/PermissionPrompt.tsx'
|
|
81
80
|
export {
|
|
@@ -99,6 +98,8 @@ export {
|
|
|
99
98
|
export { StatusBar, type StatusBarProps } from './components/agent/StatusBar.tsx'
|
|
100
99
|
export { ContextDialog, type ContextDialogProps } from './components/agent/ContextDialog.tsx'
|
|
101
100
|
export { UsageDialog, type UsageDialogProps } from './components/agent/UsageDialog.tsx'
|
|
101
|
+
export { TaskList, type TaskListProps } from './components/agent/TaskList.tsx'
|
|
102
|
+
export { TasksDialog, type TasksDialogProps } from './components/agent/TasksDialog.tsx'
|
|
102
103
|
export { UsageMeters, useMinuteClock } from './components/agent/UsageMeters.tsx'
|
|
103
104
|
export { SessionInfoDialog, type SessionInfoDialogProps } from './components/agent/SessionInfoDialog.tsx'
|
|
104
105
|
export { McpDialog, type McpDialogProps } from './components/agent/McpDialog.tsx'
|