@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,66 +4,39 @@ import { cn } from '../../lib/utils.ts'
|
|
|
4
4
|
|
|
5
5
|
export interface CodeBlockProps {
|
|
6
6
|
code: string
|
|
7
|
-
/** Header label, e.g. a language or "Parameters". */
|
|
8
7
|
label?: ReactNode
|
|
9
8
|
copyable?: boolean
|
|
10
|
-
/**
|
|
11
|
-
* `'panel'` (default) is the framed card: border, header strip, rounded.
|
|
12
|
-
* `'plain'` is the terminal treatment — the label is a dim line, the code a
|
|
13
|
-
* flat band, and copy is a character. For surfaces where a box around every
|
|
14
|
-
* payload is more chrome than content (the transcript's `lines` variant).
|
|
15
|
-
*
|
|
16
|
-
* A prop rather than a read of the transcript's variant context: this is a
|
|
17
|
-
* `ui/` primitive and has no business knowing what a transcript is. The agent
|
|
18
|
-
* component that renders it does.
|
|
19
|
-
*/
|
|
20
9
|
variant?: 'panel' | 'plain'
|
|
21
10
|
className?: string
|
|
22
11
|
}
|
|
23
12
|
|
|
24
|
-
|
|
25
|
-
* inside assistant responses is highlighted by <Response> instead. */
|
|
26
|
-
export function CodeBlock({
|
|
27
|
-
code,
|
|
28
|
-
label,
|
|
29
|
-
copyable = true,
|
|
30
|
-
variant = 'panel',
|
|
31
|
-
className,
|
|
32
|
-
}: CodeBlockProps) {
|
|
13
|
+
export function CodeBlock({ code, label, copyable = true, variant = 'panel', className }: CodeBlockProps) {
|
|
33
14
|
const plain = variant === 'plain'
|
|
34
15
|
const header = label !== undefined || copyable
|
|
35
16
|
|
|
36
17
|
if (plain) {
|
|
37
18
|
return (
|
|
38
|
-
<div data-slot=
|
|
19
|
+
<div data-slot="code-block" data-variant="plain" className={cn('min-w-0', className)}>
|
|
39
20
|
{header ? (
|
|
40
|
-
<div className=
|
|
41
|
-
<span className=
|
|
42
|
-
{copyable ?
|
|
43
|
-
<CopyButton glyph value={code} className='size-5 shrink-0 rounded-sm' />
|
|
44
|
-
) : null}
|
|
21
|
+
<div className="flex items-baseline justify-between gap-2">
|
|
22
|
+
<span className="truncate text-label leading-5 text-fg-4">{label}</span>
|
|
23
|
+
{copyable ? <CopyButton glyph value={code} className="size-5 shrink-0 rounded-sm" /> : null}
|
|
45
24
|
</div>
|
|
46
25
|
) : null}
|
|
47
|
-
<pre className=
|
|
48
|
-
{code}
|
|
49
|
-
</pre>
|
|
26
|
+
<pre className="max-h-64 overflow-auto bg-code-bg px-2 font-mono text-label leading-5 whitespace-pre-wrap text-fg-2">{code}</pre>
|
|
50
27
|
</div>
|
|
51
28
|
)
|
|
52
29
|
}
|
|
53
30
|
|
|
54
31
|
return (
|
|
55
|
-
<div
|
|
56
|
-
data-slot='code-block'
|
|
57
|
-
className={cn('overflow-hidden rounded-md border border-border bg-code-bg', className)}>
|
|
32
|
+
<div data-slot="code-block" className={cn('overflow-hidden rounded-md border border-border bg-code-bg', className)}>
|
|
58
33
|
{header ? (
|
|
59
|
-
<div className=
|
|
60
|
-
<span className=
|
|
34
|
+
<div className="flex h-8 items-center justify-between border-b border-border px-2.5">
|
|
35
|
+
<span className="font-mono text-label text-fg-3">{label}</span>
|
|
61
36
|
{copyable ? <CopyButton value={code} /> : null}
|
|
62
37
|
</div>
|
|
63
38
|
) : null}
|
|
64
|
-
<pre className=
|
|
65
|
-
{code}
|
|
66
|
-
</pre>
|
|
39
|
+
<pre className="max-h-64 overflow-auto px-3 py-2 font-mono text-label whitespace-pre-wrap text-fg-2">{code}</pre>
|
|
67
40
|
</div>
|
|
68
41
|
)
|
|
69
42
|
}
|
|
@@ -6,47 +6,34 @@ import { cn } from '../../lib/utils.ts'
|
|
|
6
6
|
|
|
7
7
|
export interface CopyButtonProps extends Omit<ButtonProps, 'onClick' | 'children'> {
|
|
8
8
|
value: string
|
|
9
|
-
/** Draw the state as characters (`⧉` / `✓`) rather than line-art icons — for
|
|
10
|
-
* terminal-styled surfaces, where an SVG reads as another app's button. */
|
|
11
9
|
glyph?: boolean
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
export function CopyButton({
|
|
15
|
-
value,
|
|
16
|
-
glyph,
|
|
17
|
-
className,
|
|
18
|
-
variant = 'ghost',
|
|
19
|
-
size = 'icon-sm',
|
|
20
|
-
...props
|
|
21
|
-
}: CopyButtonProps) {
|
|
12
|
+
export function CopyButton({ value, glyph, className, variant = 'ghost', size = 'icon-sm', ...props }: CopyButtonProps) {
|
|
22
13
|
const [copied, setCopied] = useState(false)
|
|
23
14
|
return (
|
|
24
15
|
<Button
|
|
25
16
|
variant={variant}
|
|
26
17
|
size={size}
|
|
27
|
-
aria-label=
|
|
18
|
+
aria-label="Copy"
|
|
28
19
|
className={cn('text-fg-3', className)}
|
|
29
20
|
onClick={() => {
|
|
30
|
-
// Through `copyText`, which falls back for insecure origins — the
|
|
31
|
-
// dashboard on a LAN address has no `navigator.clipboard` at all, and
|
|
32
|
-
// reaching straight for `.writeText` there throws.
|
|
33
21
|
void copyText(value).then((ok) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
if (!ok) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
37
25
|
setCopied(true)
|
|
38
26
|
setTimeout(() => setCopied(false), 1500)
|
|
39
27
|
})
|
|
40
28
|
}}
|
|
41
|
-
{...props}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
42
31
|
{glyph ? (
|
|
43
|
-
<span className={cn('font-mono text-body-sm leading-5', copied && 'text-success')}>
|
|
44
|
-
{copied ? '✓' : '⧉'}
|
|
45
|
-
</span>
|
|
32
|
+
<span className={cn('font-mono text-body-sm leading-5', copied && 'text-success')}>{copied ? '✓' : '⧉'}</span>
|
|
46
33
|
) : copied ? (
|
|
47
|
-
<Check className=
|
|
34
|
+
<Check className="size-3.5 text-success" />
|
|
48
35
|
) : (
|
|
49
|
-
<Copy className=
|
|
36
|
+
<Copy className="size-3.5" />
|
|
50
37
|
)}
|
|
51
38
|
</Button>
|
|
52
39
|
)
|
|
@@ -8,16 +8,12 @@ export const Dialog = DialogPrimitive.Root
|
|
|
8
8
|
export const DialogTrigger = DialogPrimitive.Trigger
|
|
9
9
|
export const DialogClose = DialogPrimitive.Close
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*/
|
|
18
|
-
export const DialogContent: FunctionComponent<
|
|
19
|
-
DialogPrimitive.Popup.Props & { size?: 'sm' | 'md' | 'lg' }
|
|
20
|
-
> = ({ className, children, size = 'md', ...props }) => (
|
|
11
|
+
export const DialogContent: FunctionComponent<DialogPrimitive.Popup.Props & { size?: 'sm' | 'md' | 'lg' }> = ({
|
|
12
|
+
className,
|
|
13
|
+
children,
|
|
14
|
+
size = 'md',
|
|
15
|
+
...props
|
|
16
|
+
}) => (
|
|
21
17
|
<DialogPrimitive.Portal>
|
|
22
18
|
<PortalScope>
|
|
23
19
|
<DialogPrimitive.Backdrop
|
|
@@ -28,7 +24,7 @@ export const DialogContent: FunctionComponent<
|
|
|
28
24
|
)}
|
|
29
25
|
/>
|
|
30
26
|
<DialogPrimitive.Popup
|
|
31
|
-
data-slot=
|
|
27
|
+
data-slot="dialog-content"
|
|
32
28
|
className={cn(
|
|
33
29
|
'fixed top-1/2 left-1/2 z-70 flex max-h-[min(42rem,calc(100dvh-3rem))] -translate-x-1/2 -translate-y-1/2 flex-col',
|
|
34
30
|
size === 'sm' && 'w-[min(24rem,calc(100vw-2rem))]',
|
|
@@ -40,56 +36,47 @@ export const DialogContent: FunctionComponent<
|
|
|
40
36
|
'data-ending-style:scale-95 data-ending-style:opacity-0',
|
|
41
37
|
className,
|
|
42
38
|
)}
|
|
43
|
-
{...props}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
44
41
|
{children}
|
|
45
42
|
</DialogPrimitive.Popup>
|
|
46
43
|
</PortalScope>
|
|
47
44
|
</DialogPrimitive.Portal>
|
|
48
45
|
)
|
|
49
46
|
|
|
50
|
-
/** Title row with the close button, pinned above the scrolling body. */
|
|
51
47
|
export const DialogHeader: FunctionComponent<{
|
|
52
48
|
title: string
|
|
53
49
|
description?: string
|
|
54
|
-
/** Rendered between the title and the close button. */
|
|
55
50
|
actions?: React.ReactNode
|
|
56
51
|
}> = ({ title, description, actions }) => (
|
|
57
|
-
<div className=
|
|
58
|
-
<div className=
|
|
59
|
-
<DialogPrimitive.Title className=
|
|
60
|
-
{title}
|
|
61
|
-
</DialogPrimitive.Title>
|
|
52
|
+
<div className="flex items-start gap-2 border-b border-border px-4 py-3">
|
|
53
|
+
<div className="min-w-0 flex-1">
|
|
54
|
+
<DialogPrimitive.Title className="truncate text-body-sm font-semibold text-text">{title}</DialogPrimitive.Title>
|
|
62
55
|
{description ? (
|
|
63
|
-
<DialogPrimitive.Description className=
|
|
64
|
-
{description}
|
|
65
|
-
</DialogPrimitive.Description>
|
|
56
|
+
<DialogPrimitive.Description className="mt-0.5 text-label text-fg-4">{description}</DialogPrimitive.Description>
|
|
66
57
|
) : null}
|
|
67
58
|
</div>
|
|
68
59
|
{actions}
|
|
69
60
|
<DialogPrimitive.Close
|
|
70
|
-
aria-label=
|
|
71
|
-
className=
|
|
72
|
-
|
|
61
|
+
aria-label="Close"
|
|
62
|
+
className="-mr-1 flex size-6 shrink-0 items-center justify-center rounded-md text-fg-3 transition-colors outline-none hover:bg-surface-hover hover:text-fg-1"
|
|
63
|
+
>
|
|
64
|
+
<X className="size-3.5" />
|
|
73
65
|
</DialogPrimitive.Close>
|
|
74
66
|
</div>
|
|
75
67
|
)
|
|
76
68
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
...props
|
|
81
|
-
}) => <div className={cn('min-h-0 flex-1 overflow-y-auto p-4', className)} {...props} />
|
|
69
|
+
export const DialogBody: FunctionComponent<React.HTMLAttributes<HTMLDivElement>> = ({ className, ...props }) => (
|
|
70
|
+
<div className={cn('min-h-0 flex-1 overflow-y-auto p-4', className)} {...props} />
|
|
71
|
+
)
|
|
82
72
|
|
|
83
|
-
/** A label/value row — the shape every one of these panels is mostly made of. */
|
|
84
73
|
export const DialogRow: FunctionComponent<{
|
|
85
74
|
label: string
|
|
86
75
|
children: React.ReactNode
|
|
87
76
|
mono?: boolean
|
|
88
77
|
}> = ({ label, children, mono }) => (
|
|
89
|
-
<div className=
|
|
90
|
-
<span className=
|
|
91
|
-
<span className={cn('min-w-0 truncate text-right text-body-sm text-fg-1', mono && 'font-mono text-label')}>
|
|
92
|
-
{children}
|
|
93
|
-
</span>
|
|
78
|
+
<div className="flex items-baseline justify-between gap-4 py-1.5">
|
|
79
|
+
<span className="shrink-0 text-label text-fg-3">{label}</span>
|
|
80
|
+
<span className={cn('min-w-0 truncate text-right text-body-sm text-fg-1', mono && 'font-mono text-label')}>{children}</span>
|
|
94
81
|
</div>
|
|
95
82
|
)
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
import { Button } from './Button.tsx'
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* A view with nothing in it: icon, title, description, and at most one action.
|
|
6
|
-
*
|
|
7
|
-
* Centered and given room, because an empty view is the first thing a new
|
|
8
|
-
* install shows and a flush-left sentence in the top corner reads like a bug
|
|
9
|
-
* report. It lives here rather than in one client because every panel that can
|
|
10
|
-
* be empty should be empty the same way — the extension's views and the
|
|
11
|
-
* dashboard's four sidebars are the same shape of thing.
|
|
12
|
-
*
|
|
13
|
-
* **At most one action, and never one the view header already offers.** Creating
|
|
14
|
-
* a session and adding a gateway are the `+` in the native title bar, exclusively
|
|
15
|
-
* — so those empty states point at it in words rather than growing a second
|
|
16
|
-
* button that does the same thing two inches lower. A button here is for the way
|
|
17
|
-
* out of a state the header has no answer to: clearing a filter, widening a
|
|
18
|
-
* scope.
|
|
19
|
-
*/
|
|
20
4
|
export function Empty({
|
|
21
5
|
icon,
|
|
22
6
|
title,
|
|
@@ -31,16 +15,12 @@ export function Empty({
|
|
|
31
15
|
onAction?: () => void
|
|
32
16
|
}) {
|
|
33
17
|
return (
|
|
34
|
-
<div className=
|
|
35
|
-
<div className=
|
|
36
|
-
<p className=
|
|
37
|
-
{description ?
|
|
38
|
-
<p className='max-w-[26ch] text-balance text-label leading-relaxed text-fg-4'>
|
|
39
|
-
{description}
|
|
40
|
-
</p>
|
|
41
|
-
) : null}
|
|
18
|
+
<div className="flex flex-col items-center gap-2 px-5 py-8 text-center">
|
|
19
|
+
<div className="text-fg-4 opacity-60 [&_svg]:size-7">{icon}</div>
|
|
20
|
+
<p className="text-body-sm font-medium text-fg-2">{title}</p>
|
|
21
|
+
{description ? <p className="max-w-[26ch] text-balance text-label leading-relaxed text-fg-4">{description}</p> : null}
|
|
42
22
|
{action && onAction ? (
|
|
43
|
-
<Button variant=
|
|
23
|
+
<Button variant="outline" size="sm" className="mt-1" onClick={onAction}>
|
|
44
24
|
{action}
|
|
45
25
|
</Button>
|
|
46
26
|
) : null}
|
|
@@ -48,9 +28,6 @@ export function Empty({
|
|
|
48
28
|
)
|
|
49
29
|
}
|
|
50
30
|
|
|
51
|
-
/** A key or icon named inline in an empty state's description — "use the + above". */
|
|
52
31
|
export function EmptyKey({ children }: { children: ReactNode }) {
|
|
53
|
-
return
|
|
54
|
-
<span className='rounded border border-border px-1 font-mono text-fg-3'>{children}</span>
|
|
55
|
-
)
|
|
32
|
+
return <span className="rounded border border-border px-1 font-mono text-fg-3">{children}</span>
|
|
56
33
|
}
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { type InputHTMLAttributes, forwardRef } from 'react'
|
|
2
2
|
import { cn } from '../../lib/utils.ts'
|
|
3
3
|
|
|
4
|
-
export const Input = forwardRef<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>>(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
),
|
|
19
|
-
)
|
|
4
|
+
export const Input = forwardRef<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>>(({ className, ...props }, ref) => (
|
|
5
|
+
<input
|
|
6
|
+
ref={ref}
|
|
7
|
+
data-slot="input"
|
|
8
|
+
className={cn(
|
|
9
|
+
'h-8 w-full rounded-md border border-border bg-bg px-2.5 text-body-sm text-text',
|
|
10
|
+
'placeholder:text-fg-4 transition-colors outline-none',
|
|
11
|
+
'hover:border-border-strong focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',
|
|
12
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
))
|
|
20
18
|
Input.displayName = 'Input'
|
|
@@ -7,18 +7,13 @@ export const Menu = MenuPrimitive.Root
|
|
|
7
7
|
export const MenuTrigger = MenuPrimitive.Trigger
|
|
8
8
|
|
|
9
9
|
export const MenuContent: FunctionComponent<
|
|
10
|
-
MenuPrimitive.Popup.Props &
|
|
11
|
-
Pick<MenuPrimitive.Positioner.Props, 'align' | 'side' | 'sideOffset'>
|
|
10
|
+
MenuPrimitive.Popup.Props & Pick<MenuPrimitive.Positioner.Props, 'align' | 'side' | 'sideOffset'>
|
|
12
11
|
> = ({ className, align = 'end', side = 'bottom', sideOffset = 6, ...props }) => (
|
|
13
12
|
<MenuPrimitive.Portal>
|
|
14
13
|
<PortalScope>
|
|
15
|
-
<MenuPrimitive.Positioner
|
|
16
|
-
align={align}
|
|
17
|
-
side={side}
|
|
18
|
-
sideOffset={sideOffset}
|
|
19
|
-
className='isolate z-80 outline-none'>
|
|
14
|
+
<MenuPrimitive.Positioner align={align} side={side} sideOffset={sideOffset} className="isolate z-80 outline-none">
|
|
20
15
|
<MenuPrimitive.Popup
|
|
21
|
-
data-slot=
|
|
16
|
+
data-slot="menu-content"
|
|
22
17
|
className={cn(
|
|
23
18
|
'min-w-48 rounded-md border border-border bg-surface p-1 text-fg-1 shadow-(--shadow-lg) outline-none',
|
|
24
19
|
'transition-[opacity,transform] duration-(--motion-base)',
|
|
@@ -33,13 +28,9 @@ export const MenuContent: FunctionComponent<
|
|
|
33
28
|
</MenuPrimitive.Portal>
|
|
34
29
|
)
|
|
35
30
|
|
|
36
|
-
export const MenuItem: FunctionComponent<MenuPrimitive.Item.Props & { destructive?: boolean }> = ({
|
|
37
|
-
className,
|
|
38
|
-
destructive,
|
|
39
|
-
...props
|
|
40
|
-
}) => (
|
|
31
|
+
export const MenuItem: FunctionComponent<MenuPrimitive.Item.Props & { destructive?: boolean }> = ({ className, destructive, ...props }) => (
|
|
41
32
|
<MenuPrimitive.Item
|
|
42
|
-
data-slot=
|
|
33
|
+
data-slot="menu-item"
|
|
43
34
|
className={cn(
|
|
44
35
|
'flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-body-sm outline-none select-none',
|
|
45
36
|
'data-highlighted:bg-surface-hover',
|
|
@@ -50,9 +41,6 @@ export const MenuItem: FunctionComponent<MenuPrimitive.Item.Props & { destructiv
|
|
|
50
41
|
/>
|
|
51
42
|
)
|
|
52
43
|
|
|
53
|
-
export const MenuSeparator: FunctionComponent<MenuPrimitive.Separator.Props> = ({
|
|
54
|
-
className,
|
|
55
|
-
...props
|
|
56
|
-
}) => (
|
|
44
|
+
export const MenuSeparator: FunctionComponent<MenuPrimitive.Separator.Props> = ({ className, ...props }) => (
|
|
57
45
|
<MenuPrimitive.Separator className={cn('my-1 h-px bg-border', className)} {...props} />
|
|
58
46
|
)
|
|
@@ -1,27 +1,7 @@
|
|
|
1
1
|
import { type FunctionComponent, type ReactNode } from 'react'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Re-establishes the `.wd-root` styling scope inside a portal.
|
|
5
|
-
*
|
|
6
|
-
* The scoped stylesheet (`@workerdeck/ui/scoped.css`) rewrites every rule to
|
|
7
|
-
* live under `.wd-root`, so an embedder's page keeps its own design system and
|
|
8
|
-
* the panel keeps this one. Base UI popups (Menu, Dialog, AlertDialog, Select,
|
|
9
|
-
* Tooltip) portal to `document.body` — OUTSIDE the embedder's wrapper — so
|
|
10
|
-
* without this they would render unstyled, or worse, styled by the host.
|
|
11
|
-
*
|
|
12
|
-
* `display: contents` (inline, so no stylesheet needs to load for it) makes the
|
|
13
|
-
* element generate no box: positioning, hit-testing and the popup's own layout
|
|
14
|
-
* are untouched. It still carries the class, which is all the scope needs —
|
|
15
|
-
* design tokens declared on `.wd-root` inherit through it, and every
|
|
16
|
-
* `.wd-root <x>` rule matches below it. Theme keeps working because the dark
|
|
17
|
-
* token block matches `[data-theme='dark']` on ANY ancestor, and `document.body`
|
|
18
|
-
* sits under the host's themed `<html>` just like the panel does.
|
|
19
|
-
*
|
|
20
|
-
* Under the classic `theme.css` integration the class matches no rule and the
|
|
21
|
-
* element is inert, so the wrapper is unconditional.
|
|
22
|
-
*/
|
|
23
3
|
export const PortalScope: FunctionComponent<{ children?: ReactNode }> = ({ children }) => (
|
|
24
|
-
<div className=
|
|
4
|
+
<div className="wd-root" style={{ display: 'contents' }}>
|
|
25
5
|
{children}
|
|
26
6
|
</div>
|
|
27
7
|
)
|
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
import { cn } from '../../lib/utils.ts'
|
|
2
2
|
|
|
3
3
|
export interface ProgressRingProps {
|
|
4
|
-
/** Filled share, 0–100 (clamped). */
|
|
5
4
|
value: number
|
|
6
|
-
/** Outer diameter in px. */
|
|
7
5
|
size?: number
|
|
8
6
|
strokeWidth?: number
|
|
9
7
|
className?: string
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
/** Tiny SVG progress circle; stroke color comes from `currentColor` so callers set it
|
|
13
|
-
* via text color classes (e.g. warning/danger past thresholds). */
|
|
14
10
|
export function ProgressRing({ value, size = 13, strokeWidth = 2, className }: ProgressRingProps) {
|
|
15
11
|
const clamped = Math.min(100, Math.max(0, value))
|
|
16
12
|
const radius = (size - strokeWidth) / 2
|
|
17
13
|
const circumference = 2 * Math.PI * radius
|
|
18
14
|
return (
|
|
19
15
|
<svg
|
|
20
|
-
data-slot=
|
|
16
|
+
data-slot="progress-ring"
|
|
21
17
|
width={size}
|
|
22
18
|
height={size}
|
|
23
19
|
viewBox={`0 0 ${size} ${size}`}
|
|
24
|
-
role=
|
|
20
|
+
role="img"
|
|
25
21
|
aria-label={`${Math.round(clamped)}%`}
|
|
26
|
-
className={cn('shrink-0 -rotate-90', className)}
|
|
22
|
+
className={cn('shrink-0 -rotate-90', className)}
|
|
23
|
+
>
|
|
24
|
+
<circle cx={size / 2} cy={size / 2} r={radius} fill="none" stroke="currentColor" strokeOpacity={0.2} strokeWidth={strokeWidth} />
|
|
27
25
|
<circle
|
|
28
26
|
cx={size / 2}
|
|
29
27
|
cy={size / 2}
|
|
30
28
|
r={radius}
|
|
31
|
-
fill=
|
|
32
|
-
stroke=
|
|
33
|
-
strokeOpacity={0.2}
|
|
29
|
+
fill="none"
|
|
30
|
+
stroke="currentColor"
|
|
34
31
|
strokeWidth={strokeWidth}
|
|
35
|
-
|
|
36
|
-
<circle
|
|
37
|
-
cx={size / 2}
|
|
38
|
-
cy={size / 2}
|
|
39
|
-
r={radius}
|
|
40
|
-
fill='none'
|
|
41
|
-
stroke='currentColor'
|
|
42
|
-
strokeWidth={strokeWidth}
|
|
43
|
-
strokeLinecap='round'
|
|
32
|
+
strokeLinecap="round"
|
|
44
33
|
strokeDasharray={circumference}
|
|
45
34
|
strokeDashoffset={circumference * (1 - clamped / 100)}
|
|
46
35
|
/>
|
|
@@ -8,38 +8,27 @@ export const Select = SelectPrimitive.Root
|
|
|
8
8
|
export const SelectValue = SelectPrimitive.Value
|
|
9
9
|
export const SelectItemText = SelectPrimitive.ItemText
|
|
10
10
|
|
|
11
|
-
export const SelectTrigger: FunctionComponent<SelectPrimitive.Trigger.Props> = ({
|
|
12
|
-
className,
|
|
13
|
-
children,
|
|
14
|
-
...props
|
|
15
|
-
}) => (
|
|
11
|
+
export const SelectTrigger: FunctionComponent<SelectPrimitive.Trigger.Props> = ({ className, children, ...props }) => (
|
|
16
12
|
<SelectPrimitive.Trigger
|
|
17
|
-
data-slot=
|
|
13
|
+
data-slot="select-trigger"
|
|
18
14
|
className={cn(
|
|
19
15
|
'inline-flex h-7 items-center justify-between gap-1.5 rounded-md border border-border bg-bg px-2 text-body-sm text-text',
|
|
20
16
|
'transition-colors outline-none hover:border-border-strong focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',
|
|
21
17
|
'data-popup-open:border-border-strong disabled:pointer-events-none disabled:opacity-50',
|
|
22
18
|
className,
|
|
23
19
|
)}
|
|
24
|
-
{...props}
|
|
20
|
+
{...props}
|
|
21
|
+
>
|
|
25
22
|
{children}
|
|
26
|
-
<SelectPrimitive.Icon className=
|
|
27
|
-
<ChevronsUpDown className=
|
|
23
|
+
<SelectPrimitive.Icon className="text-fg-4">
|
|
24
|
+
<ChevronsUpDown className="size-3.5" />
|
|
28
25
|
</SelectPrimitive.Icon>
|
|
29
26
|
</SelectPrimitive.Trigger>
|
|
30
27
|
)
|
|
31
28
|
|
|
32
29
|
export const SelectContent: FunctionComponent<
|
|
33
|
-
SelectPrimitive.Popup.Props &
|
|
34
|
-
|
|
35
|
-
> = ({
|
|
36
|
-
className,
|
|
37
|
-
align = 'start',
|
|
38
|
-
alignItemWithTrigger = false,
|
|
39
|
-
side = 'bottom',
|
|
40
|
-
sideOffset = 6,
|
|
41
|
-
...props
|
|
42
|
-
}) => (
|
|
30
|
+
SelectPrimitive.Popup.Props & Pick<SelectPrimitive.Positioner.Props, 'align' | 'alignItemWithTrigger' | 'side' | 'sideOffset'>
|
|
31
|
+
> = ({ className, align = 'start', alignItemWithTrigger = false, side = 'bottom', sideOffset = 6, ...props }) => (
|
|
43
32
|
<SelectPrimitive.Portal>
|
|
44
33
|
<PortalScope>
|
|
45
34
|
<SelectPrimitive.Positioner
|
|
@@ -47,9 +36,10 @@ export const SelectContent: FunctionComponent<
|
|
|
47
36
|
alignItemWithTrigger={alignItemWithTrigger}
|
|
48
37
|
side={side}
|
|
49
38
|
sideOffset={sideOffset}
|
|
50
|
-
className=
|
|
39
|
+
className="isolate z-80 outline-none"
|
|
40
|
+
>
|
|
51
41
|
<SelectPrimitive.Popup
|
|
52
|
-
data-slot=
|
|
42
|
+
data-slot="select-content"
|
|
53
43
|
className={cn(
|
|
54
44
|
'max-h-[min(24rem,var(--available-height))] min-w-[var(--anchor-width)] overflow-y-auto',
|
|
55
45
|
'rounded-md border border-border bg-surface p-1 text-fg-1 shadow-(--shadow-lg) outline-none',
|
|
@@ -62,22 +52,19 @@ export const SelectContent: FunctionComponent<
|
|
|
62
52
|
</SelectPrimitive.Portal>
|
|
63
53
|
)
|
|
64
54
|
|
|
65
|
-
export const SelectItem: FunctionComponent<SelectPrimitive.Item.Props> = ({
|
|
66
|
-
className,
|
|
67
|
-
children,
|
|
68
|
-
...props
|
|
69
|
-
}) => (
|
|
55
|
+
export const SelectItem: FunctionComponent<SelectPrimitive.Item.Props> = ({ className, children, ...props }) => (
|
|
70
56
|
<SelectPrimitive.Item
|
|
71
|
-
data-slot=
|
|
57
|
+
data-slot="select-item"
|
|
72
58
|
className={cn(
|
|
73
59
|
'flex cursor-pointer items-start gap-2 rounded-sm px-2 py-1.5 text-body-sm text-text outline-none select-none',
|
|
74
60
|
'data-highlighted:bg-surface-hover',
|
|
75
61
|
className,
|
|
76
62
|
)}
|
|
77
|
-
{...props}
|
|
78
|
-
|
|
79
|
-
<
|
|
80
|
-
|
|
63
|
+
{...props}
|
|
64
|
+
>
|
|
65
|
+
<span className="flex min-w-0 flex-1 flex-col gap-0.5">{children}</span>
|
|
66
|
+
<SelectPrimitive.ItemIndicator className="mt-0.5 text-fg-1">
|
|
67
|
+
<Check className="size-3.5" />
|
|
81
68
|
</SelectPrimitive.ItemIndicator>
|
|
82
69
|
</SelectPrimitive.Item>
|
|
83
70
|
)
|
|
@@ -2,11 +2,10 @@ import { Toaster as SonnerToaster, toast } from 'sonner'
|
|
|
2
2
|
|
|
3
3
|
export { toast }
|
|
4
4
|
|
|
5
|
-
/** Token-themed toaster; relies on the [data-theme] swap, so no `theme` prop needed. */
|
|
6
5
|
export function Toaster() {
|
|
7
6
|
return (
|
|
8
7
|
<SonnerToaster
|
|
9
|
-
position=
|
|
8
|
+
position="bottom-right"
|
|
10
9
|
toastOptions={{
|
|
11
10
|
style: {
|
|
12
11
|
background: 'var(--surface)',
|
|
@@ -2,5 +2,5 @@ import { LoaderCircle } from 'lucide-react'
|
|
|
2
2
|
import { cn } from '../../lib/utils.ts'
|
|
3
3
|
|
|
4
4
|
export function Spinner({ className }: { className?: string }) {
|
|
5
|
-
return <LoaderCircle aria-label=
|
|
5
|
+
return <LoaderCircle aria-label="Loading" className={cn('size-4 animate-spin text-fg-3', className)} />
|
|
6
6
|
}
|