@workerdeck/ui 0.17.0 → 0.18.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-CnNYEX80.d.mts → SessionPanel-CnU_IJ3-.d.mts} +24 -9
- package/build/{SessionPanel-DMPhsNlW.mjs → SessionPanel-DPx8Iz8a.mjs} +439 -74
- package/build/SessionPanel-DPx8Iz8a.mjs.map +1 -0
- package/build/{format-DfI_je9S.d.mts → format-ljc3lKpA.d.mts} +1 -1
- package/build/format.d.mts +16 -5
- package/build/format.mjs +2 -3
- package/build/index.d.mts +237 -8
- package/build/index.mjs +399 -167
- package/build/index.mjs.map +1 -1
- package/build/{format-DqR56Y8l.mjs → status-BE-zg88x.mjs} +154 -2
- package/build/status-BE-zg88x.mjs.map +1 -0
- package/build/workspace.d.mts +4 -1
- package/build/workspace.mjs +4 -3
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/ContextRing.tsx +41 -0
- package/src/components/agent/EngineIcon.tsx +40 -0
- package/src/components/agent/SessionBrowser.tsx +115 -22
- package/src/components/agent/SessionPanel.tsx +153 -3
- package/src/components/agent/SessionSteps.tsx +233 -0
- package/src/components/agent/SessionWorkspace.tsx +4 -0
- package/src/components/agent/StatusBar.tsx +4 -2
- package/src/components/agent/SubagentStrip.tsx +134 -0
- package/src/components/agent/Transcript.tsx +138 -19
- package/src/components/agent/transcript-rows.ts +9 -1
- package/src/components/terminal/TerminalTranscript.tsx +77 -3
- package/src/components/terminal/affordances.tsx +34 -0
- package/src/components/terminal/blocks.ts +28 -0
- package/src/components/terminal/height.ts +24 -0
- package/src/components/terminal/items.tsx +1 -1
- package/src/components/terminal/scrubber.tsx +28 -3
- package/src/components/terminal/tool-run.ts +49 -5
- package/src/index.ts +19 -1
- package/src/lib/status.ts +16 -3
- package/src/styles/terminal.css +10 -0
- package/src/styles/theme.css +42 -0
- package/build/SessionPanel-DMPhsNlW.mjs.map +0 -1
- package/build/format-DqR56Y8l.mjs.map +0 -1
- package/build/status-Ydzi7n6j.mjs +0 -143
- package/build/status-Ydzi7n6j.mjs.map +0 -1
|
@@ -117,14 +117,58 @@ const trimmed = (value: unknown): string | undefined =>
|
|
|
117
117
|
* emptier than a plain tool row's.
|
|
118
118
|
*/
|
|
119
119
|
export function taskLabel(task: ToolCallItem): string {
|
|
120
|
+
return `${task.name}(${taskIdentity(task)})`
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The inner half of {@link taskLabel} — `Explore · find the auth check` — without
|
|
125
|
+
* the `Task(…)` wrapper.
|
|
126
|
+
*
|
|
127
|
+
* Split out for the sub-agent takeover's header, which names the agent it is
|
|
128
|
+
* showing and has no room (or reason) to repeat the tool's own name: the whole
|
|
129
|
+
* surface *is* that Task. Extracted rather than re-spelled so the header and the
|
|
130
|
+
* row it was opened from cannot drift, and so both keep matching protocol's
|
|
131
|
+
* `subagentLabel`, which reads the same two input fields for the sessions list.
|
|
132
|
+
*/
|
|
133
|
+
export function taskIdentity(task: ToolCallItem): string {
|
|
120
134
|
const input = task.input as { description?: unknown; subagent_type?: unknown } | null
|
|
121
135
|
const description = trimmed(input?.description)
|
|
122
136
|
const agent = trimmed(input?.subagent_type)
|
|
123
|
-
|
|
124
|
-
agent
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
return agent && description
|
|
138
|
+
? `${agent} · ${clip(description)}`
|
|
139
|
+
: (agent ?? (description ? clip(description) : toolInputPreview(task.input)))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* **What this agent was actually asked** — the sub-agent's brief, or undefined
|
|
144
|
+
* when the engine did not give us one.
|
|
145
|
+
*
|
|
146
|
+
* Measured against a live session rather than assumed: the Agent SDK's call
|
|
147
|
+
* carries `{description, subagent_type, run_in_background, prompt}`, and
|
|
148
|
+
* `prompt` is the instruction.
|
|
149
|
+
*
|
|
150
|
+
* **Whether it also arrives in the stream depends on the agent.** A foreground
|
|
151
|
+
* `Task` forwards its brief as a real nested `user_message` (that is what
|
|
152
|
+
* `forwardSubagentText` buys and what the reducer stamps a `parentToolUseId`
|
|
153
|
+
* on), so `subagentItems` picks it up and the frame already has it. A
|
|
154
|
+
* **background** agent does not: measured on a session with eight of them,
|
|
155
|
+
* zero items carried a `parentToolUseId` on a `user` kind. Those runs are
|
|
156
|
+
* exactly the ones you open a takeover on — an agent working while you read
|
|
157
|
+
* something else — and their brief was nowhere at all.
|
|
158
|
+
*
|
|
159
|
+
* So this is the *fallback*, not the source: the callers splice it in only when
|
|
160
|
+
* the frame carries no brief of its own, or the same instruction would be drawn
|
|
161
|
+
* twice.
|
|
162
|
+
*
|
|
163
|
+
* `description` is deliberately not a fallback: it is the 3–5 word label the
|
|
164
|
+
* header already prints, and repeating it as a brief would claim we know the
|
|
165
|
+
* instruction when we do not. **Codex genuinely has none** — its `spawn_agent`
|
|
166
|
+
* message is an encrypted blob on the wire — so on that engine this is
|
|
167
|
+
* undefined and the row is not drawn, rather than drawn empty.
|
|
168
|
+
*/
|
|
169
|
+
export function taskBrief(task: ToolCallItem): string | undefined {
|
|
170
|
+
const input = task.input as { prompt?: unknown } | null
|
|
171
|
+
return trimmed(input?.prompt)
|
|
128
172
|
}
|
|
129
173
|
|
|
130
174
|
const callBusy = (call: ToolCallItem): boolean =>
|
package/src/index.ts
CHANGED
|
@@ -180,7 +180,24 @@ export {
|
|
|
180
180
|
export { SessionStatusIcon } from './components/agent/SessionBrowser.tsx'
|
|
181
181
|
// Lifted out of the VS Code sidebar once the dashboard grew a collapsed rail
|
|
182
182
|
// that needs the same glyph — two copies of a trademark set is one too many.
|
|
183
|
-
export {
|
|
183
|
+
export {
|
|
184
|
+
EngineIcon,
|
|
185
|
+
engineMark,
|
|
186
|
+
vendorMarkClass,
|
|
187
|
+
vendorTextClass,
|
|
188
|
+
} from './components/agent/EngineIcon.tsx'
|
|
189
|
+
// Lifted out of the sidebar for the same reason: the work under a row is a
|
|
190
|
+
// protocol fact, so every list annotates it with one component.
|
|
191
|
+
export {
|
|
192
|
+
type Step,
|
|
193
|
+
StepRow,
|
|
194
|
+
StepToggle,
|
|
195
|
+
runningSteps,
|
|
196
|
+
sessionSteps,
|
|
197
|
+
} from './components/agent/SessionSteps.tsx'
|
|
198
|
+
// The sub-agent takeover's one line. Exported for hosts that draw their own
|
|
199
|
+
// panel chrome; the panel raises it itself.
|
|
200
|
+
export { SubagentStrip } from './components/agent/SubagentStrip.tsx'
|
|
184
201
|
export { ProjectIcon } from './components/agent/ProjectIcon.tsx'
|
|
185
202
|
export {
|
|
186
203
|
SessionEmptyState,
|
|
@@ -188,6 +205,7 @@ export {
|
|
|
188
205
|
} from './components/agent/SessionEmptyState.tsx'
|
|
189
206
|
export { PromptTokenText } from './components/agent/PromptTokenText.tsx'
|
|
190
207
|
export { STATUS_META } from './components/agent/status.ts'
|
|
208
|
+
export { ContextRing } from './components/agent/ContextRing.tsx'
|
|
191
209
|
|
|
192
210
|
// Utilities
|
|
193
211
|
export { cn } from './lib/utils.ts'
|
package/src/lib/status.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ModelOption, RateLimitInfo, SessionStatus } from '@workerdeck/protocol'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* How a session's live readings become a status line — the pure half, so every
|
|
@@ -174,7 +174,20 @@ export function modelLabel(vitals: ModelReadings | undefined): string {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/** Context percentage as its meter severity — the reading and the colour come
|
|
177
|
-
* from one place so a panel and a status bar never disagree.
|
|
178
|
-
|
|
177
|
+
* from one place so a panel and a status bar never disagree.
|
|
178
|
+
*
|
|
179
|
+
* Takes only the number it reads, so the compact `ContextReading` that rides
|
|
180
|
+
* the sessions list and the full `ContextUsage` on the event stream are coloured
|
|
181
|
+
* by one rule rather than two. */
|
|
182
|
+
export function contextSeverity(usage: { percentage: number } | undefined): StatusSeverity {
|
|
179
183
|
return meterSeverity(usage?.percentage)
|
|
180
184
|
}
|
|
185
|
+
|
|
186
|
+
/** {@link meterSeverity} as a text colour class. Here rather than beside each
|
|
187
|
+
* meter because the status bar, the context dialog and a sessions-list row all
|
|
188
|
+
* paint the same reading, and a fourth copy of the thresholds is a fourth
|
|
189
|
+
* chance for one surface to call 81% orange and another call it grey. */
|
|
190
|
+
export function meterColorClass(pct: number | undefined): string {
|
|
191
|
+
const severity = meterSeverity(pct)
|
|
192
|
+
return severity === 'error' ? 'text-danger' : severity === 'warning' ? 'text-warning' : 'text-fg-3'
|
|
193
|
+
}
|
package/src/styles/terminal.css
CHANGED
|
@@ -1032,6 +1032,16 @@
|
|
|
1032
1032
|
pointer-events: none;
|
|
1033
1033
|
z-index: 11;
|
|
1034
1034
|
}
|
|
1035
|
+
/* The sub-agent's brief, collapsed: clipped on the same wrapped lines
|
|
1036
|
+
`briefPx` counts, so the height the virtualizer reserved and the height the
|
|
1037
|
+
row draws cannot disagree. The count itself comes from the component
|
|
1038
|
+
(`BRIEF_LINES`), one number rather than one here and one there. */
|
|
1039
|
+
.term-brief-clip {
|
|
1040
|
+
display: -webkit-box;
|
|
1041
|
+
-webkit-box-orient: vertical;
|
|
1042
|
+
overflow: hidden;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1035
1045
|
.term-scrubber .term-scrub-ex {
|
|
1036
1046
|
display: -webkit-box;
|
|
1037
1047
|
-webkit-line-clamp: 4;
|
package/src/styles/theme.css
CHANGED
|
@@ -51,6 +51,37 @@
|
|
|
51
51
|
--fg-4: #a1a1a1;
|
|
52
52
|
--fg-disabled: #c9c9c9;
|
|
53
53
|
|
|
54
|
+
/* ---------- Vendor ----------
|
|
55
|
+
* The engine's own colour, worn by its mark and (where the brand allows) by
|
|
56
|
+
* the model name beside it in a sessions list. Nowhere else.
|
|
57
|
+
*
|
|
58
|
+
* This is the one place a brand colour is allowed in the product, and it earns
|
|
59
|
+
* it by doing a job nothing else on the row does: the list already spends blue
|
|
60
|
+
* on running, amber on waiting, red on failed and muted on idle, so none of
|
|
61
|
+
* those can also say *whose engine this is* — and the mark alone is a 12px
|
|
62
|
+
* silhouette.
|
|
63
|
+
*
|
|
64
|
+
* Two values per vendor, because that glyph has to hold against both grounds:
|
|
65
|
+
* the dark-ground value is lifted and the light-ground one darkened.
|
|
66
|
+
*
|
|
67
|
+
* **OpenAI's is monochrome, and that is their rule rather than our taste**:
|
|
68
|
+
* their guidelines forbid adding colour to the mark, so it is pure white on
|
|
69
|
+
* dark and a near-black on light — both sanctioned, and the only kind of pair
|
|
70
|
+
* legible on both grounds. A green was tried first and is wrong, however well
|
|
71
|
+
* it reads. Near-black rather than `#000` because pure black would sit *harder*
|
|
72
|
+
* than the session title above it, so the mark would out-weigh the name it is
|
|
73
|
+
* labelling.
|
|
74
|
+
*
|
|
75
|
+
* That makes the two vendors asymmetric, which is the honest outcome: coral is
|
|
76
|
+
* Anthropic's brand and monochrome is OpenAI's, so a rule that gave both an
|
|
77
|
+
* accent would misrepresent one of them. It also means OpenAI's token is at
|
|
78
|
+
* full contrast, which is why only the **mark** wears it — see
|
|
79
|
+
* `vendorTextClass` in `EngineIcon.tsx`.
|
|
80
|
+
*
|
|
81
|
+
* A third vendor is two declarations here and one map entry there. */
|
|
82
|
+
--vendor-claude: #b3573a;
|
|
83
|
+
--vendor-openai: #373737;
|
|
84
|
+
|
|
54
85
|
/* ---------- Accent ----------
|
|
55
86
|
* VS Code blue (`#0078d4`) — the product's primary. It is the *same* hue in
|
|
56
87
|
* both themes rather than the inverse of the canvas: a primary that flips
|
|
@@ -122,6 +153,10 @@
|
|
|
122
153
|
--fg-4: #6e6e6e;
|
|
123
154
|
--fg-disabled: #454545;
|
|
124
155
|
|
|
156
|
+
/* ---------- Vendor ---------- (see the light block for the whole argument) */
|
|
157
|
+
--vendor-claude: #cc7c5e;
|
|
158
|
+
--vendor-openai: #ffffff;
|
|
159
|
+
|
|
125
160
|
/* ---------- Accent ----------
|
|
126
161
|
* The same blue as light. `--accent` stays `#0078d4` because it is a *fill*
|
|
127
162
|
* carrying white text (4.6:1) — the brighter `#3794ff` would drop that to
|
|
@@ -275,6 +310,13 @@
|
|
|
275
310
|
--color-info: var(--info);
|
|
276
311
|
--color-info-bg: var(--info-bg);
|
|
277
312
|
|
|
313
|
+
/* Vendor colours as real utilities (`text-vendor-claude`), not hand-written
|
|
314
|
+
classes: `cn`'s tailwind-merge only knows to *replace* a colour it can parse,
|
|
315
|
+
and `EngineIcon` ships its own `text-fg-3` that the vendor class has to win
|
|
316
|
+
against rather than merely follow in source order. */
|
|
317
|
+
--color-vendor-claude: var(--vendor-claude);
|
|
318
|
+
--color-vendor-openai: var(--vendor-openai);
|
|
319
|
+
|
|
278
320
|
/* Stock-name bridge → utilities (bg-primary / text-muted-foreground / ...). */
|
|
279
321
|
--color-primary: var(--primary);
|
|
280
322
|
--color-primary-foreground: var(--primary-foreground);
|