@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.
Files changed (145) hide show
  1. package/build/{SessionPanel-DgwjH4Ve.mjs → SessionPanel-DDgBkHsi.mjs} +2196 -3364
  2. package/build/SessionPanel-DDgBkHsi.mjs.map +1 -0
  3. package/build/SessionPanel-E_L_ldQ5.d.mts +207 -0
  4. package/build/format-B8ZM9LPy.d.mts +16 -0
  5. package/build/format.d.mts +8 -75
  6. package/build/format.mjs +2 -2
  7. package/build/index.d.mts +63 -1138
  8. package/build/index.mjs +4 -300
  9. package/build/index.mjs.map +1 -1
  10. package/build/scoped.css +80 -24
  11. package/build/{status-BE-zg88x.mjs → status-C0HSLZas.mjs} +14 -85
  12. package/build/status-C0HSLZas.mjs.map +1 -0
  13. package/build/workspace.d.mts +9 -124
  14. package/build/workspace.mjs +13 -143
  15. package/build/workspace.mjs.map +1 -1
  16. package/package.json +4 -4
  17. package/src/components/agent/CodeEditor.tsx +31 -103
  18. package/src/components/agent/Composer.tsx +114 -333
  19. package/src/components/agent/ContextDialog.tsx +20 -41
  20. package/src/components/agent/ContextRing.tsx +6 -28
  21. package/src/components/agent/Conversation.tsx +21 -33
  22. package/src/components/agent/EditorTabs.tsx +39 -67
  23. package/src/components/agent/EngineIcon.tsx +29 -72
  24. package/src/components/agent/EntryIcon.tsx +16 -0
  25. package/src/components/agent/FileCard.tsx +10 -16
  26. package/src/components/agent/FileTree.tsx +52 -125
  27. package/src/components/agent/FileViewer.tsx +30 -60
  28. package/src/components/agent/HostFilesDialog.tsx +40 -69
  29. package/src/components/agent/Loader.tsx +3 -24
  30. package/src/components/agent/McpDialog.tsx +64 -137
  31. package/src/components/agent/Message.tsx +2 -21
  32. package/src/components/agent/ModelSelect.tsx +33 -62
  33. package/src/components/agent/PermissionModeSelect.tsx +21 -79
  34. package/src/components/agent/PermissionPrompt.tsx +59 -63
  35. package/src/components/agent/ProjectIcon.tsx +228 -93
  36. package/src/components/agent/PromptTokenText.tsx +10 -16
  37. package/src/components/agent/QuestionPrompt.tsx +43 -64
  38. package/src/components/agent/Reasoning.tsx +13 -16
  39. package/src/components/agent/Response.tsx +4 -6
  40. package/src/components/agent/Scrubber.tsx +31 -80
  41. package/src/components/agent/SessionBrowser.tsx +64 -243
  42. package/src/components/agent/SessionEmptyState.tsx +10 -40
  43. package/src/components/agent/SessionInfoDialog.tsx +46 -78
  44. package/src/components/agent/SessionItem.tsx +53 -320
  45. package/src/components/agent/SessionList.tsx +23 -39
  46. package/src/components/agent/SessionPanel.tsx +345 -923
  47. package/src/components/agent/SessionStatusIcon.tsx +14 -28
  48. package/src/components/agent/SessionSteps.tsx +34 -187
  49. package/src/components/agent/SessionWorkspace.tsx +108 -215
  50. package/src/components/agent/SkillsDialog.tsx +35 -80
  51. package/src/components/agent/StatusBar.tsx +51 -136
  52. package/src/components/agent/SubagentStrip.tsx +12 -77
  53. package/src/components/agent/ToolCallCard.tsx +94 -135
  54. package/src/components/agent/Transcript.tsx +103 -1288
  55. package/src/components/agent/TranscriptItemView.tsx +155 -0
  56. package/src/components/agent/TranscriptRows.tsx +418 -0
  57. package/src/components/agent/UsageDialog.tsx +7 -29
  58. package/src/components/agent/UsageMeters.tsx +18 -73
  59. package/src/components/agent/pulse.tsx +9 -32
  60. package/src/components/agent/scrubber-marks.ts +33 -103
  61. package/src/components/agent/status.ts +1 -5
  62. package/src/components/agent/tool-result-fetch.tsx +3 -25
  63. package/src/components/agent/tool-result-image.tsx +32 -111
  64. package/src/components/agent/transcript-rows.ts +41 -118
  65. package/src/components/agent/transcript-variant.tsx +3 -80
  66. package/src/components/agent/use-height-epoch.ts +10 -24
  67. package/src/components/agent/use-path-links.ts +45 -63
  68. package/src/components/agent/use-subagent-frame.ts +106 -0
  69. package/src/components/agent/use-transcript-jumps.ts +49 -107
  70. package/src/components/prompt-area/animated-placeholder.tsx +6 -5
  71. package/src/components/prompt-area/clipboard-helpers.ts +26 -21
  72. package/src/components/prompt-area/cursor-helpers.ts +79 -37
  73. package/src/components/prompt-area/dom-helpers.ts +97 -69
  74. package/src/components/prompt-area/file-strip.tsx +90 -80
  75. package/src/components/prompt-area/html-to-markdown.ts +67 -46
  76. package/src/components/prompt-area/image-strip.tsx +8 -18
  77. package/src/components/prompt-area/index.ts +2 -15
  78. package/src/components/prompt-area/prompt-area-engine.ts +100 -115
  79. package/src/components/prompt-area/prompt-area-list-ops.ts +66 -59
  80. package/src/components/prompt-area/prompt-area.tsx +31 -38
  81. package/src/components/prompt-area/remove-button.tsx +3 -9
  82. package/src/components/prompt-area/segment-helpers.ts +4 -12
  83. package/src/components/prompt-area/trigger-popover.tsx +15 -27
  84. package/src/components/prompt-area/trigger-presets.ts +0 -10
  85. package/src/components/prompt-area/types.ts +3 -8
  86. package/src/components/prompt-area/use-chip-editing.ts +305 -0
  87. package/src/components/prompt-area/use-markdown-mode.ts +7 -17
  88. package/src/components/prompt-area/use-prompt-area-events.ts +80 -86
  89. package/src/components/prompt-area/use-prompt-area-keydown.ts +598 -0
  90. package/src/components/prompt-area/use-prompt-area-state.ts +5 -23
  91. package/src/components/prompt-area/use-prompt-area.ts +170 -896
  92. package/src/components/prompt-area/use-trigger-search.ts +21 -19
  93. package/src/components/terminal/PermissionPrompt.tsx +56 -59
  94. package/src/components/terminal/QuestionPrompt.tsx +28 -79
  95. package/src/components/terminal/StatusLine.tsx +13 -63
  96. package/src/components/terminal/TerminalTranscript.tsx +44 -160
  97. package/src/components/terminal/affordances.tsx +68 -98
  98. package/src/components/terminal/blocks.ts +20 -156
  99. package/src/components/terminal/diff.tsx +19 -67
  100. package/src/components/terminal/height.ts +163 -313
  101. package/src/components/terminal/image-box.ts +0 -44
  102. package/src/components/terminal/items.tsx +150 -305
  103. package/src/components/terminal/markdown.tsx +36 -95
  104. package/src/components/terminal/press.tsx +26 -53
  105. package/src/components/terminal/prompt.tsx +40 -170
  106. package/src/components/terminal/result-preview.ts +8 -56
  107. package/src/components/terminal/row.tsx +11 -98
  108. package/src/components/terminal/scrubber.tsx +108 -401
  109. package/src/components/terminal/surface.tsx +6 -54
  110. package/src/components/terminal/todos.ts +66 -0
  111. package/src/components/terminal/tool-run.ts +21 -192
  112. package/src/components/ui/AlertDialog.tsx +8 -23
  113. package/src/components/ui/Badge.tsx +18 -29
  114. package/src/components/ui/Button.tsx +7 -15
  115. package/src/components/ui/Card.tsx +2 -13
  116. package/src/components/ui/CodeBlock.tsx +10 -37
  117. package/src/components/ui/CopyButton.tsx +10 -23
  118. package/src/components/ui/Dialog.tsx +23 -36
  119. package/src/components/ui/Empty.tsx +6 -29
  120. package/src/components/ui/Input.tsx +14 -16
  121. package/src/components/ui/Menu.tsx +6 -18
  122. package/src/components/ui/PortalScope.tsx +1 -21
  123. package/src/components/ui/ProgressRing.tsx +8 -19
  124. package/src/components/ui/Select.tsx +18 -31
  125. package/src/components/ui/Sonner.tsx +1 -2
  126. package/src/components/ui/Spinner.tsx +1 -1
  127. package/src/components/ui/Splitter.tsx +30 -64
  128. package/src/components/ui/Textarea.tsx +2 -5
  129. package/src/components/ui/Tooltip.tsx +3 -13
  130. package/src/format.ts +0 -9
  131. package/src/index.ts +16 -113
  132. package/src/lib/clipboard.ts +8 -28
  133. package/src/lib/css.ts +8 -0
  134. package/src/lib/format.ts +101 -86
  135. package/src/lib/plan-request.ts +16 -0
  136. package/src/lib/status.ts +45 -95
  137. package/src/lib/tool-icon.ts +39 -47
  138. package/src/lib/utils.ts +1 -3
  139. package/src/styles/terminal.css +81 -49
  140. package/src/styles/theme.css +136 -19
  141. package/src/workspace.ts +1 -24
  142. package/build/SessionPanel-13l25ubU.d.mts +0 -609
  143. package/build/SessionPanel-DgwjH4Ve.mjs.map +0 -1
  144. package/build/format-ljc3lKpA.d.mts +0 -59
  145. package/build/status-BE-zg88x.mjs.map +0 -1
@@ -1,56 +1,19 @@
1
- import {
2
- useCallback,
3
- useRef,
4
- type KeyboardEvent as ReactKeyboardEvent,
5
- type PointerEvent as ReactPointerEvent,
6
- } from 'react'
1
+ import { useCallback, useRef, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent } from 'react'
7
2
  import { cn } from '../../lib/utils.ts'
8
3
 
9
4
  export interface SplitterProps {
10
- /**
11
- * ARIA's sense of the word: a `vertical` splitter is a vertical bar between
12
- * two side-by-side panes, and it resizes a **width**. A `horizontal` one sits
13
- * between stacked panes and resizes a **height**.
14
- */
15
5
  orientation: 'vertical' | 'horizontal'
16
- /** Current size of the pane this splitter controls, in pixels. */
17
6
  value: number
18
7
  onValueChange: (value: number) => void
19
8
  min: number
20
9
  max: number
21
- /** Keyboard step. */
22
10
  step?: number
23
- /**
24
- * Size to snap back to on a double-click — the convention every pane divider
25
- * that can be dragged is expected to honour. Omit and a double-click does
26
- * nothing, which is the honest behaviour when there is no default to mean.
27
- */
28
11
  defaultValue?: number
29
- /** Set when dragging the splitter *away* from the origin should shrink the
30
- * controlled pane — i.e. the pane is on the right or the bottom. */
31
12
  inverted?: boolean
32
- /** Required: "Resize" alone does not say which of two splitters this is. */
33
13
  'aria-label': string
34
14
  className?: string
35
15
  }
36
16
 
37
- /**
38
- * A draggable pane divider.
39
- *
40
- * Hand-rolled rather than depended on: `@base-ui/react` ships no splitter, the
41
- * behaviour is a hundred lines of pointer events, and this repo's instinct at
42
- * this layer is to own it (the composer is vendored for the same reason).
43
- *
44
- * Pointer capture is what makes it survive a fast drag — without it the pointer
45
- * leaves the 5px bar within a frame and the moves go to whatever is underneath,
46
- * which for this layout is an iframe-free but still selection-happy code pane.
47
- * The drag origin is captured on pointerdown and every move is measured against
48
- * it, so the pane cannot drift relative to the cursor over a long drag the way
49
- * per-move deltas do once clamping is involved.
50
- *
51
- * Keyboard-operable and announced as a separator, because a pane you can only
52
- * size by dragging is a pane some people cannot size.
53
- */
54
17
  export function Splitter({
55
18
  orientation,
56
19
  value,
@@ -69,8 +32,9 @@ export function Splitter({
69
32
  const clamp = useCallback((next: number) => Math.min(max, Math.max(min, next)), [min, max])
70
33
 
71
34
  const onPointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {
72
- // Secondary buttons open context menus; they are not drags.
73
- if (event.button !== 0) return
35
+ if (event.button !== 0) {
36
+ return
37
+ }
74
38
  event.preventDefault()
75
39
  event.currentTarget.setPointerCapture(event.pointerId)
76
40
  drag.current = { origin: vertical ? event.clientX : event.clientY, start: value }
@@ -78,43 +42,52 @@ export function Splitter({
78
42
 
79
43
  const onPointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {
80
44
  const state = drag.current
81
- if (!state) return
45
+ if (!state) {
46
+ return
47
+ }
82
48
  const delta = (vertical ? event.clientX : event.clientY) - state.origin
83
49
  onValueChange(clamp(state.start + (inverted ? -delta : delta)))
84
50
  }
85
51
 
86
52
  const endDrag = (event: ReactPointerEvent<HTMLDivElement>) => {
87
- if (!drag.current) return
53
+ if (!drag.current) {
54
+ return
55
+ }
88
56
  drag.current = null
89
57
  if (event.currentTarget.hasPointerCapture(event.pointerId)) {
90
58
  event.currentTarget.releasePointerCapture(event.pointerId)
91
59
  }
92
60
  }
93
61
 
94
- // The second click of a double-click has already started a drag, so the reset
95
- // has to land after `endDrag` — which it does: dblclick fires after pointerup.
62
+ // Reached after `endDrag` — the second click of a double-click has already started a drag, and dblclick fires after pointerup.
96
63
  const onDoubleClick = () => {
97
- if (defaultValue !== undefined) onValueChange(clamp(defaultValue))
64
+ if (defaultValue !== undefined) {
65
+ onValueChange(clamp(defaultValue))
66
+ }
98
67
  }
99
68
 
100
69
  const onKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {
101
- // The arrows that move *along* this splitter's axis of travel; the other
102
- // pair is left to the page, which is what a separator should do with them.
103
70
  const grow = vertical ? 'ArrowRight' : 'ArrowDown'
104
71
  const shrink = vertical ? 'ArrowLeft' : 'ArrowUp'
105
72
  const direction = inverted ? -1 : 1
106
- if (event.key === grow) onValueChange(clamp(value + step * direction))
107
- else if (event.key === shrink) onValueChange(clamp(value - step * direction))
108
- else if (event.key === 'Home') onValueChange(min)
109
- else if (event.key === 'End') onValueChange(max)
110
- else return
73
+ if (event.key === grow) {
74
+ onValueChange(clamp(value + step * direction))
75
+ } else if (event.key === shrink) {
76
+ onValueChange(clamp(value - step * direction))
77
+ } else if (event.key === 'Home') {
78
+ onValueChange(min)
79
+ } else if (event.key === 'End') {
80
+ onValueChange(max)
81
+ } else {
82
+ return
83
+ }
111
84
  event.preventDefault()
112
85
  }
113
86
 
114
87
  return (
115
88
  <div
116
- data-slot='splitter'
117
- role='separator'
89
+ data-slot="splitter"
90
+ role="separator"
118
91
  tabIndex={0}
119
92
  aria-label={label}
120
93
  aria-orientation={orientation}
@@ -128,20 +101,13 @@ export function Splitter({
128
101
  onDoubleClick={onDoubleClick}
129
102
  onKeyDown={onKeyDown}
130
103
  className={cn(
131
- // A 1px line that reads as a border, with a larger invisible grab area
132
- // around it — a hairline is an honest divider and a cruel target.
133
104
  'group relative shrink-0 touch-none bg-border transition-colors',
134
105
  'hover:bg-border-strong focus-visible:bg-accent focus-visible:outline-none',
135
106
  vertical ? 'w-px cursor-col-resize' : 'h-px cursor-row-resize',
136
107
  className,
137
- )}>
138
- <span
139
- aria-hidden
140
- className={cn(
141
- 'absolute',
142
- vertical ? '-inset-x-[3px] inset-y-0' : '-inset-y-[3px] inset-x-0',
143
- )}
144
- />
108
+ )}
109
+ >
110
+ <span aria-hidden className={cn('absolute', vertical ? '-inset-x-[3px] inset-y-0' : '-inset-y-[3px] inset-x-0')} />
145
111
  </div>
146
112
  )
147
113
  }
@@ -1,13 +1,10 @@
1
1
  import { type TextareaHTMLAttributes, forwardRef } from 'react'
2
2
  import { cn } from '../../lib/utils.ts'
3
3
 
4
- export const Textarea = forwardRef<
5
- HTMLTextAreaElement,
6
- TextareaHTMLAttributes<HTMLTextAreaElement>
7
- >(({ className, ...props }, ref) => (
4
+ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>(({ className, ...props }, ref) => (
8
5
  <textarea
9
6
  ref={ref}
10
- data-slot='textarea'
7
+ data-slot="textarea"
11
8
  className={cn(
12
9
  'w-full resize-none rounded-md border border-border bg-bg px-2.5 py-2 text-body-sm text-text',
13
10
  'placeholder:text-fg-4 transition-colors outline-none',
@@ -10,9 +10,9 @@ export const TooltipContent: FunctionComponent<
10
10
  > = ({ className, side = 'top', sideOffset = 6, ...props }) => (
11
11
  <TooltipPrimitive.Portal>
12
12
  <PortalScope>
13
- <TooltipPrimitive.Positioner side={side} sideOffset={sideOffset} className='isolate z-90'>
13
+ <TooltipPrimitive.Positioner side={side} sideOffset={sideOffset} className="isolate z-90">
14
14
  <TooltipPrimitive.Popup
15
- data-slot='tooltip-content'
15
+ data-slot="tooltip-content"
16
16
  className={cn(
17
17
  'rounded-md border border-border bg-surface px-2 py-1 text-label text-fg-2 shadow-(--shadow-md) outline-none',
18
18
  className,
@@ -24,14 +24,6 @@ export const TooltipContent: FunctionComponent<
24
24
  </TooltipPrimitive.Portal>
25
25
  )
26
26
 
27
- /**
28
- * Convenience wrapper: `<Tip content="..."><Button/></Tip>`.
29
- *
30
- * Pass `render` when the trigger must *be* an element you already have — a tab, a
31
- * row — rather than something wrapped in a span. The default span is fine beside
32
- * a button but would break any layout that styles its own children (a flex tab
33
- * strip gets an extra box between the container and its items).
34
- */
35
27
  export function Tip({
36
28
  content,
37
29
  render,
@@ -45,9 +37,7 @@ export function Tip({
45
37
  }) {
46
38
  return (
47
39
  <TooltipPrimitive.Root>
48
- <TooltipPrimitive.Trigger render={render ?? <span className='inline-flex' />}>
49
- {children}
50
- </TooltipPrimitive.Trigger>
40
+ <TooltipPrimitive.Trigger render={render ?? <span className="inline-flex" />}>{children}</TooltipPrimitive.Trigger>
51
41
  <TooltipContent side={side}>{content}</TooltipContent>
52
42
  </TooltipPrimitive.Root>
53
43
  )
package/src/format.ts CHANGED
@@ -1,11 +1,2 @@
1
- /**
2
- * The formatting helpers, on their own entry point.
3
- *
4
- * `src/index.ts` pulls in React and every component with it; a host that renders
5
- * session readings *outside* React — the VS Code extension host drawing them in
6
- * the window status bar, say — needs the numbers spelled the same way without
7
- * paying for that. These functions are pure and dependency-free, so the subpath
8
- * costs nothing and keeps one truth for what "45.2k" and "2h 10m" mean.
9
- */
10
1
  export * from './lib/format.ts'
11
2
  export * from './lib/status.ts'
package/src/index.ts CHANGED
@@ -1,17 +1,9 @@
1
- // Primitives
2
1
  export { Button, buttonVariants, type ButtonProps } from './components/ui/Button.tsx'
3
2
  export { Badge, badgeVariants, type BadgeProps } from './components/ui/Badge.tsx'
4
3
  export { Card, CardContent, CardHeader, CardTitle } from './components/ui/Card.tsx'
5
4
  export { Input } from './components/ui/Input.tsx'
6
5
  export { Textarea } from './components/ui/Textarea.tsx'
7
- export {
8
- Select,
9
- SelectContent,
10
- SelectItem,
11
- SelectItemText,
12
- SelectTrigger,
13
- SelectValue,
14
- } from './components/ui/Select.tsx'
6
+ export { Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue } from './components/ui/Select.tsx'
15
7
  export {
16
8
  AlertDialog,
17
9
  AlertDialogClose,
@@ -20,22 +12,8 @@ export {
20
12
  AlertDialogTitle,
21
13
  AlertDialogTrigger,
22
14
  } from './components/ui/AlertDialog.tsx'
23
- export {
24
- Menu,
25
- MenuContent,
26
- MenuItem,
27
- MenuSeparator,
28
- MenuTrigger,
29
- } from './components/ui/Menu.tsx'
30
- export {
31
- Dialog,
32
- DialogBody,
33
- DialogClose,
34
- DialogContent,
35
- DialogHeader,
36
- DialogRow,
37
- DialogTrigger,
38
- } from './components/ui/Dialog.tsx'
15
+ export { Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger } from './components/ui/Menu.tsx'
16
+ export { Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger } from './components/ui/Dialog.tsx'
39
17
  export { Tip, TooltipContent, TooltipProvider } from './components/ui/Tooltip.tsx'
40
18
  export { Toaster, toast } from './components/ui/Sonner.tsx'
41
19
  export { CopyButton, type CopyButtonProps } from './components/ui/CopyButton.tsx'
@@ -44,7 +22,6 @@ export { CodeBlock, type CodeBlockProps } from './components/ui/CodeBlock.tsx'
44
22
  export { Splitter, type SplitterProps } from './components/ui/Splitter.tsx'
45
23
  export { Empty, EmptyKey } from './components/ui/Empty.tsx'
46
24
  export { ProgressRing, type ProgressRingProps } from './components/ui/ProgressRing.tsx'
47
- // Prompt input (vendored just-marketing/prompt-area, themed to these tokens)
48
25
  export {
49
26
  PromptArea,
50
27
  usePromptAreaState,
@@ -64,7 +41,6 @@ export {
64
41
  type TriggerSuggestion,
65
42
  } from './components/prompt-area/index.ts'
66
43
 
67
- // Agent-control components
68
44
  export {
69
45
  SessionPanel,
70
46
  type SessionControls,
@@ -73,48 +49,17 @@ export {
73
49
  type SessionVitals,
74
50
  type TerminalMetrics,
75
51
  } from './components/agent/SessionPanel.tsx'
76
- // The workspace layout and its Monaco editor live at `@workerdeck/ui/workspace`
77
- // — deliberately unreachable from here, so importing this entry never drags
78
- // Monaco into the bundle. See `src/workspace.ts`.
79
52
  export { Transcript, type TranscriptProps } from './components/agent/Transcript.tsx'
80
- // The terminal theme. `SessionPanel`/`Transcript` reach it through
81
- // `variant: 'terminal'` and most embedders need nothing else — these are for a
82
- // host composing the surface by hand (a prompt outside the panel, a status line
83
- // in its own chrome) and for the primitives, so a host's own row lands on the
84
- // same character cell as ours.
85
- export {
86
- TerminalSurface,
87
- type TerminalSurfaceProps,
88
- } from './components/terminal/surface.tsx'
89
- export {
90
- TerminalTranscript,
91
- TerminalItemView,
92
- type TerminalTranscriptProps,
93
- } from './components/terminal/TerminalTranscript.tsx'
53
+ export { TerminalSurface, type TerminalSurfaceProps } from './components/terminal/surface.tsx'
54
+ export { TerminalTranscript, TerminalItemView, type TerminalTranscriptProps } from './components/terminal/TerminalTranscript.tsx'
94
55
  export { TerminalStatusLine, type TerminalStatusLineProps } from './components/terminal/StatusLine.tsx'
95
- export {
96
- TerminalPermissionPrompt,
97
- type TerminalPermissionPromptProps,
98
- } from './components/terminal/PermissionPrompt.tsx'
99
- export {
100
- TerminalQuestionPrompt,
101
- type TerminalQuestionPromptProps,
102
- } from './components/terminal/QuestionPrompt.tsx'
56
+ export { TerminalPermissionPrompt, type TerminalPermissionPromptProps } from './components/terminal/PermissionPrompt.tsx'
57
+ export { TerminalQuestionPrompt, type TerminalQuestionPromptProps } from './components/terminal/QuestionPrompt.tsx'
103
58
  export { TerminalDiff, previewPatch } from './components/terminal/diff.tsx'
104
59
  export { TerminalMarkdown, type TerminalMarkdownProps } from './components/terminal/markdown.tsx'
105
60
  export { Band, Blank, Ink, Row, type RowProps, type Tone } from './components/terminal/row.tsx'
106
- export {
107
- CopyAction,
108
- WithActions,
109
- useAffordances,
110
- type TerminalAffordances,
111
- } from './components/terminal/affordances.tsx'
112
- export {
113
- Conversation,
114
- ConversationContent,
115
- ConversationScrollButton,
116
- type ConversationProps,
117
- } from './components/agent/Conversation.tsx'
61
+ export { CopyAction, WithActions, useAffordances, type TerminalAffordances } from './components/terminal/affordances.tsx'
62
+ export { Conversation, ConversationContent, ConversationScrollButton, type ConversationProps } from './components/agent/Conversation.tsx'
118
63
  export { Message, MessageContent, type MessageProps } from './components/agent/Message.tsx'
119
64
  export {
120
65
  TranscriptDensityProvider,
@@ -138,13 +83,7 @@ export {
138
83
  type QuestionPromptProps,
139
84
  type QuestionBehaviorMeta,
140
85
  } from './components/agent/QuestionPrompt.tsx'
141
- export {
142
- Composer,
143
- skillPrompt,
144
- type ComposerFileMatch,
145
- type ComposerHandle,
146
- type ComposerProps,
147
- } from './components/agent/Composer.tsx'
86
+ export { Composer, skillPrompt, type ComposerFileMatch, type ComposerHandle, type ComposerProps } from './components/agent/Composer.tsx'
148
87
  export { ModelSelect, type ModelSelectProps } from './components/agent/ModelSelect.tsx'
149
88
  export {
150
89
  PERMISSION_MODES,
@@ -159,59 +98,23 @@ export { StatusBar, type StatusBarProps } from './components/agent/StatusBar.tsx
159
98
  export { ContextDialog, type ContextDialogProps } from './components/agent/ContextDialog.tsx'
160
99
  export { UsageDialog, type UsageDialogProps } from './components/agent/UsageDialog.tsx'
161
100
  export { UsageMeters, useMinuteClock } from './components/agent/UsageMeters.tsx'
162
- export {
163
- SessionInfoDialog,
164
- type SessionInfoDialogProps,
165
- } from './components/agent/SessionInfoDialog.tsx'
101
+ export { SessionInfoDialog, type SessionInfoDialogProps } from './components/agent/SessionInfoDialog.tsx'
166
102
  export { McpDialog, type McpDialogProps } from './components/agent/McpDialog.tsx'
167
103
  export { SkillsDialog, type SkillsDialogProps } from './components/agent/SkillsDialog.tsx'
168
104
  export { HostFilesDialog, type HostFilesDialogProps } from './components/agent/HostFilesDialog.tsx'
169
- export {
170
- SessionList,
171
- SessionListItem,
172
- type SessionListItemProps,
173
- type SessionListProps,
174
- } from './components/agent/SessionList.tsx'
175
- export {
176
- SessionBrowser,
177
- rowShapeClass,
178
- type SessionBrowserProps,
179
- } from './components/agent/SessionBrowser.tsx'
180
- // The session card itself — one component, every client. `SessionBrowser` is the
181
- // dashboard's list *around* it; a host that wants only the card (the VS Code
182
- // sidebar does, because the filtering chrome above it is native) takes this.
105
+ export { SessionList, SessionListItem, type SessionListItemProps, type SessionListProps } from './components/agent/SessionList.tsx'
106
+ export { SessionBrowser, rowShapeClass, type SessionBrowserProps } from './components/agent/SessionBrowser.tsx'
183
107
  export { SessionItem, type SessionItemProps } from './components/agent/SessionItem.tsx'
184
108
  export { SessionStatusIcon } from './components/agent/SessionStatusIcon.tsx'
185
- // Lifted out of the VS Code sidebar once the dashboard grew a collapsed rail
186
- // that needs the same glyph two copies of a trademark set is one too many.
187
- export {
188
- EngineIcon,
189
- engineMark,
190
- vendorMarkClass,
191
- vendorTextClass,
192
- } from './components/agent/EngineIcon.tsx'
193
- // Lifted out of the sidebar for the same reason: the work under a row is a
194
- // protocol fact, so every list annotates it with one component.
195
- export {
196
- type Step,
197
- StepRow,
198
- StepToggle,
199
- runningSteps,
200
- sessionSteps,
201
- } from './components/agent/SessionSteps.tsx'
202
- // The sub-agent takeover's one line. Exported for hosts that draw their own
203
- // panel chrome; the panel raises it itself.
109
+ export { EngineIcon, engineMark, vendorMarkClass, vendorTextClass } from './components/agent/EngineIcon.tsx'
110
+ export { type Step, StepRow, StepToggle, runningSteps, sessionSteps } from './components/agent/SessionSteps.tsx'
204
111
  export { SubagentStrip } from './components/agent/SubagentStrip.tsx'
205
112
  export { ProjectIcon } from './components/agent/ProjectIcon.tsx'
206
- export {
207
- SessionEmptyState,
208
- type SessionEmptyStateProps,
209
- } from './components/agent/SessionEmptyState.tsx'
113
+ export { SessionEmptyState, type SessionEmptyStateProps } from './components/agent/SessionEmptyState.tsx'
210
114
  export { PromptTokenText } from './components/agent/PromptTokenText.tsx'
211
115
  export { STATUS_META } from './components/agent/status.ts'
212
116
  export { ContextRing } from './components/agent/ContextRing.tsx'
213
117
 
214
- // Utilities
215
118
  export { cn } from './lib/utils.ts'
216
119
  export { copyText } from './lib/clipboard.ts'
217
120
  export { isMutatingTool, toolIcon } from './lib/tool-icon.ts'
@@ -1,47 +1,25 @@
1
- /**
2
- * Copy text to the clipboard, on origins where the modern API does not exist.
3
- *
4
- * `navigator.clipboard` is gated on a **secure context**: HTTPS, or localhost.
5
- * A WorkerDeck dashboard reached the way it is meant to be reached — plain HTTP
6
- * on a LAN address, from a laptop or a phone — is neither, so `navigator.clipboard`
7
- * is `undefined` there and touching `.writeText` throws outright. That is the
8
- * normal deployment, not an edge case, which is why this falls back rather than
9
- * feature-detecting into a disabled button.
10
- *
11
- * The fallback is `document.execCommand('copy')` over an off-screen textarea.
12
- * It is deprecated and it is also the only thing that works here; every browser
13
- * still implements it. Returns whether the text actually landed, so a caller can
14
- * avoid claiming success it did not have.
15
- */
16
1
  export async function copyText(value: string): Promise<boolean> {
17
- // Optional-chained, not `in`-checked: on an insecure origin the property is
18
- // absent entirely, and some embedded webviews expose a `clipboard` object
19
- // whose `writeText` rejects. Both end up in the fallback.
2
+ // Optional-chained, not `in`-checked: some embedded webviews expose a `clipboard` object whose `writeText` rejects.
20
3
  try {
21
4
  if (navigator.clipboard?.writeText) {
22
5
  await navigator.clipboard.writeText(value)
23
6
  return true
24
7
  }
25
- } catch {
26
- // Permission denied, or a webview that lied about having the API.
27
- }
8
+ } catch {}
28
9
  return legacyCopy(value)
29
10
  }
30
11
 
31
12
  function legacyCopy(value: string): boolean {
32
- if (typeof document === 'undefined') return false
13
+ if (typeof document === 'undefined') {
14
+ return false
15
+ }
33
16
  const textarea = document.createElement('textarea')
34
17
  textarea.value = value
35
- // Off-screen rather than hidden: `display: none` and `visibility: hidden`
36
- // elements cannot hold a selection, so the copy would silently do nothing.
37
- // `readOnly` keeps the mobile keyboard from appearing for the instant it exists.
38
18
  textarea.setAttribute('readonly', '')
39
19
  textarea.style.position = 'fixed'
40
20
  textarea.style.top = '-9999px'
41
21
  textarea.style.opacity = '0'
42
22
  document.body.appendChild(textarea)
43
- // Preserve where the user was: selecting steals the current selection, and on
44
- // a text field mid-edit that is visible and annoying.
45
23
  const previous = document.activeElement
46
24
  try {
47
25
  textarea.select()
@@ -51,6 +29,8 @@ function legacyCopy(value: string): boolean {
51
29
  return false
52
30
  } finally {
53
31
  document.body.removeChild(textarea)
54
- if (previous instanceof HTMLElement) previous.focus()
32
+ if (previous instanceof HTMLElement) {
33
+ previous.focus()
34
+ }
55
35
  }
56
36
  }
package/src/lib/css.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pass a colour through only if this engine can actually parse it. Category colours arrive
3
+ * from the CLI as untrusted strings, and an unparseable one must fall back to the token
4
+ * colour rather than reaching the DOM.
5
+ */
6
+ export function cssColor(color: string): string | undefined {
7
+ return typeof CSS !== 'undefined' && CSS.supports('color', color) ? color : undefined
8
+ }