@workerdeck/ui 0.22.0 → 1.0.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/build/{SessionPanel-DgwjH4Ve.mjs → SessionPanel-DDgBkHsi.mjs} +2196 -3364
- package/build/SessionPanel-DDgBkHsi.mjs.map +1 -0
- package/build/SessionPanel-E_L_ldQ5.d.mts +207 -0
- package/build/format-B8ZM9LPy.d.mts +16 -0
- package/build/format.d.mts +8 -75
- package/build/format.mjs +2 -2
- package/build/index.d.mts +63 -1138
- package/build/index.mjs +4 -300
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +80 -24
- package/build/{status-BE-zg88x.mjs → status-C0HSLZas.mjs} +14 -85
- package/build/status-C0HSLZas.mjs.map +1 -0
- package/build/workspace.d.mts +9 -124
- package/build/workspace.mjs +13 -143
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/CodeEditor.tsx +31 -103
- package/src/components/agent/Composer.tsx +114 -333
- package/src/components/agent/ContextDialog.tsx +20 -41
- package/src/components/agent/ContextRing.tsx +6 -28
- package/src/components/agent/Conversation.tsx +21 -33
- package/src/components/agent/EditorTabs.tsx +39 -67
- package/src/components/agent/EngineIcon.tsx +29 -72
- package/src/components/agent/EntryIcon.tsx +16 -0
- package/src/components/agent/FileCard.tsx +10 -16
- package/src/components/agent/FileTree.tsx +52 -125
- package/src/components/agent/FileViewer.tsx +30 -60
- package/src/components/agent/HostFilesDialog.tsx +40 -69
- package/src/components/agent/Loader.tsx +3 -24
- package/src/components/agent/McpDialog.tsx +64 -137
- package/src/components/agent/Message.tsx +2 -21
- package/src/components/agent/ModelSelect.tsx +33 -62
- package/src/components/agent/PermissionModeSelect.tsx +21 -79
- package/src/components/agent/PermissionPrompt.tsx +59 -63
- package/src/components/agent/ProjectIcon.tsx +228 -93
- package/src/components/agent/PromptTokenText.tsx +10 -16
- package/src/components/agent/QuestionPrompt.tsx +43 -64
- package/src/components/agent/Reasoning.tsx +13 -16
- package/src/components/agent/Response.tsx +4 -6
- package/src/components/agent/Scrubber.tsx +31 -80
- package/src/components/agent/SessionBrowser.tsx +64 -243
- package/src/components/agent/SessionEmptyState.tsx +10 -40
- package/src/components/agent/SessionInfoDialog.tsx +46 -78
- package/src/components/agent/SessionItem.tsx +53 -320
- package/src/components/agent/SessionList.tsx +23 -39
- package/src/components/agent/SessionPanel.tsx +345 -923
- package/src/components/agent/SessionStatusIcon.tsx +14 -28
- package/src/components/agent/SessionSteps.tsx +34 -187
- package/src/components/agent/SessionWorkspace.tsx +108 -215
- package/src/components/agent/SkillsDialog.tsx +35 -80
- package/src/components/agent/StatusBar.tsx +51 -136
- package/src/components/agent/SubagentStrip.tsx +12 -77
- package/src/components/agent/ToolCallCard.tsx +94 -135
- package/src/components/agent/Transcript.tsx +103 -1288
- package/src/components/agent/TranscriptItemView.tsx +155 -0
- package/src/components/agent/TranscriptRows.tsx +418 -0
- package/src/components/agent/UsageDialog.tsx +7 -29
- package/src/components/agent/UsageMeters.tsx +18 -73
- package/src/components/agent/pulse.tsx +9 -32
- package/src/components/agent/scrubber-marks.ts +33 -103
- package/src/components/agent/status.ts +1 -5
- package/src/components/agent/tool-result-fetch.tsx +3 -25
- package/src/components/agent/tool-result-image.tsx +32 -111
- package/src/components/agent/transcript-rows.ts +41 -118
- package/src/components/agent/transcript-variant.tsx +3 -80
- package/src/components/agent/use-height-epoch.ts +10 -24
- package/src/components/agent/use-path-links.ts +45 -63
- package/src/components/agent/use-subagent-frame.ts +106 -0
- package/src/components/agent/use-transcript-jumps.ts +49 -107
- package/src/components/prompt-area/animated-placeholder.tsx +6 -5
- package/src/components/prompt-area/clipboard-helpers.ts +26 -21
- package/src/components/prompt-area/cursor-helpers.ts +79 -37
- package/src/components/prompt-area/dom-helpers.ts +97 -69
- package/src/components/prompt-area/file-strip.tsx +90 -80
- package/src/components/prompt-area/html-to-markdown.ts +67 -46
- package/src/components/prompt-area/image-strip.tsx +8 -18
- package/src/components/prompt-area/index.ts +2 -15
- package/src/components/prompt-area/prompt-area-engine.ts +100 -115
- package/src/components/prompt-area/prompt-area-list-ops.ts +66 -59
- package/src/components/prompt-area/prompt-area.tsx +31 -38
- package/src/components/prompt-area/remove-button.tsx +3 -9
- package/src/components/prompt-area/segment-helpers.ts +4 -12
- package/src/components/prompt-area/trigger-popover.tsx +15 -27
- package/src/components/prompt-area/trigger-presets.ts +0 -10
- package/src/components/prompt-area/types.ts +3 -8
- package/src/components/prompt-area/use-chip-editing.ts +305 -0
- package/src/components/prompt-area/use-markdown-mode.ts +7 -17
- package/src/components/prompt-area/use-prompt-area-events.ts +80 -86
- package/src/components/prompt-area/use-prompt-area-keydown.ts +598 -0
- package/src/components/prompt-area/use-prompt-area-state.ts +5 -23
- package/src/components/prompt-area/use-prompt-area.ts +170 -896
- package/src/components/prompt-area/use-trigger-search.ts +21 -19
- package/src/components/terminal/PermissionPrompt.tsx +56 -59
- package/src/components/terminal/QuestionPrompt.tsx +28 -79
- package/src/components/terminal/StatusLine.tsx +13 -63
- package/src/components/terminal/TerminalTranscript.tsx +44 -160
- package/src/components/terminal/affordances.tsx +68 -98
- package/src/components/terminal/blocks.ts +20 -156
- package/src/components/terminal/diff.tsx +19 -67
- package/src/components/terminal/height.ts +163 -313
- package/src/components/terminal/image-box.ts +0 -44
- package/src/components/terminal/items.tsx +150 -305
- package/src/components/terminal/markdown.tsx +36 -95
- package/src/components/terminal/press.tsx +26 -53
- package/src/components/terminal/prompt.tsx +40 -170
- package/src/components/terminal/result-preview.ts +8 -56
- package/src/components/terminal/row.tsx +11 -98
- package/src/components/terminal/scrubber.tsx +108 -401
- package/src/components/terminal/surface.tsx +6 -54
- package/src/components/terminal/todos.ts +66 -0
- package/src/components/terminal/tool-run.ts +21 -192
- package/src/components/ui/AlertDialog.tsx +8 -23
- package/src/components/ui/Badge.tsx +18 -29
- package/src/components/ui/Button.tsx +7 -15
- package/src/components/ui/Card.tsx +2 -13
- package/src/components/ui/CodeBlock.tsx +10 -37
- package/src/components/ui/CopyButton.tsx +10 -23
- package/src/components/ui/Dialog.tsx +23 -36
- package/src/components/ui/Empty.tsx +6 -29
- package/src/components/ui/Input.tsx +14 -16
- package/src/components/ui/Menu.tsx +6 -18
- package/src/components/ui/PortalScope.tsx +1 -21
- package/src/components/ui/ProgressRing.tsx +8 -19
- package/src/components/ui/Select.tsx +18 -31
- package/src/components/ui/Sonner.tsx +1 -2
- package/src/components/ui/Spinner.tsx +1 -1
- package/src/components/ui/Splitter.tsx +30 -64
- package/src/components/ui/Textarea.tsx +2 -5
- package/src/components/ui/Tooltip.tsx +3 -13
- package/src/format.ts +0 -9
- package/src/index.ts +16 -113
- package/src/lib/clipboard.ts +8 -28
- package/src/lib/css.ts +8 -0
- package/src/lib/format.ts +101 -86
- package/src/lib/plan-request.ts +16 -0
- package/src/lib/status.ts +45 -95
- package/src/lib/tool-icon.ts +39 -47
- package/src/lib/utils.ts +1 -3
- package/src/styles/terminal.css +81 -49
- package/src/styles/theme.css +136 -19
- package/src/workspace.ts +1 -24
- package/build/SessionPanel-13l25ubU.d.mts +0 -609
- package/build/SessionPanel-DgwjH4Ve.mjs.map +0 -1
- package/build/format-ljc3lKpA.d.mts +0 -59
- package/build/status-BE-zg88x.mjs.map +0 -1
|
@@ -4,13 +4,10 @@ import { cn } from '../../lib/utils.ts'
|
|
|
4
4
|
|
|
5
5
|
export interface ResponseProps {
|
|
6
6
|
children: string
|
|
7
|
-
/** Streaming text: tolerate incomplete markdown (unclosed fences, half links). */
|
|
8
7
|
streaming?: boolean
|
|
9
8
|
className?: string
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
/** Markdown renderer for assistant output — streaming-safe via streamdown, code
|
|
13
|
-
* highlighted with shiki (dual theme follows [data-theme] through the dark: variant). */
|
|
14
11
|
export const Response = memo(
|
|
15
12
|
function Response({ children, streaming, className }: ResponseProps) {
|
|
16
13
|
return (
|
|
@@ -18,11 +15,12 @@ export const Response = memo(
|
|
|
18
15
|
mode={streaming ? 'streaming' : 'static'}
|
|
19
16
|
parseIncompleteMarkdown={streaming}
|
|
20
17
|
shikiTheme={['github-light', 'github-dark']}
|
|
21
|
-
|
|
18
|
+
linkSafety={{ enabled: false }}
|
|
19
|
+
className={cn('size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0', className)}
|
|
20
|
+
>
|
|
22
21
|
{children}
|
|
23
22
|
</Streamdown>
|
|
24
23
|
)
|
|
25
24
|
},
|
|
26
|
-
(prev, next) =>
|
|
27
|
-
prev.children === next.children && prev.streaming === next.streaming && prev.className === next.className,
|
|
25
|
+
(prev, next) => prev.children === next.children && prev.streaming === next.streaming && prev.className === next.className,
|
|
28
26
|
)
|
|
@@ -15,48 +15,19 @@ import {
|
|
|
15
15
|
type Mark,
|
|
16
16
|
} from './scrubber-marks.ts'
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* The cards variant's overview rail — the terminal scrubber's proportional
|
|
20
|
-
* sibling.
|
|
21
|
-
*
|
|
22
|
-
* Positions are **index space**, not pixel space: mark y is
|
|
23
|
-
* `itemIndex / items.length` of the rail, because proportional text and
|
|
24
|
-
* variable row heights leave the cards transcript with no honest pixel claim
|
|
25
|
-
* (the terminal rail's positions ride the height calculator, which has none
|
|
26
|
-
* here). Less precise, but it still answers the reader's questions — where did
|
|
27
|
-
* I type, where did it fail, where is the approval waiting.
|
|
28
|
-
*
|
|
29
|
-
* And because the positions are approximate, the rail is an **annotation, not
|
|
30
|
-
* a scrollbar**: no drag-to-scrub, no viewport band (meaningless without pixel
|
|
31
|
-
* positions), and the native scrollbar stays. The container never takes the
|
|
32
|
-
* pointer — only the marks do, when interactive — so the scrollbar keeps
|
|
33
|
-
* working straight through the paint. Hover peeks (from `items`, never the DOM
|
|
34
|
-
* — the row a mark describes is usually unmounted) and a click jumps to the
|
|
35
|
-
* mark's item through `onJumpToItem`.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
18
|
export interface ScrubberProps {
|
|
39
19
|
items: readonly TranscriptItem[]
|
|
40
20
|
pendingApprovals: readonly PermissionRequest[]
|
|
41
|
-
/** The catch-up boundary's item index, when the recap is spliced in. */
|
|
42
21
|
recapItemIndex?: number
|
|
43
|
-
/** Bookmarked item indices. Paint only — no store, no set affordance. */
|
|
44
22
|
bookmarks?: readonly number[]
|
|
45
|
-
/** The sub-agent takeover's parent id, when this rail belongs to a frame —
|
|
46
|
-
* what "top level" means to the mark walk (see `buildMarks`). */
|
|
47
23
|
frameParentId?: string
|
|
48
|
-
/** Whether the rail answers the pointer (hover peek, click to jump). False
|
|
49
|
-
* renders passive paint. */
|
|
50
24
|
interactive?: boolean
|
|
51
|
-
/** Jump to an item index. The caller owns the row mapping — item indices are
|
|
52
|
-
* not row indices (`rowIndexForItem`). */
|
|
53
25
|
onJumpToItem?: (itemIndex: number) => void
|
|
54
26
|
className?: string
|
|
55
27
|
}
|
|
56
28
|
|
|
57
29
|
function peekContent(
|
|
58
30
|
cluster: Cluster,
|
|
59
|
-
/** The member the pointer resolved to — see {@link nearestMember}. */
|
|
60
31
|
first: Mark | undefined,
|
|
61
32
|
items: readonly TranscriptItem[],
|
|
62
33
|
pendingApprovals: readonly PermissionRequest[],
|
|
@@ -68,7 +39,7 @@ function peekContent(
|
|
|
68
39
|
body = request ? (
|
|
69
40
|
<>
|
|
70
41
|
<div>{request.title ?? 'Permission required'}</div>
|
|
71
|
-
<div className=
|
|
42
|
+
<div className="wd-scrub-ex" data-tone="muted">
|
|
72
43
|
{`${request.displayName ?? request.toolName}(${toolInputPreview(request.input)})`}
|
|
73
44
|
</div>
|
|
74
45
|
</>
|
|
@@ -76,17 +47,15 @@ function peekContent(
|
|
|
76
47
|
} else if (first && first.kind !== 'recap') {
|
|
77
48
|
const item = items[first.itemIndex]
|
|
78
49
|
if (first.kind === 'turn' || first.kind === 'turnFailed') {
|
|
79
|
-
// The merged mark's peek carries both halves: the message the turn ended
|
|
80
|
-
// on, and the done-line (with its reasons, when it failed).
|
|
81
50
|
const turn = first.turnIndex === undefined ? undefined : items[first.turnIndex]
|
|
82
51
|
body = (
|
|
83
52
|
<>
|
|
84
|
-
{item?.kind === 'assistant_text' ? <div className=
|
|
53
|
+
{item?.kind === 'assistant_text' ? <div className="wd-scrub-ex">{item.text}</div> : null}
|
|
85
54
|
{turn?.kind === 'turn_result' ? (
|
|
86
55
|
<>
|
|
87
56
|
<div data-tone={turn.isError ? 'danger' : 'muted'}>{doneLine(turn)}</div>
|
|
88
57
|
{turn.errors?.map((message, index) => (
|
|
89
|
-
<div key={index} data-tone=
|
|
58
|
+
<div key={index} data-tone="danger">
|
|
90
59
|
{message}
|
|
91
60
|
</div>
|
|
92
61
|
))}
|
|
@@ -101,18 +70,12 @@ function peekContent(
|
|
|
101
70
|
: undefined
|
|
102
71
|
body = (
|
|
103
72
|
<>
|
|
104
|
-
<div
|
|
105
|
-
|
|
106
|
-
data-tone={
|
|
107
|
-
first.kind === 'error' || first.kind === 'toolFailed' ? 'danger' : undefined
|
|
108
|
-
}>
|
|
109
|
-
{first.kind === 'user' ? <span data-tone='muted'>{'❯ '}</span> : null}
|
|
73
|
+
<div className="wd-scrub-ex" data-tone={first.kind === 'error' || first.kind === 'toolFailed' ? 'danger' : undefined}>
|
|
74
|
+
{first.kind === 'user' ? <span data-tone="muted">{'❯ '}</span> : null}
|
|
110
75
|
{excerpt(item)}
|
|
111
76
|
</div>
|
|
112
|
-
{/* Which tool failed is rarely the question — the first non-blank
|
|
113
|
-
line of what it said back is the thing worth peeking at. */}
|
|
114
77
|
{failure ? (
|
|
115
|
-
<div className=
|
|
78
|
+
<div className="wd-scrub-ex" data-tone="danger">
|
|
116
79
|
{failure}
|
|
117
80
|
</div>
|
|
118
81
|
) : null}
|
|
@@ -122,7 +85,7 @@ function peekContent(
|
|
|
122
85
|
}
|
|
123
86
|
return (
|
|
124
87
|
<>
|
|
125
|
-
<div data-tone=
|
|
88
|
+
<div data-tone="muted">
|
|
126
89
|
{KIND_NAME[first?.kind ?? cluster.kind]}
|
|
127
90
|
{more}
|
|
128
91
|
</div>
|
|
@@ -144,60 +107,57 @@ export function Scrubber({
|
|
|
144
107
|
const railRef = useRef<HTMLDivElement | null>(null)
|
|
145
108
|
const peekRef = useRef<HTMLDivElement | null>(null)
|
|
146
109
|
const [railH, setRailH] = useState(0)
|
|
147
|
-
const [peek, setPeek] = useState<{ cluster: Cluster; mark: Mark | undefined; y: number } | null>(
|
|
148
|
-
null,
|
|
149
|
-
)
|
|
110
|
+
const [peek, setPeek] = useState<{ cluster: Cluster; mark: Mark | undefined; y: number } | null>(null)
|
|
150
111
|
|
|
151
112
|
useEffect(() => {
|
|
152
113
|
const element = railRef.current
|
|
153
|
-
if (!element)
|
|
114
|
+
if (!element) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
154
117
|
const observer = new ResizeObserver(() => setRailH(element.clientHeight))
|
|
155
118
|
observer.observe(element)
|
|
156
119
|
setRailH(element.clientHeight)
|
|
157
120
|
return () => observer.disconnect()
|
|
158
121
|
}, [])
|
|
159
122
|
|
|
160
|
-
// A peek is a snapshot of the cluster it was opened on; if the transcript
|
|
161
|
-
// changes underneath (a fixture/session swap, a burst of new items), drop it
|
|
162
|
-
// rather than describe items that no longer exist.
|
|
163
123
|
useEffect(() => {
|
|
164
124
|
setPeek(null)
|
|
165
125
|
}, [items])
|
|
166
126
|
|
|
167
|
-
// The peek can be taller than the space beside its mark — clamp it into the
|
|
168
|
-
// rail after it has a measured height.
|
|
169
127
|
useLayoutEffect(() => {
|
|
170
128
|
const element = peekRef.current
|
|
171
|
-
if (!element || !peek)
|
|
129
|
+
if (!element || !peek) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
172
132
|
const height = element.offsetHeight
|
|
173
133
|
const railHeight = railRef.current?.clientHeight ?? 0
|
|
174
134
|
element.style.top = `${Math.max(4, Math.min(railHeight - height - 4, peek.y - height / 2))}px`
|
|
175
135
|
}, [peek])
|
|
176
136
|
|
|
177
137
|
const clusters = useMemo(() => {
|
|
178
|
-
if (railH <= 0)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
138
|
+
if (railH <= 0) {
|
|
139
|
+
return []
|
|
140
|
+
}
|
|
141
|
+
const built = clusterMarks(buildMarks(items, { frameParentId, bookmarks, recapItemIndex }), railH, items.length)
|
|
142
|
+
if (pendingApprovals.length > 0) {
|
|
143
|
+
built.push(approvalCluster(railH))
|
|
144
|
+
}
|
|
185
145
|
return built
|
|
186
146
|
}, [items, frameParentId, bookmarks, recapItemIndex, pendingApprovals, railH])
|
|
187
147
|
|
|
188
|
-
|
|
189
|
-
const railY = (clientY: number): number =>
|
|
190
|
-
clientY - (railRef.current?.getBoundingClientRect().top ?? 0)
|
|
148
|
+
const railY = (clientY: number): number => clientY - (railRef.current?.getBoundingClientRect().top ?? 0)
|
|
191
149
|
|
|
192
150
|
const activate = (cluster: Cluster, clientY: number) => {
|
|
193
151
|
if (cluster.kind === 'approval' && cluster.marks.length === 0) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
152
|
+
if (items.length > 0) {
|
|
153
|
+
onJumpToItem?.(items.length - 1)
|
|
154
|
+
}
|
|
197
155
|
return
|
|
198
156
|
}
|
|
199
157
|
const mark = nearestMember(cluster, railY(clientY))
|
|
200
|
-
if (mark)
|
|
158
|
+
if (mark) {
|
|
159
|
+
onJumpToItem?.(mark.itemIndex)
|
|
160
|
+
}
|
|
201
161
|
}
|
|
202
162
|
|
|
203
163
|
const showPeek = (cluster: Cluster, clientY: number) => {
|
|
@@ -210,19 +170,12 @@ export function Scrubber({
|
|
|
210
170
|
)
|
|
211
171
|
}
|
|
212
172
|
|
|
213
|
-
// Paint either way: the rail duplicates information the transcript itself
|
|
214
|
-
// carries, and interactive it is a pointer affordance rather than the
|
|
215
|
-
// scroll surface — the native scrollbar stays the accessible one.
|
|
216
173
|
return (
|
|
217
|
-
<div
|
|
218
|
-
ref={railRef}
|
|
219
|
-
className={cn('wd-scrubber', className)}
|
|
220
|
-
data-interactive={interactive || undefined}
|
|
221
|
-
aria-hidden>
|
|
174
|
+
<div ref={railRef} className={cn('wd-scrubber', className)} data-interactive={interactive || undefined} aria-hidden>
|
|
222
175
|
{clusters.map((cluster, index) => (
|
|
223
176
|
<div
|
|
224
177
|
key={index}
|
|
225
|
-
className=
|
|
178
|
+
className="wd-scrub-mark"
|
|
226
179
|
data-lane={cluster.lane}
|
|
227
180
|
data-kind={cluster.kind}
|
|
228
181
|
style={{ top: cluster.y, height: cluster.h }}
|
|
@@ -230,8 +183,6 @@ export function Scrubber({
|
|
|
230
183
|
? {
|
|
231
184
|
onClick: (event) => activate(cluster, event.clientY),
|
|
232
185
|
onPointerEnter: (event) => showPeek(cluster, event.clientY),
|
|
233
|
-
// A chain-merged bar can span the rail; sliding along it
|
|
234
|
-
// retargets the peek to the member under the pointer.
|
|
235
186
|
onPointerMove: (event) => showPeek(cluster, event.clientY),
|
|
236
187
|
onPointerLeave: () => setPeek(null),
|
|
237
188
|
}
|
|
@@ -239,7 +190,7 @@ export function Scrubber({
|
|
|
239
190
|
/>
|
|
240
191
|
))}
|
|
241
192
|
{peek ? (
|
|
242
|
-
<div ref={peekRef} className=
|
|
193
|
+
<div ref={peekRef} className="wd-scrub-peek" style={{ top: peek.y }}>
|
|
243
194
|
{peekContent(peek.cluster, peek.mark, items, pendingApprovals)}
|
|
244
195
|
</div>
|
|
245
196
|
) : null}
|