@workerdeck/ui 2.11.0 → 2.12.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-BVbau2eR.d.mts → SessionPanel-CJwp5B3q.d.mts} +8 -3
- package/build/index.d.mts +36 -18
- package/build/index.mjs +109 -46
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +4 -10
- package/build/{use-path-links-DO_79-wX.mjs → use-path-links-CepFsMdW.mjs} +137 -79
- package/build/use-path-links-CepFsMdW.mjs.map +1 -0
- package/build/workspace.d.mts +1 -1
- package/build/workspace.mjs +1 -1
- package/package.json +4 -4
- package/src/components/agent/Composer.tsx +28 -5
- package/src/components/agent/PromptTokenText.tsx +9 -1
- package/src/components/agent/SessionBrowser.tsx +17 -1
- package/src/components/agent/SessionItem.tsx +6 -23
- package/src/components/agent/SessionPanel.tsx +10 -0
- package/src/components/agent/SessionSteps.tsx +15 -55
- package/src/components/agent/SubagentToggle.tsx +69 -0
- package/src/components/agent/Transcript.tsx +91 -80
- package/src/components/agent/composer-commands.ts +18 -0
- package/src/index.ts +10 -2
- package/src/styles/terminal.css +9 -1
- package/build/use-path-links-DO_79-wX.mjs.map +0 -1
|
@@ -10,7 +10,7 @@ import { subagentItems, terminalBlocks, type ToolCallItem } from '../terminal/bl
|
|
|
10
10
|
import { taskBrief, taskBusy } from '../terminal/tool-run.ts'
|
|
11
11
|
import { type TranscriptRow } from './transcript-rows.ts'
|
|
12
12
|
import { TranscriptRows } from './TranscriptRows.tsx'
|
|
13
|
-
import { SkillNamesProvider } from './PromptTokenText.tsx'
|
|
13
|
+
import { SessionNamesProvider, SkillNamesProvider } from './PromptTokenText.tsx'
|
|
14
14
|
import { TerminalSurface } from '../terminal/surface.tsx'
|
|
15
15
|
import { ROW_GAP, TranscriptVariantProvider, type TranscriptDensity, type TranscriptVariant } from './transcript-variant.tsx'
|
|
16
16
|
|
|
@@ -79,6 +79,8 @@ export interface TranscriptProps {
|
|
|
79
79
|
stickyPrompt?: boolean
|
|
80
80
|
scrubber?: boolean
|
|
81
81
|
bookmarks?: readonly string[]
|
|
82
|
+
// Folded peer slugs, so a `#Name` this client can name draws as a token.
|
|
83
|
+
sessionNames?: readonly string[]
|
|
82
84
|
replaying?: boolean
|
|
83
85
|
catchUp?: { from: number; since?: number }
|
|
84
86
|
jumpToRecapRef?: RefObject<(() => void) | null>
|
|
@@ -104,6 +106,7 @@ export function Transcript({
|
|
|
104
106
|
stickyPrompt = false,
|
|
105
107
|
scrubber,
|
|
106
108
|
bookmarks,
|
|
109
|
+
sessionNames,
|
|
107
110
|
replaying = false,
|
|
108
111
|
catchUp,
|
|
109
112
|
jumpToRecapRef,
|
|
@@ -141,88 +144,96 @@ export function Transcript({
|
|
|
141
144
|
return (
|
|
142
145
|
<TranscriptVariantProvider value={variant}>
|
|
143
146
|
<SkillNamesProvider names={skillNames}>
|
|
144
|
-
<
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
emptyState
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)
|
|
164
|
-
) : null}
|
|
165
|
-
{frame && frameTask === undefined && !replaying ? null : (
|
|
166
|
-
<TranscriptRows
|
|
167
|
-
rows={rows}
|
|
168
|
-
boundary={boundary}
|
|
169
|
-
since={catchUp?.since}
|
|
170
|
-
terminal={terminal}
|
|
171
|
-
replaying={replaying}
|
|
172
|
-
stickyPrompt={!frame && stickyPrompt}
|
|
173
|
-
gap={gap}
|
|
174
|
-
fontSize={fontSize}
|
|
175
|
-
lineHeight={lineHeight}
|
|
176
|
-
items={items}
|
|
177
|
-
pendingApprovals={state.pendingApprovals}
|
|
178
|
-
scrubber={scrubber}
|
|
179
|
-
bookmarks={frame ? undefined : bookmarks}
|
|
180
|
-
affordances={affordances}
|
|
181
|
-
fileUrl={fileUrl}
|
|
182
|
-
attachmentUrl={attachmentUrl}
|
|
183
|
-
hostImage={hostImage}
|
|
184
|
-
jumpToRecapRef={jumpToRecapRef}
|
|
185
|
-
repinRef={repinRef}
|
|
186
|
-
reveal={frame ? undefined : reveal}
|
|
187
|
-
frameParentId={frame?.parentToolUseId}
|
|
188
|
-
onOpenSubagent={frame ? undefined : onOpenSubagent}
|
|
189
|
-
/>
|
|
190
|
-
)}
|
|
191
|
-
{(frame ? frameTask !== undefined && taskBusy(frameTask, items) : showLoader(state)) ? (
|
|
192
|
-
terminal ? (
|
|
193
|
-
<>
|
|
194
|
-
{state.items.length > 0 ? <div className="term-blank" aria-hidden /> : null}
|
|
195
|
-
<WorkingRow
|
|
196
|
-
label={state.status === 'starting' ? 'Starting…' : 'Working…'}
|
|
197
|
-
startedAt={runStartedAt}
|
|
198
|
-
tokens={state.contextUsage?.totalTokens}
|
|
147
|
+
<SessionNamesProvider names={sessionNames}>
|
|
148
|
+
<Conversation className={cn(replaying && 'invisible', className)}>
|
|
149
|
+
<ConversationContent className={cn(terminal && 'gap-0 p-0')}>
|
|
150
|
+
<TerminalShell active={terminal} fontSize={fontSize} lineHeight={lineHeight} affordances={affordances}>
|
|
151
|
+
{frame ? (
|
|
152
|
+
frameTask === undefined && !replaying ? (
|
|
153
|
+
<div className={cn(terminal ? 'term-row text-fg-4' : 'p-4 text-body-sm text-fg-4')}>
|
|
154
|
+
This sub-agent's work is not in this transcript.
|
|
155
|
+
</div>
|
|
156
|
+
) : null
|
|
157
|
+
) : items.length === 0 && state.status !== 'starting' ? (
|
|
158
|
+
emptyState !== undefined ? (
|
|
159
|
+
emptyState
|
|
160
|
+
) : (
|
|
161
|
+
<SessionEmptyState
|
|
162
|
+
cwd={state.cwd}
|
|
163
|
+
hasCommands={!!state.commands?.length}
|
|
164
|
+
hasSkills={!!state.skills?.some((s) => s.enabled)}
|
|
165
|
+
canBrowseFiles={canBrowseFiles}
|
|
199
166
|
/>
|
|
200
|
-
|
|
201
|
-
) :
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
167
|
+
)
|
|
168
|
+
) : null}
|
|
169
|
+
{frame && frameTask === undefined && !replaying ? null : (
|
|
170
|
+
<TranscriptRows
|
|
171
|
+
rows={rows}
|
|
172
|
+
boundary={boundary}
|
|
173
|
+
since={catchUp?.since}
|
|
174
|
+
terminal={terminal}
|
|
175
|
+
replaying={replaying}
|
|
176
|
+
stickyPrompt={!frame && stickyPrompt}
|
|
177
|
+
gap={gap}
|
|
178
|
+
fontSize={fontSize}
|
|
179
|
+
lineHeight={lineHeight}
|
|
180
|
+
items={items}
|
|
181
|
+
pendingApprovals={state.pendingApprovals}
|
|
182
|
+
scrubber={scrubber}
|
|
183
|
+
bookmarks={frame ? undefined : bookmarks}
|
|
184
|
+
affordances={affordances}
|
|
185
|
+
fileUrl={fileUrl}
|
|
186
|
+
attachmentUrl={attachmentUrl}
|
|
187
|
+
hostImage={hostImage}
|
|
188
|
+
jumpToRecapRef={jumpToRecapRef}
|
|
189
|
+
repinRef={repinRef}
|
|
190
|
+
reveal={frame ? undefined : reveal}
|
|
191
|
+
frameParentId={frame?.parentToolUseId}
|
|
192
|
+
onOpenSubagent={frame ? undefined : onOpenSubagent}
|
|
193
|
+
/>
|
|
221
194
|
)}
|
|
195
|
+
{(frame ? frameTask !== undefined && taskBusy(frameTask, items) : showLoader(state)) ? (
|
|
196
|
+
terminal ? (
|
|
197
|
+
<>
|
|
198
|
+
{state.items.length > 0 ? <div className="term-blank" aria-hidden /> : null}
|
|
199
|
+
<WorkingRow
|
|
200
|
+
label={state.status === 'starting' ? 'Starting…' : 'Working…'}
|
|
201
|
+
startedAt={runStartedAt}
|
|
202
|
+
tokens={state.contextUsage?.totalTokens}
|
|
203
|
+
/>
|
|
204
|
+
</>
|
|
205
|
+
) : (
|
|
206
|
+
<Loader label={state.status === 'starting' ? 'Starting session…' : undefined} />
|
|
207
|
+
)
|
|
208
|
+
) : null}
|
|
209
|
+
</TerminalShell>
|
|
210
|
+
</ConversationContent>
|
|
211
|
+
<ConversationScrollButton />
|
|
212
|
+
{replaying ? (
|
|
213
|
+
<div
|
|
214
|
+
data-slot="transcript-hold"
|
|
215
|
+
aria-hidden
|
|
216
|
+
className="wd-hold-appear visible pointer-events-none absolute inset-0 overflow-hidden"
|
|
217
|
+
>
|
|
218
|
+
<div className={cn('mx-auto w-full max-w-[var(--wd-transcript-max-width)]', !terminal && 'px-4 py-4')}>
|
|
219
|
+
{terminal ? (
|
|
220
|
+
<TerminalSurface
|
|
221
|
+
fontSize={fontSize}
|
|
222
|
+
lineHeight={lineHeight}
|
|
223
|
+
affordances={false}
|
|
224
|
+
bleed="1ch"
|
|
225
|
+
className="term-transcript"
|
|
226
|
+
>
|
|
227
|
+
<WorkingRow label="Loading…" />
|
|
228
|
+
</TerminalSurface>
|
|
229
|
+
) : (
|
|
230
|
+
<Loader label="Loading session…" />
|
|
231
|
+
)}
|
|
232
|
+
</div>
|
|
222
233
|
</div>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
</
|
|
234
|
+
) : null}
|
|
235
|
+
</Conversation>
|
|
236
|
+
</SessionNamesProvider>
|
|
226
237
|
</SkillNamesProvider>
|
|
227
238
|
</TranscriptVariantProvider>
|
|
228
239
|
)
|
|
@@ -109,6 +109,24 @@ export function rankComposerRows(query: string, rows: readonly ComposerRow[]): C
|
|
|
109
109
|
return scored.map(({ row }) => row)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// The `#` picker's ranking. Peers arrive most-recently-active first, and an empty query keeps that
|
|
113
|
+
// order: which session moved last is the most useful thing the list can say before anything is typed.
|
|
114
|
+
export function rankPeerSessions<T extends { slug: string; label: string }>(query: string, peers: readonly T[]): T[] {
|
|
115
|
+
const needle = query.trim()
|
|
116
|
+
if (!needle) {
|
|
117
|
+
return [...peers]
|
|
118
|
+
}
|
|
119
|
+
const scored: Array<{ score: number; index: number; peer: T }> = []
|
|
120
|
+
peers.forEach((peer, index) => {
|
|
121
|
+
const score = matchScore(needle, [peer.slug, peer.label, ...peer.slug.split('-')])
|
|
122
|
+
if (score > 0) {
|
|
123
|
+
scored.push({ score, index, peer })
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
scored.sort((a, b) => b.score - a.score || a.index - b.index)
|
|
127
|
+
return scored.map(({ peer }) => peer)
|
|
128
|
+
}
|
|
129
|
+
|
|
112
130
|
export function matchClientCommand(text: string, commands: readonly ClientCommand[]): { command: ClientCommand; args: string } | undefined {
|
|
113
131
|
const match = /^\/([\w:-]+)[ \t]*([\s\S]*)$/.exec(text.trim())
|
|
114
132
|
if (!match) {
|
package/src/index.ts
CHANGED
|
@@ -94,6 +94,7 @@ export { Composer, type ComposerFileMatch, type ComposerHandle, type ComposerPro
|
|
|
94
94
|
export {
|
|
95
95
|
mergeComposerRows,
|
|
96
96
|
rankComposerRows,
|
|
97
|
+
rankPeerSessions,
|
|
97
98
|
matchClientCommand,
|
|
98
99
|
skillPrompt,
|
|
99
100
|
type ClientCommand,
|
|
@@ -124,11 +125,18 @@ export { SessionBrowser, rowShapeClass, type SessionBrowserProps } from './compo
|
|
|
124
125
|
export { SessionItem, type SelectModifiers, type SessionItemProps } from './components/agent/SessionItem.tsx'
|
|
125
126
|
export { SessionStatusIcon } from './components/agent/SessionStatusIcon.tsx'
|
|
126
127
|
export { EngineIcon, engineMark, vendorMarkClass, vendorTextClass } from './components/agent/EngineIcon.tsx'
|
|
127
|
-
export { type Step, StepRow,
|
|
128
|
+
export { type Step, StepRow, sessionSteps } from './components/agent/SessionSteps.tsx'
|
|
129
|
+
export {
|
|
130
|
+
SUBAGENT_DISPLAY_META,
|
|
131
|
+
SUBAGENT_DISPLAY_ORDER,
|
|
132
|
+
SubagentDisplayIcon,
|
|
133
|
+
SubagentToggle,
|
|
134
|
+
type SubagentToggleProps,
|
|
135
|
+
} from './components/agent/SubagentToggle.tsx'
|
|
128
136
|
export { SubagentStrip } from './components/agent/SubagentStrip.tsx'
|
|
129
137
|
export { ProjectIcon } from './components/agent/ProjectIcon.tsx'
|
|
130
138
|
export { SessionEmptyState, type SessionEmptyStateProps } from './components/agent/SessionEmptyState.tsx'
|
|
131
|
-
export { PromptTokenText } from './components/agent/PromptTokenText.tsx'
|
|
139
|
+
export { PromptTokenText, SessionNamesProvider, SkillNamesProvider } from './components/agent/PromptTokenText.tsx'
|
|
132
140
|
export { STATUS_META } from './components/agent/status.ts'
|
|
133
141
|
export { ContextRing } from './components/agent/ContextRing.tsx'
|
|
134
142
|
|
package/src/styles/terminal.css
CHANGED
|
@@ -699,10 +699,18 @@ button.term-link {
|
|
|
699
699
|
/* The head's own strip: the full lane, out of flow, so the head needs no
|
|
700
700
|
footprint-cancelling margin - sticky confinement clamps the margin box, and
|
|
701
701
|
a negative margin shrinks it to nothing, letting the head overshoot the
|
|
702
|
-
lane's end by its own height (two pinned prompts on screen at once).
|
|
702
|
+
lane's end by its own height (two pinned prompts on screen at once).
|
|
703
|
+
|
|
704
|
+
The z-index is what makes the head's ground actually cover: the row it
|
|
705
|
+
overlays is a later sibling and carries `term-hoverable`, which is
|
|
706
|
+
`position: relative` for the row actions, so at `z-index: auto` the row's
|
|
707
|
+
text painted OVER the pinned head and the two read as one line of overlap.
|
|
708
|
+
Raising the strip - inside the lane's own stacking context, so it still
|
|
709
|
+
sits under nothing else - puts it back above the row it is there to hide. */
|
|
703
710
|
[data-terminal] [data-sticky-lane] > [data-sticky-headlane] {
|
|
704
711
|
position: absolute;
|
|
705
712
|
inset: 0;
|
|
713
|
+
z-index: 1;
|
|
706
714
|
pointer-events: none;
|
|
707
715
|
}
|
|
708
716
|
|