@workerdeck/ui 3.4.0 → 3.5.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/index.d.mts +55 -4
- package/build/index.mjs +592 -157
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +88 -0
- package/package.json +4 -4
- package/src/components/agent/AgentAvatar.tsx +117 -0
- package/src/components/agent/SessionBrowser.tsx +243 -42
- package/src/components/agent/SessionGroups.tsx +130 -20
- package/src/components/agent/SessionItem.tsx +180 -69
- package/src/index.ts +11 -0
- package/src/lib/team-drop.ts +65 -0
- package/src/styles/theme.css +17 -0
package/build/scoped.css
CHANGED
|
@@ -311,18 +311,27 @@
|
|
|
311
311
|
.wd-root .right-4 {
|
|
312
312
|
right: calc(var(--spacing) * 4);
|
|
313
313
|
}
|
|
314
|
+
.wd-root .-bottom-1 {
|
|
315
|
+
bottom: calc(var(--spacing) * -1);
|
|
316
|
+
}
|
|
314
317
|
.wd-root .bottom-0 {
|
|
315
318
|
bottom: 0px;
|
|
316
319
|
}
|
|
317
320
|
.wd-root .bottom-2 {
|
|
318
321
|
bottom: calc(var(--spacing) * 2);
|
|
319
322
|
}
|
|
323
|
+
.wd-root .bottom-3 {
|
|
324
|
+
bottom: calc(var(--spacing) * 3);
|
|
325
|
+
}
|
|
320
326
|
.wd-root .bottom-4 {
|
|
321
327
|
bottom: calc(var(--spacing) * 4);
|
|
322
328
|
}
|
|
323
329
|
.wd-root .bottom-full {
|
|
324
330
|
bottom: 100%;
|
|
325
331
|
}
|
|
332
|
+
.wd-root .-left-\[7px\] {
|
|
333
|
+
left: calc(7px * -1);
|
|
334
|
+
}
|
|
326
335
|
.wd-root .left-0 {
|
|
327
336
|
left: 0px;
|
|
328
337
|
}
|
|
@@ -455,6 +464,12 @@
|
|
|
455
464
|
.wd-root .ml-1 {
|
|
456
465
|
margin-left: var(--spacing);
|
|
457
466
|
}
|
|
467
|
+
.wd-root .ml-3 {
|
|
468
|
+
margin-left: calc(var(--spacing) * 3);
|
|
469
|
+
}
|
|
470
|
+
.wd-root .ml-3\.5 {
|
|
471
|
+
margin-left: calc(var(--spacing) * 3.5);
|
|
472
|
+
}
|
|
458
473
|
.wd-root .ml-auto {
|
|
459
474
|
margin-left: auto;
|
|
460
475
|
}
|
|
@@ -501,6 +516,10 @@
|
|
|
501
516
|
width: calc(var(--spacing) * 1.5);
|
|
502
517
|
height: calc(var(--spacing) * 1.5);
|
|
503
518
|
}
|
|
519
|
+
.wd-root .size-1\/2 {
|
|
520
|
+
width: calc(1 / 2 * 100%);
|
|
521
|
+
height: calc(1 / 2 * 100%);
|
|
522
|
+
}
|
|
504
523
|
.wd-root .size-2 {
|
|
505
524
|
width: calc(var(--spacing) * 2);
|
|
506
525
|
height: calc(var(--spacing) * 2);
|
|
@@ -1003,6 +1022,9 @@
|
|
|
1003
1022
|
.wd-root .rounded {
|
|
1004
1023
|
border-radius: 0.25rem;
|
|
1005
1024
|
}
|
|
1025
|
+
.wd-root .rounded-\[3px\] {
|
|
1026
|
+
border-radius: 3px;
|
|
1027
|
+
}
|
|
1006
1028
|
.wd-root .rounded-\[4px\] {
|
|
1007
1029
|
border-radius: 4px;
|
|
1008
1030
|
}
|
|
@@ -1064,6 +1086,12 @@
|
|
|
1064
1086
|
.wd-root .border-\[var\(--wd-shell-accent\)\] {
|
|
1065
1087
|
border-color: var(--wd-shell-accent);
|
|
1066
1088
|
}
|
|
1089
|
+
.wd-root .border-accent\/50 {
|
|
1090
|
+
border-color: var(--accent);
|
|
1091
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1092
|
+
border-color: color-mix(in oklab, var(--accent) 50%, transparent);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1067
1095
|
.wd-root .border-border {
|
|
1068
1096
|
border-color: var(--border);
|
|
1069
1097
|
}
|
|
@@ -1139,6 +1167,12 @@
|
|
|
1139
1167
|
.wd-root .bg-accent-bg {
|
|
1140
1168
|
background-color: var(--accent-bg);
|
|
1141
1169
|
}
|
|
1170
|
+
.wd-root .bg-accent\/10 {
|
|
1171
|
+
background-color: var(--accent);
|
|
1172
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1173
|
+
background-color: color-mix(in oklab, var(--accent) 10%, transparent);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1142
1176
|
.wd-root .bg-background {
|
|
1143
1177
|
background-color: var(--background);
|
|
1144
1178
|
}
|
|
@@ -1352,6 +1386,9 @@
|
|
|
1352
1386
|
.wd-root .p-px {
|
|
1353
1387
|
padding: 1px;
|
|
1354
1388
|
}
|
|
1389
|
+
.wd-root .px-0\.5 {
|
|
1390
|
+
padding-inline: calc(var(--spacing) * 0.5);
|
|
1391
|
+
}
|
|
1355
1392
|
.wd-root .px-1 {
|
|
1356
1393
|
padding-inline: var(--spacing);
|
|
1357
1394
|
}
|
|
@@ -1475,6 +1512,9 @@
|
|
|
1475
1512
|
.wd-root .pb-\[var\(--wd-composer-padding\)\] {
|
|
1476
1513
|
padding-bottom: var(--wd-composer-padding);
|
|
1477
1514
|
}
|
|
1515
|
+
.wd-root .pl-1 {
|
|
1516
|
+
padding-left: var(--spacing);
|
|
1517
|
+
}
|
|
1478
1518
|
.wd-root .pl-1\.5 {
|
|
1479
1519
|
padding-left: calc(var(--spacing) * 1.5);
|
|
1480
1520
|
}
|
|
@@ -1571,9 +1611,16 @@
|
|
|
1571
1611
|
.wd-root .text-\[0\.75rem\] {
|
|
1572
1612
|
font-size: 0.75rem;
|
|
1573
1613
|
}
|
|
1614
|
+
.wd-root .text-\[0\.625rem\] {
|
|
1615
|
+
font-size: 0.625rem;
|
|
1616
|
+
}
|
|
1574
1617
|
.wd-root .text-\[9px\] {
|
|
1575
1618
|
font-size: 9px;
|
|
1576
1619
|
}
|
|
1620
|
+
.wd-root .leading-3\.5 {
|
|
1621
|
+
--tw-leading: calc(var(--spacing) * 3.5);
|
|
1622
|
+
line-height: calc(var(--spacing) * 3.5);
|
|
1623
|
+
}
|
|
1577
1624
|
.wd-root .leading-4 {
|
|
1578
1625
|
--tw-leading: calc(var(--spacing) * 4);
|
|
1579
1626
|
line-height: calc(var(--spacing) * 4);
|
|
@@ -1790,6 +1837,10 @@
|
|
|
1790
1837
|
--tw-shadow: var(--shadow-xs);
|
|
1791
1838
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1792
1839
|
}
|
|
1840
|
+
.wd-root .shadow-\[inset_0_-2px_0_0_var\(--color-accent\)\] {
|
|
1841
|
+
--tw-shadow: inset 0 -2px 0 0 var(--tw-shadow-color, var(--color-accent));
|
|
1842
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1843
|
+
}
|
|
1793
1844
|
.wd-root .shadow-\[inset_0_2px_0_0_var\(--color-accent\)\] {
|
|
1794
1845
|
--tw-shadow: inset 0 2px 0 0 var(--tw-shadow-color, var(--color-accent));
|
|
1795
1846
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
@@ -1810,10 +1861,23 @@
|
|
|
1810
1861
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
1811
1862
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1812
1863
|
}
|
|
1864
|
+
.wd-root .ring-1 {
|
|
1865
|
+
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
1866
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1867
|
+
}
|
|
1813
1868
|
.wd-root .ring-2 {
|
|
1814
1869
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
1815
1870
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1816
1871
|
}
|
|
1872
|
+
.wd-root .ring-accent {
|
|
1873
|
+
--tw-ring-color: var(--accent);
|
|
1874
|
+
}
|
|
1875
|
+
.wd-root .ring-danger\/60 {
|
|
1876
|
+
--tw-ring-color: var(--danger);
|
|
1877
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1878
|
+
--tw-ring-color: color-mix(in oklab, var(--danger) 60%, transparent);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1817
1881
|
.wd-root .ring-ring\/30 {
|
|
1818
1882
|
--tw-ring-color: var(--ring);
|
|
1819
1883
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -1828,6 +1892,10 @@
|
|
|
1828
1892
|
--tw-blur: blur(8px);
|
|
1829
1893
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1830
1894
|
}
|
|
1895
|
+
.wd-root .grayscale {
|
|
1896
|
+
--tw-grayscale: grayscale(100%);
|
|
1897
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1898
|
+
}
|
|
1831
1899
|
.wd-root .backdrop-blur-\[1px\] {
|
|
1832
1900
|
--tw-backdrop-blur: blur(1px);
|
|
1833
1901
|
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
|
|
@@ -1906,6 +1974,9 @@
|
|
|
1906
1974
|
.wd-root .\[counter-reset\:line\] {
|
|
1907
1975
|
counter-reset: line;
|
|
1908
1976
|
}
|
|
1977
|
+
.wd-root .\[image-rendering\:pixelated\] {
|
|
1978
|
+
image-rendering: pixelated;
|
|
1979
|
+
}
|
|
1909
1980
|
@media (hover: hover) {
|
|
1910
1981
|
.wd-root .group-hover\:block:is(:where(.group):hover *) {
|
|
1911
1982
|
display: block;
|
|
@@ -3424,6 +3495,23 @@
|
|
|
3424
3495
|
opacity: 0;
|
|
3425
3496
|
animation: wd-hold-appear 150ms ease-out 600ms forwards;
|
|
3426
3497
|
}
|
|
3498
|
+
@keyframes wd-avatar-strip {
|
|
3499
|
+
to {
|
|
3500
|
+
background-position-x: var(--wd-strip-end);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
.wd-root .wd-avatar-strip {
|
|
3504
|
+
image-rendering: pixelated;
|
|
3505
|
+
background-repeat: no-repeat;
|
|
3506
|
+
animation-name: wd-avatar-strip;
|
|
3507
|
+
animation-timing-function: steps(var(--wd-strip-steps));
|
|
3508
|
+
animation-iteration-count: infinite;
|
|
3509
|
+
}
|
|
3510
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3511
|
+
.wd-root .wd-avatar-strip {
|
|
3512
|
+
animation: none;
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3427
3515
|
@keyframes prompt-area-chip-ripple {
|
|
3428
3516
|
0% {
|
|
3429
3517
|
transform: scale(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workerdeck/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Styled agent-control component library for WorkerDeck: session panel, transcript with streaming, tool-call cards, permission prompts, session list, composer. Tailwind v4 + Base UI + cva. Pairs with the headless @workerdeck/react layer.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"streamdown": "^2.6.0",
|
|
40
40
|
"tailwind-merge": "^3.7.0",
|
|
41
41
|
"use-stick-to-bottom": "^1.1.6",
|
|
42
|
-
"@workerdeck/
|
|
43
|
-
"@workerdeck/
|
|
44
|
-
"@workerdeck/
|
|
42
|
+
"@workerdeck/protocol": "3.5.0",
|
|
43
|
+
"@workerdeck/client": "3.5.0",
|
|
44
|
+
"@workerdeck/react": "3.5.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"monaco-editor": "^0.56.0",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react'
|
|
2
|
+
import type { AgentRef, SessionRow, SessionState } from '@workerdeck/protocol'
|
|
3
|
+
import { EngineIcon, vendorMarkClass } from './EngineIcon.tsx'
|
|
4
|
+
import { SessionStatusIcon } from './SessionStatusIcon.tsx'
|
|
5
|
+
import { cn } from '../../lib/utils.ts'
|
|
6
|
+
|
|
7
|
+
export type AgentAvatarImage = { still: string; busy?: { src: string; durations: number[] } }
|
|
8
|
+
|
|
9
|
+
// Keyed by `AgentRef.avatar`, the gateway path: the host fetches with its own credentials and hands over data URLs.
|
|
10
|
+
export type AgentAvatars = Record<string, AgentAvatarImage>
|
|
11
|
+
|
|
12
|
+
export function avatarOf(avatars: AgentAvatars | undefined, agent: AgentRef | undefined): AgentAvatarImage | undefined {
|
|
13
|
+
return agent?.avatar === undefined ? undefined : avatars?.[agent.avatar]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AgentAvatarProps {
|
|
17
|
+
row: SessionRow
|
|
18
|
+
image?: AgentAvatarImage
|
|
19
|
+
size?: number
|
|
20
|
+
// The state the corner badge draws; a folded lead passes its team's most urgent one.
|
|
21
|
+
state?: SessionState
|
|
22
|
+
badge?: boolean
|
|
23
|
+
className?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function AgentAvatar({ row, image, size = 32, state, badge = true, className }: AgentAvatarProps) {
|
|
27
|
+
const { info } = row
|
|
28
|
+
const shown = state ?? row.state
|
|
29
|
+
const busy = shown === 'working' && image?.busy && image.busy.durations.length > 1 ? image.busy : undefined
|
|
30
|
+
const asleep = info.engineAsleep === true
|
|
31
|
+
const engine = info.engine ?? 'claude'
|
|
32
|
+
const badgeSize = size >= 28 ? 14 : 11
|
|
33
|
+
return (
|
|
34
|
+
<span
|
|
35
|
+
data-slot="agent-avatar"
|
|
36
|
+
className={cn('relative inline-flex shrink-0', className)}
|
|
37
|
+
style={{ width: size, height: size }}
|
|
38
|
+
title={info.agent?.name}
|
|
39
|
+
>
|
|
40
|
+
{busy ? (
|
|
41
|
+
<span
|
|
42
|
+
aria-hidden
|
|
43
|
+
className="wd-avatar-strip size-full rounded-[4px]"
|
|
44
|
+
style={
|
|
45
|
+
{
|
|
46
|
+
backgroundImage: `url(${busy.src})`,
|
|
47
|
+
backgroundSize: `${busy.durations.length * size}px ${size}px`,
|
|
48
|
+
'--wd-strip-end': `-${busy.durations.length * size}px`,
|
|
49
|
+
'--wd-strip-steps': busy.durations.length,
|
|
50
|
+
animationDuration: `${busy.durations.reduce((sum, ms) => sum + ms, 0)}ms`,
|
|
51
|
+
} as CSSProperties
|
|
52
|
+
}
|
|
53
|
+
/>
|
|
54
|
+
) : image ? (
|
|
55
|
+
<img
|
|
56
|
+
src={image.still}
|
|
57
|
+
alt=""
|
|
58
|
+
draggable={false}
|
|
59
|
+
className={cn('size-full rounded-[4px] [image-rendering:pixelated]', asleep && 'opacity-60 grayscale')}
|
|
60
|
+
/>
|
|
61
|
+
) : (
|
|
62
|
+
<span className="flex size-full items-center justify-center rounded-[4px] border border-dashed border-border">
|
|
63
|
+
<EngineIcon engine={engine} model={info.model} className={cn('size-1/2', vendorMarkClass(engine, info.model))} />
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
66
|
+
{badge && (shown !== 'idle' || asleep) ? (
|
|
67
|
+
<span
|
|
68
|
+
className="absolute -right-1 -bottom-1 flex items-center justify-center rounded-full bg-bg"
|
|
69
|
+
style={{ width: badgeSize, height: badgeSize }}
|
|
70
|
+
>
|
|
71
|
+
<SessionStatusIcon row={{ ...row, state: shown }} className="size-full p-px" />
|
|
72
|
+
</span>
|
|
73
|
+
) : null}
|
|
74
|
+
</span>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function LeadChip({ className }: { className?: string }) {
|
|
79
|
+
return (
|
|
80
|
+
<span
|
|
81
|
+
className={cn(
|
|
82
|
+
'shrink-0 rounded-[3px] border border-accent/50 px-1 text-[0.625rem] leading-3.5 font-medium tracking-wide text-accent',
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
>
|
|
86
|
+
LEAD
|
|
87
|
+
</span>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AgentHeadingProps {
|
|
92
|
+
row: SessionRow
|
|
93
|
+
image?: AgentAvatarImage
|
|
94
|
+
// Overrides `AgentRef.conversation`, which a gateway sends from the second conversation on.
|
|
95
|
+
conversation?: number
|
|
96
|
+
className?: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// A session panel's title for an agent: avatar, name, its place in a team. Plain sessions keep the host's own title.
|
|
100
|
+
export function AgentHeading({ row, image, conversation, className }: AgentHeadingProps) {
|
|
101
|
+
const agent = row.info.agent
|
|
102
|
+
if (!agent) {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
const n = conversation ?? agent.conversation ?? 1
|
|
106
|
+
const detail = [agent.lead !== undefined && agent.team ? `${agent.team} team` : undefined, `conversation ${n}`]
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
.join(' · ')
|
|
109
|
+
return (
|
|
110
|
+
<span data-slot="agent-heading" className={cn('flex min-w-0 items-center gap-2', className)}>
|
|
111
|
+
<AgentAvatar row={row} image={image} size={24} />
|
|
112
|
+
<span className="truncate text-body-sm font-medium text-fg-1">{agent.name}</span>
|
|
113
|
+
{agent.leads ? <LeadChip /> : null}
|
|
114
|
+
{detail ? <span className="shrink-0 text-label text-fg-4">{detail}</span> : null}
|
|
115
|
+
</span>
|
|
116
|
+
)
|
|
117
|
+
}
|