@corbits/react-ui 0.1.0 → 0.3.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/LICENSE +174 -499
- package/README.md +228 -85
- package/dist/blocks/login/auth-layout.d.ts +1 -1
- package/dist/blocks/login/auth-layout.js +3 -33
- package/dist/blocks/login/login-form.js +6 -119
- package/dist/hooks/use-command-palette-navigation.js +18 -17
- package/dist/hooks/use-controllable-state.d.ts +2 -2
- package/dist/hooks/use-controllable-state.js +23 -15
- package/dist/hooks/use-delayed-autofocus.js +10 -11
- package/dist/hooks/use-dismissable-popover.d.ts +2 -2
- package/dist/hooks/use-dismissable-popover.js +21 -29
- package/dist/hooks/use-list-selection.js +54 -85
- package/dist/hooks/use-prefers-reduced-motion.js +8 -4
- package/dist/hooks/use-scroll-current-into-view.js +5 -7
- package/dist/index.d.ts +0 -11
- package/dist/index.js +10 -19
- package/dist/lib/artifact.js +16 -35
- package/dist/lib/chart-geometry.js +33 -20
- package/dist/lib/chart-palette.js +17 -31
- package/dist/lib/chat-message.js +32 -13
- package/dist/lib/chat-parts.js +2 -1
- package/dist/lib/csv.js +42 -26
- package/dist/lib/metrics.js +30 -48
- package/dist/lib/now-item.js +26 -25
- package/dist/lib/relative-time.js +14 -29
- package/dist/lib/theme.js +32 -30
- package/dist/lib/url.js +9 -8
- package/dist/lib/workflow-display-flow.js +37 -33
- package/dist/lib/workflow-registry.d.ts +0 -41
- package/dist/lib/workflow-registry.js +13 -23
- package/dist/lib/workflow-run-error.js +16 -20
- package/dist/lib/workflow-run-progress.js +94 -52
- package/dist/lib/workflow-run.js +17 -13
- package/dist/styles.css +257 -19
- package/dist/test/render-hook.js +11 -12
- package/dist/theme.css +92 -14
- package/dist/ui/agent-turn.d.ts +1 -1
- package/dist/ui/agent-turn.js +4 -37
- package/dist/ui/animated-number.js +15 -28
- package/dist/ui/approval-card.js +5 -90
- package/dist/ui/artifact-body.js +47 -125
- package/dist/ui/artifact-notice.d.ts +1 -1
- package/dist/ui/artifact-notice.js +9 -16
- package/dist/ui/avatar.d.ts +21 -4
- package/dist/ui/avatar.js +40 -49
- package/dist/ui/badge.js +4 -12
- package/dist/ui/bar-chart.d.ts +1 -1
- package/dist/ui/bar-chart.js +22 -74
- package/dist/ui/block-card.js +7 -45
- package/dist/ui/boot-screen.d.ts +1 -1
- package/dist/ui/boot-screen.js +4 -33
- package/dist/ui/bulk-action-bar.d.ts +1 -1
- package/dist/ui/bulk-action-bar.js +14 -33
- package/dist/ui/button.js +10 -24
- package/dist/ui/card.js +5 -25
- package/dist/ui/category-bars.js +14 -59
- package/dist/ui/chart-frame.d.ts +2 -2
- package/dist/ui/chart-frame.js +6 -85
- package/dist/ui/chat-composer.js +4 -16
- package/dist/ui/chat-dock-timing.js +7 -3
- package/dist/ui/chat-input.js +20 -100
- package/dist/ui/chat-panel.d.ts +1 -1
- package/dist/ui/chat-panel.js +7 -41
- package/dist/ui/chat-thread.js +13 -31
- package/dist/ui/checkbox.js +10 -52
- package/dist/ui/command-palette.js +85 -246
- package/dist/ui/compare-body.d.ts +1 -1
- package/dist/ui/compare-body.js +28 -98
- package/dist/ui/confirm-button.js +8 -15
- package/dist/ui/corbits-mark.js +7 -16
- package/dist/ui/csv-table.d.ts +1 -1
- package/dist/ui/csv-table.js +8 -50
- package/dist/ui/dialog.d.ts +1 -1
- package/dist/ui/dialog.js +32 -62
- package/dist/ui/dictation-status-line.js +7 -25
- package/dist/ui/dither-canvas.d.ts +1 -1
- package/dist/ui/dither-canvas.js +31 -56
- package/dist/ui/embed-body.d.ts +1 -1
- package/dist/ui/embed-body.js +7 -56
- package/dist/ui/empty-state.d.ts +1 -1
- package/dist/ui/empty-state.js +3 -26
- package/dist/ui/file-input.js +17 -37
- package/dist/ui/filter-bar.d.ts +1 -1
- package/dist/ui/filter-bar.js +11 -71
- package/dist/ui/filter-chip.d.ts +1 -1
- package/dist/ui/filter-chip.js +7 -15
- package/dist/ui/gate-block.d.ts +2 -1
- package/dist/ui/gate-block.js +4 -29
- package/dist/ui/horizontal-stepper.d.ts +1 -1
- package/dist/ui/horizontal-stepper.js +18 -45
- package/dist/ui/input.js +1 -6
- package/dist/ui/inspector-shell.d.ts +5 -5
- package/dist/ui/inspector-shell.js +14 -81
- package/dist/ui/intake-form.d.ts +1 -1
- package/dist/ui/intake-form.js +21 -97
- package/dist/ui/kind-card-grid.js +9 -31
- package/dist/ui/library-page-header.js +4 -32
- package/dist/ui/list-detail.js +5 -35
- package/dist/ui/live-run-banner.d.ts +1 -1
- package/dist/ui/live-run-banner.js +5 -15
- package/dist/ui/live-status-line.js +6 -24
- package/dist/ui/menu.js +7 -24
- package/dist/ui/message-bubble.d.ts +3 -3
- package/dist/ui/message-bubble.js +5 -44
- package/dist/ui/mic-button.d.ts +1 -1
- package/dist/ui/mic-button.js +15 -21
- package/dist/ui/mic-permission-dialog.js +5 -41
- package/dist/ui/notifications-bell.js +9 -43
- package/dist/ui/now-cards.d.ts +2 -2
- package/dist/ui/now-cards.js +16 -94
- package/dist/ui/page-panel.js +11 -11
- package/dist/ui/page-shell.js +4 -11
- package/dist/ui/parts-renderer.js +51 -149
- package/dist/ui/profile-card.js +7 -111
- package/dist/ui/progress-checklist.js +11 -49
- package/dist/ui/provider-mark.d.ts +2 -2
- package/dist/ui/provider-mark.js +9 -16
- package/dist/ui/quick-reply-chips.js +6 -14
- package/dist/ui/quote-card.js +12 -32
- package/dist/ui/reasoning-block.d.ts +1 -1
- package/dist/ui/reasoning-block.js +14 -33
- package/dist/ui/research-body.js +32 -213
- package/dist/ui/rich-empty-state.js +6 -48
- package/dist/ui/run-now-button.js +14 -15
- package/dist/ui/section.d.ts +1 -1
- package/dist/ui/section.js +4 -40
- package/dist/ui/select.js +1 -15
- package/dist/ui/selection-checkbox.d.ts +1 -1
- package/dist/ui/selection-checkbox.js +11 -29
- package/dist/ui/settings-panel.js +7 -50
- package/dist/ui/shimmer-text.js +3 -6
- package/dist/ui/sidebar-item-row.d.ts +1 -1
- package/dist/ui/sidebar-item-row.js +9 -39
- package/dist/ui/sidebar-panel.js +15 -40
- package/dist/ui/sidebar.js +15 -76
- package/dist/ui/skeleton.js +3 -7
- package/dist/ui/sortable-table.js +29 -72
- package/dist/ui/sparkline.js +14 -38
- package/dist/ui/stat-grid.js +17 -52
- package/dist/ui/status-dot.d.ts +1 -1
- package/dist/ui/status-dot.js +8 -20
- package/dist/ui/step-list.d.ts +1 -0
- package/dist/ui/step-list.js +11 -50
- package/dist/ui/switch.js +4 -16
- package/dist/ui/table.js +7 -32
- package/dist/ui/tabs.js +39 -54
- package/dist/ui/textarea.js +10 -19
- package/dist/ui/theme-provider.js +54 -78
- package/dist/ui/theme-toggle.js +13 -16
- package/dist/ui/thinking-indicator.js +3 -15
- package/dist/ui/thinking-label.js +16 -35
- package/dist/ui/thinking-mark.d.ts +1 -1
- package/dist/ui/thinking-mark.js +4 -145
- package/dist/ui/time-series-chart.js +43 -174
- package/dist/ui/toast.js +12 -20
- package/dist/ui/token-mosaic.js +5 -43
- package/dist/ui/tool-block.js +18 -70
- package/dist/ui/tool-narrative.d.ts +1 -1
- package/dist/ui/tool-narrative.js +10 -57
- package/dist/ui/tool-picker.js +57 -145
- package/dist/ui/tooltip.d.ts +1 -1
- package/dist/ui/tooltip.js +4 -33
- package/dist/ui/top-bar.js +14 -72
- package/dist/ui/trace-waterfall.d.ts +1 -1
- package/dist/ui/trace-waterfall.js +21 -93
- package/dist/ui/typing-indicator.d.ts +1 -1
- package/dist/ui/typing-indicator.js +4 -18
- package/dist/ui/view-toggle.js +10 -29
- package/dist/ui/voice-waveform.js +3 -11
- package/package.json +96 -535
package/dist/ui/step-list.js
CHANGED
|
@@ -6,72 +6,33 @@ const STATE_LABEL = {
|
|
|
6
6
|
pending: "Pending",
|
|
7
7
|
active: "Now",
|
|
8
8
|
done: "Done",
|
|
9
|
-
failed: "Failed"
|
|
9
|
+
failed: "Failed",
|
|
10
10
|
};
|
|
11
11
|
const MARK_CLASS = {
|
|
12
12
|
pending: "border-input text-muted-foreground",
|
|
13
13
|
active: "border-primary-emphasis bg-primary/10 text-primary-emphasis",
|
|
14
14
|
done: "border-transparent bg-success text-success-foreground",
|
|
15
|
-
failed: "border-transparent bg-destructive text-destructive-foreground"
|
|
15
|
+
failed: "border-transparent bg-destructive text-destructive-foreground",
|
|
16
16
|
};
|
|
17
17
|
const LABEL_CLASS = {
|
|
18
18
|
pending: "text-muted-foreground",
|
|
19
19
|
active: "font-medium text-primary-emphasis",
|
|
20
20
|
done: "text-foreground",
|
|
21
|
-
failed: "font-medium text-destructive"
|
|
21
|
+
failed: "font-medium text-destructive",
|
|
22
22
|
};
|
|
23
23
|
/** One step row — a mark carrying its own glyph and colour, a name, and the
|
|
24
|
-
* status spelled out in text so it never depends on colour alone. */
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
className: cn("grid grid-cols-[20px_minmax(0,1fr)_auto] items-start gap-2 py-1.5", className),
|
|
28
|
-
children: [
|
|
29
|
-
/*#__PURE__*/ _jsx("span", {
|
|
30
|
-
"aria-hidden": true,
|
|
31
|
-
className: cn("mt-0.5 grid size-5 place-items-center rounded-sm border text-[10px] font-semibold", MARK_CLASS[step.status]),
|
|
32
|
-
children: step.status === "done" ? /*#__PURE__*/ _jsx(Check, {
|
|
33
|
-
className: "size-3"
|
|
34
|
-
}) : step.status === "failed" ? /*#__PURE__*/ _jsx(X, {
|
|
35
|
-
className: "size-3"
|
|
36
|
-
}) : step.status === "active" ? /*#__PURE__*/ _jsx(StatusDot, {
|
|
37
|
-
label: "",
|
|
38
|
-
tone: "emphasis",
|
|
39
|
-
live: true
|
|
40
|
-
}) : null
|
|
41
|
-
}),
|
|
42
|
-
/*#__PURE__*/ _jsxs("span", {
|
|
43
|
-
className: "min-w-0",
|
|
44
|
-
children: [
|
|
45
|
-
/*#__PURE__*/ _jsx("span", {
|
|
46
|
-
className: cn("block truncate text-sm", LABEL_CLASS[step.status]),
|
|
47
|
-
children: step.name
|
|
48
|
-
}),
|
|
49
|
-
step.meta === undefined ? null : /*#__PURE__*/ _jsx("span", {
|
|
50
|
-
className: "block truncate text-xs text-muted-foreground",
|
|
51
|
-
children: step.meta
|
|
52
|
-
})
|
|
53
|
-
]
|
|
54
|
-
}),
|
|
55
|
-
/*#__PURE__*/ _jsx("span", {
|
|
56
|
-
className: "shrink-0 text-[10.5px] font-bold uppercase tracking-[0.05em] text-muted-foreground",
|
|
57
|
-
children: STATE_LABEL[step.status]
|
|
58
|
-
})
|
|
59
|
-
]
|
|
60
|
-
});
|
|
24
|
+
* status spelled out in text so it never depends on colour alone. */
|
|
25
|
+
export function StepListRow({ step, className }) {
|
|
26
|
+
return (_jsxs("li", { "data-step-status": step.status, className: cn("grid grid-cols-[20px_minmax(0,1fr)_auto] items-start gap-2 py-1.5", className), children: [_jsx("span", { "aria-hidden": true, className: cn("mt-0.5 grid size-5 place-items-center rounded-sm border text-[10px] font-semibold", MARK_CLASS[step.status]), children: step.status === "done" ? (_jsx(Check, { className: "size-3" })) : step.status === "failed" ? (_jsx(X, { className: "size-3" })) : step.status === "active" ? (_jsx(StatusDot, { label: "", tone: "emphasis", live: true })) : null }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: cn("block truncate text-sm", LABEL_CLASS[step.status]), children: step.name }), step.meta === undefined ? null : (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: step.meta }))] }), _jsx("span", { className: "shrink-0 text-[10.5px] font-bold uppercase tracking-[0.05em] text-muted-foreground", children: STATE_LABEL[step.status] })] }));
|
|
61
27
|
}
|
|
62
28
|
/**
|
|
63
29
|
* A run's steps and how far along each one is. `aria-live="polite"` so a
|
|
64
30
|
* step flipping to done or failed is announced without stealing focus from
|
|
65
31
|
* wherever the reader actually is — the same rule `progress-checklist.tsx`
|
|
66
32
|
* follows for a long-running operation's checklist.
|
|
67
|
-
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
className: cn("flex flex-col", className),
|
|
73
|
-
children: steps.map((step)=>/*#__PURE__*/ _jsx(StepListRow, {
|
|
74
|
-
step: step
|
|
75
|
-
}, step.id))
|
|
76
|
-
});
|
|
33
|
+
*/
|
|
34
|
+
export function StepList({ steps, label = "Steps", className }) {
|
|
35
|
+
if (steps.length === 0)
|
|
36
|
+
return null;
|
|
37
|
+
return (_jsx("ol", { "aria-label": label, "aria-live": "polite", className: cn("flex flex-col", className), children: steps.map((step) => (_jsx(StepListRow, { step: step }, step.id))) }));
|
|
77
38
|
}
|
package/dist/ui/switch.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { cn } from "../lib/utils.js";
|
|
3
4
|
/**
|
|
@@ -10,20 +11,7 @@ import { cn } from "../lib/utils.js";
|
|
|
10
11
|
*
|
|
11
12
|
* The knob is a `<span>` inside the button rather than a `::after`, so its
|
|
12
13
|
* transform animates independently of the track's colour transition.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
id: id,
|
|
17
|
-
role: "switch",
|
|
18
|
-
"aria-checked": checked,
|
|
19
|
-
"aria-label": label,
|
|
20
|
-
"aria-describedby": describedBy,
|
|
21
|
-
disabled: disabled,
|
|
22
|
-
onClick: ()=>onCheckedChange(!checked),
|
|
23
|
-
className: cn("inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent p-0.5 transition-colors disabled:opacity-50", checked ? "bg-primary" : "bg-input", className),
|
|
24
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
25
|
-
"aria-hidden": true,
|
|
26
|
-
className: cn("size-4 rounded-full bg-background transition-transform", checked && "translate-x-4")
|
|
27
|
-
})
|
|
28
|
-
});
|
|
14
|
+
*/
|
|
15
|
+
export function Switch({ checked, onCheckedChange, label, id, disabled = false, describedBy, className, }) {
|
|
16
|
+
return (_jsx("button", { type: "button", id: id, role: "switch", "aria-checked": checked, "aria-label": label, "aria-describedby": describedBy, disabled: disabled, onClick: () => onCheckedChange(!checked), className: cn("inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent p-0.5 transition-colors disabled:opacity-50", checked ? "bg-primary" : "bg-input", className), children: _jsx("span", { "aria-hidden": true, className: cn("size-4 rounded-full bg-background transition-transform", checked && "translate-x-4") }) }));
|
|
29
17
|
}
|
package/dist/ui/table.js
CHANGED
|
@@ -1,48 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
export function Table({ className, ...props }) {
|
|
4
|
-
return
|
|
5
|
-
className: "relative w-full overflow-x-auto",
|
|
6
|
-
children: /*#__PURE__*/ _jsx("table", {
|
|
7
|
-
"data-slot": "table",
|
|
8
|
-
className: cn("w-full caption-bottom text-sm", className),
|
|
9
|
-
...props
|
|
10
|
-
})
|
|
11
|
-
});
|
|
4
|
+
return (_jsx("div", { className: "relative w-full overflow-x-auto", children: _jsx("table", { "data-slot": "table", className: cn("w-full caption-bottom text-sm", className), ...props }) }));
|
|
12
5
|
}
|
|
13
6
|
export function TableHeader({ className, ...props }) {
|
|
14
|
-
return
|
|
15
|
-
className: cn("[&_tr]:border-b [&_tr]:border-border", className),
|
|
16
|
-
...props
|
|
17
|
-
});
|
|
7
|
+
return (_jsx("thead", { className: cn("[&_tr]:border-b [&_tr]:border-border", className), ...props }));
|
|
18
8
|
}
|
|
19
9
|
export function TableBody({ className, ...props }) {
|
|
20
|
-
return
|
|
21
|
-
className: cn("[&_tr:last-child]:border-0", className),
|
|
22
|
-
...props
|
|
23
|
-
});
|
|
10
|
+
return (_jsx("tbody", { className: cn("[&_tr:last-child]:border-0", className), ...props }));
|
|
24
11
|
}
|
|
25
12
|
export function TableRow({ className, ...props }) {
|
|
26
|
-
return
|
|
27
|
-
className: cn("border-b border-border transition-colors hover:bg-muted", className),
|
|
28
|
-
...props
|
|
29
|
-
});
|
|
13
|
+
return (_jsx("tr", { className: cn("border-b border-border transition-colors hover:bg-muted", className), ...props }));
|
|
30
14
|
}
|
|
31
15
|
export function TableHead({ className, ...props }) {
|
|
32
|
-
return
|
|
33
|
-
className: cn("h-10 px-3 text-left align-middle font-medium text-muted-foreground", className),
|
|
34
|
-
...props
|
|
35
|
-
});
|
|
16
|
+
return (_jsx("th", { className: cn("h-10 px-3 text-left align-middle font-medium text-muted-foreground", className), ...props }));
|
|
36
17
|
}
|
|
37
18
|
export function TableCell({ className, ...props }) {
|
|
38
|
-
return
|
|
39
|
-
className: cn("p-3 align-middle", className),
|
|
40
|
-
...props
|
|
41
|
-
});
|
|
19
|
+
return _jsx("td", { className: cn("p-3 align-middle", className), ...props });
|
|
42
20
|
}
|
|
43
21
|
export function TableCaption({ className, ...props }) {
|
|
44
|
-
return
|
|
45
|
-
className: cn("mt-3 text-sm text-muted-foreground", className),
|
|
46
|
-
...props
|
|
47
|
-
});
|
|
22
|
+
return (_jsx("caption", { className: cn("mt-3 text-sm text-muted-foreground", className), ...props }));
|
|
48
23
|
}
|
package/dist/ui/tabs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useId, useRef } from "react";
|
|
3
4
|
import { cn } from "../lib/utils.js";
|
|
@@ -24,78 +25,62 @@ import { cn } from "../lib/utils.js";
|
|
|
24
25
|
* next Tab press escapes from the wrong place. Selection by click does *not*
|
|
25
26
|
* move focus — the clicked tab already has it — and focus is never pushed into
|
|
26
27
|
* the panel, which is the "improvement" that stops people arrowing on.
|
|
27
|
-
*/
|
|
28
|
+
*/
|
|
29
|
+
export function Tabs({ tabs, active, onChange, label, children, variant = "underline", className, }) {
|
|
28
30
|
const base = useId();
|
|
29
|
-
const tabId = (id)
|
|
30
|
-
const panelId = (id)
|
|
31
|
+
const tabId = (id) => `${base}-tab-${id}`;
|
|
32
|
+
const panelId = (id) => `${base}-panel-${id}`;
|
|
31
33
|
// Every tab button is rendered whatever the selection, so the node to focus
|
|
32
34
|
// already exists at the moment the key is handled — no effect, no timer.
|
|
33
35
|
const buttons = useRef(new Map());
|
|
34
|
-
/** Arrow/Home/End selection: change the tab *and* take focus with it. */
|
|
36
|
+
/** Arrow/Home/End selection: change the tab *and* take focus with it. */
|
|
37
|
+
function focusTab(id) {
|
|
35
38
|
onChange(id);
|
|
36
39
|
buttons.current.get(id)?.focus();
|
|
37
40
|
}
|
|
38
41
|
function move(delta) {
|
|
39
|
-
const index = tabs.findIndex((tab)=>tab.id === active);
|
|
42
|
+
const index = tabs.findIndex((tab) => tab.id === active);
|
|
40
43
|
const next = tabs[(index + delta + tabs.length) % tabs.length];
|
|
41
|
-
if (next !== undefined)
|
|
44
|
+
if (next !== undefined)
|
|
45
|
+
focusTab(next.id);
|
|
42
46
|
}
|
|
43
|
-
return
|
|
44
|
-
className: cn("flex flex-col gap-4", className),
|
|
45
|
-
children: [
|
|
46
|
-
/*#__PURE__*/ _jsx("div", {
|
|
47
|
-
role: "tablist",
|
|
48
|
-
"aria-label": label,
|
|
49
|
-
onKeyDown: (event)=>{
|
|
47
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-4", className), children: [_jsx("div", { role: "tablist", "aria-label": label, onKeyDown: (event) => {
|
|
50
48
|
if (event.key === "ArrowRight") {
|
|
51
49
|
event.preventDefault();
|
|
52
50
|
move(1);
|
|
53
|
-
}
|
|
51
|
+
}
|
|
52
|
+
else if (event.key === "ArrowLeft") {
|
|
54
53
|
event.preventDefault();
|
|
55
54
|
move(-1);
|
|
56
|
-
}
|
|
55
|
+
}
|
|
56
|
+
else if (event.key === "Home") {
|
|
57
57
|
event.preventDefault();
|
|
58
|
-
if (tabs[0] !== undefined)
|
|
59
|
-
|
|
58
|
+
if (tabs[0] !== undefined)
|
|
59
|
+
focusTab(tabs[0].id);
|
|
60
|
+
}
|
|
61
|
+
else if (event.key === "End") {
|
|
60
62
|
event.preventDefault();
|
|
61
63
|
const last = tabs[tabs.length - 1];
|
|
62
|
-
if (last !== undefined)
|
|
64
|
+
if (last !== undefined)
|
|
65
|
+
focusTab(last.id);
|
|
63
66
|
}
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
}, className: cn("flex flex-wrap items-center", variant === "underline"
|
|
68
|
+
? "gap-1 border-b border-border"
|
|
69
|
+
: "gap-1 rounded-md border border-border p-1"), children: tabs.map((tab) => {
|
|
67
70
|
const selected = tab.id === active;
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
else
|
|
72
|
-
|
|
73
|
-
type: "button",
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
tab.label,
|
|
84
|
-
tab.count === undefined ? null : /*#__PURE__*/ _jsx("span", {
|
|
85
|
-
className: "rounded-sm bg-muted px-1.5 font-mono text-xs text-muted-foreground",
|
|
86
|
-
children: tab.count
|
|
87
|
-
})
|
|
88
|
-
]
|
|
89
|
-
}, tab.id);
|
|
90
|
-
})
|
|
91
|
-
}),
|
|
92
|
-
/*#__PURE__*/ _jsx("div", {
|
|
93
|
-
role: "tabpanel",
|
|
94
|
-
id: panelId(active),
|
|
95
|
-
"aria-labelledby": tabId(active),
|
|
96
|
-
tabIndex: 0,
|
|
97
|
-
children: children(active)
|
|
98
|
-
})
|
|
99
|
-
]
|
|
100
|
-
});
|
|
71
|
+
return (_jsxs("button", { ref: (node) => {
|
|
72
|
+
if (node === null)
|
|
73
|
+
buttons.current.delete(tab.id);
|
|
74
|
+
else
|
|
75
|
+
buttons.current.set(tab.id, node);
|
|
76
|
+
}, type: "button", role: "tab", id: tabId(tab.id), "aria-selected": selected, "aria-controls": panelId(tab.id), tabIndex: selected ? 0 : -1, onClick: () => onChange(tab.id), className: cn("inline-flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors", variant === "underline"
|
|
77
|
+
? // The 2px border exists on both states, transparent when
|
|
78
|
+
// inactive, so selecting a tab does not shift the row.
|
|
79
|
+
selected
|
|
80
|
+
? "-mb-px border-b-2 border-primary-emphasis text-foreground"
|
|
81
|
+
: "-mb-px border-b-2 border-transparent text-muted-foreground hover:text-foreground"
|
|
82
|
+
: selected
|
|
83
|
+
? "rounded-sm bg-muted text-foreground"
|
|
84
|
+
: "rounded-sm text-muted-foreground hover:text-foreground"), children: [tab.label, tab.count === undefined ? null : (_jsx("span", { className: "rounded-sm bg-muted px-1.5 font-mono text-xs text-muted-foreground", children: tab.count }))] }, tab.id));
|
|
85
|
+
}) }), _jsx("div", { role: "tabpanel", id: panelId(active), "aria-labelledby": tabId(active), tabIndex: 0, children: children(active) })] }));
|
|
101
86
|
}
|
package/dist/ui/textarea.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { useEffect, useRef } from "react";
|
|
3
4
|
import { cn } from "../lib/utils.js";
|
|
@@ -6,27 +7,17 @@ import { cn } from "../lib/utils.js";
|
|
|
6
7
|
const DEFAULT_MAX_HEIGHT_PX = 200;
|
|
7
8
|
export function Textarea({ className, autoResize, maxHeight = DEFAULT_MAX_HEIGHT_PX, onChange, ...props }) {
|
|
8
9
|
const ref = useRef(null);
|
|
9
|
-
const resize = (element)=>{
|
|
10
|
+
const resize = (element) => {
|
|
10
11
|
element.style.height = "auto";
|
|
11
12
|
element.style.height = `${Math.min(element.scrollHeight, maxHeight)}px`;
|
|
12
13
|
};
|
|
13
|
-
useEffect(()=>{
|
|
14
|
-
if (autoResize && ref.current)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ref: ref,
|
|
21
|
-
"data-slot": "textarea",
|
|
22
|
-
style: autoResize ? {
|
|
23
|
-
maxHeight
|
|
24
|
-
} : undefined,
|
|
25
|
-
className: cn("flex min-h-16 w-full min-w-0 rounded-md border border-input bg-card px-3 py-2 text-sm shadow-xs transition-colors", autoResize && "resize-none overflow-y-auto", "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
26
|
-
onChange: (event)=>{
|
|
27
|
-
if (autoResize) resize(event.currentTarget);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (autoResize && ref.current)
|
|
16
|
+
resize(ref.current);
|
|
17
|
+
}, [autoResize, maxHeight]);
|
|
18
|
+
return (_jsx("textarea", { ref: ref, "data-slot": "textarea", style: autoResize ? { maxHeight } : undefined, className: cn("flex min-h-16 w-full min-w-0 rounded-md border border-input bg-card px-3 py-2 text-sm shadow-xs transition-colors", autoResize && "resize-none overflow-y-auto", "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className), onChange: (event) => {
|
|
19
|
+
if (autoResize)
|
|
20
|
+
resize(event.currentTarget);
|
|
28
21
|
onChange?.(event);
|
|
29
|
-
},
|
|
30
|
-
...props
|
|
31
|
-
});
|
|
22
|
+
}, ...props }));
|
|
32
23
|
}
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { DEFAULT_THEME_STORAGE_KEY, applyThemeToRoot, nextThemeMode, parseThemePreference, resolveThemeMode, serializeThemePreference } from "../lib/theme.js";
|
|
4
|
-
const ThemeContext =
|
|
5
|
-
function readSystemPrefersDark() {
|
|
6
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
7
|
-
return false;
|
|
8
|
-
}
|
|
9
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
10
|
-
}
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
|
|
4
|
+
import { DEFAULT_THEME_STORAGE_KEY, applyThemeToRoot, nextThemeMode, parseThemePreference, resolveThemeMode, serializeThemePreference, } from "../lib/theme.js";
|
|
5
|
+
const ThemeContext = createContext(null);
|
|
11
6
|
function readStoredPreference(storageKey, fallback) {
|
|
12
|
-
if (typeof window === "undefined")
|
|
7
|
+
if (typeof window === "undefined")
|
|
8
|
+
return fallback;
|
|
13
9
|
try {
|
|
14
10
|
return parseThemePreference(window.localStorage.getItem(storageKey), fallback);
|
|
15
|
-
}
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
16
13
|
return fallback;
|
|
17
14
|
}
|
|
18
15
|
}
|
|
19
16
|
function writeStoredPreference(storageKey, preference) {
|
|
20
|
-
if (typeof window === "undefined")
|
|
17
|
+
if (typeof window === "undefined")
|
|
18
|
+
return;
|
|
21
19
|
try {
|
|
22
20
|
window.localStorage.setItem(storageKey, serializeThemePreference(preference));
|
|
23
|
-
}
|
|
24
|
-
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Quota / private mode — preference still applies for this session.
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
@@ -30,92 +29,69 @@ function writeStoredPreference(storageKey, preference) {
|
|
|
30
29
|
* The host mounts this once near the root and keys `storageKey` by user when
|
|
31
30
|
* signed in. Mode is applied as `.dark` on the root (see theme.css); presets
|
|
32
31
|
* as `data-theme`. Components never import storage — they call `useTheme`.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const [systemPrefersDark, setSystemPrefersDark] = useState(readSystemPrefersDark);
|
|
43
|
-
// Re-read when the host changes the storage key (e.g. user signs in).
|
|
44
|
-
useEffect(()=>{
|
|
32
|
+
*/
|
|
33
|
+
export function ThemeProvider({ children, storageKey = DEFAULT_THEME_STORAGE_KEY, defaultMode = "system", defaultPreset = "default", root = null, }) {
|
|
34
|
+
const fallback = useMemo(() => ({ mode: defaultMode, preset: defaultPreset }), [defaultMode, defaultPreset]);
|
|
35
|
+
// Both start from what the server renders; the effects below read
|
|
36
|
+
// localStorage and matchMedia after mount so hydration never mismatches.
|
|
37
|
+
const [preference, setPreference] = useState(fallback);
|
|
38
|
+
const [systemPrefersDark, setSystemPrefersDark] = useState(false);
|
|
39
|
+
// Re-read on mount and when the host changes the storage key (e.g. user signs in).
|
|
40
|
+
useEffect(() => {
|
|
45
41
|
setPreference(readStoredPreference(storageKey, fallback));
|
|
46
|
-
}, [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
useEffect(()=>{
|
|
51
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
42
|
+
}, [storageKey, fallback]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (typeof window === "undefined" ||
|
|
45
|
+
typeof window.matchMedia !== "function") {
|
|
52
46
|
return;
|
|
53
47
|
}
|
|
54
48
|
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
55
|
-
const onChange = ()=>setSystemPrefersDark(media.matches);
|
|
49
|
+
const onChange = () => setSystemPrefersDark(media.matches);
|
|
56
50
|
onChange();
|
|
57
51
|
media.addEventListener("change", onChange);
|
|
58
|
-
return ()=>media.removeEventListener("change", onChange);
|
|
52
|
+
return () => media.removeEventListener("change", onChange);
|
|
59
53
|
}, []);
|
|
60
54
|
const resolvedMode = resolveThemeMode(preference.mode, systemPrefersDark);
|
|
61
|
-
useEffect(()=>{
|
|
62
|
-
const target = root ??
|
|
63
|
-
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const target = root ??
|
|
57
|
+
(typeof document !== "undefined" ? document.documentElement : null);
|
|
58
|
+
if (target === null)
|
|
59
|
+
return;
|
|
64
60
|
applyThemeToRoot(target, resolvedMode, preference.preset);
|
|
65
|
-
}, [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
]);
|
|
70
|
-
const setMode = useCallback((mode)=>{
|
|
71
|
-
setPreference((current)=>{
|
|
72
|
-
const next = {
|
|
73
|
-
mode,
|
|
74
|
-
preset: current.preset
|
|
75
|
-
};
|
|
61
|
+
}, [root, resolvedMode, preference.preset]);
|
|
62
|
+
const setMode = useCallback((mode) => {
|
|
63
|
+
setPreference((current) => {
|
|
64
|
+
const next = { mode, preset: current.preset };
|
|
76
65
|
writeStoredPreference(storageKey, next);
|
|
77
66
|
return next;
|
|
78
67
|
});
|
|
79
|
-
}, [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
setPreference((current)=>{
|
|
84
|
-
const next = {
|
|
85
|
-
mode: current.mode,
|
|
86
|
-
preset
|
|
87
|
-
};
|
|
68
|
+
}, [storageKey]);
|
|
69
|
+
const setPreset = useCallback((preset) => {
|
|
70
|
+
setPreference((current) => {
|
|
71
|
+
const next = { mode: current.mode, preset };
|
|
88
72
|
writeStoredPreference(storageKey, next);
|
|
89
73
|
return next;
|
|
90
74
|
});
|
|
91
|
-
}, [
|
|
92
|
-
|
|
93
|
-
]);
|
|
94
|
-
const cycleMode = useCallback(()=>{
|
|
75
|
+
}, [storageKey]);
|
|
76
|
+
const cycleMode = useCallback(() => {
|
|
95
77
|
setMode(nextThemeMode(preference.mode));
|
|
96
|
-
}, [
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
preset: preference.preset,
|
|
106
|
-
setPreset
|
|
107
|
-
}), [
|
|
78
|
+
}, [preference.mode, setMode]);
|
|
79
|
+
const value = useMemo(() => ({
|
|
80
|
+
mode: preference.mode,
|
|
81
|
+
setMode,
|
|
82
|
+
cycleMode,
|
|
83
|
+
resolvedMode,
|
|
84
|
+
preset: preference.preset,
|
|
85
|
+
setPreset,
|
|
86
|
+
}), [
|
|
108
87
|
preference.mode,
|
|
109
88
|
preference.preset,
|
|
110
89
|
setMode,
|
|
111
90
|
cycleMode,
|
|
112
91
|
resolvedMode,
|
|
113
|
-
setPreset
|
|
92
|
+
setPreset,
|
|
114
93
|
]);
|
|
115
|
-
return
|
|
116
|
-
value: value,
|
|
117
|
-
children: children
|
|
118
|
-
});
|
|
94
|
+
return (_jsx(ThemeContext.Provider, { value: value, children: children }));
|
|
119
95
|
}
|
|
120
96
|
export function useTheme() {
|
|
121
97
|
const value = useContext(ThemeContext);
|
package/dist/ui/theme-toggle.js
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { Monitor, Moon, Sun } from "lucide-react";
|
|
3
4
|
import { Button } from "./button.js";
|
|
4
5
|
import { useTheme } from "./theme-provider.js";
|
|
5
6
|
function iconFor(mode) {
|
|
6
|
-
if (mode === "light")
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
if (mode === "light")
|
|
8
|
+
return _jsx(Sun, {});
|
|
9
|
+
if (mode === "dark")
|
|
10
|
+
return _jsx(Moon, {});
|
|
11
|
+
return _jsx(Monitor, {});
|
|
9
12
|
}
|
|
10
13
|
function labelFor(mode) {
|
|
11
|
-
if (mode === "light")
|
|
12
|
-
|
|
14
|
+
if (mode === "light")
|
|
15
|
+
return "Light theme";
|
|
16
|
+
if (mode === "dark")
|
|
17
|
+
return "Dark theme";
|
|
13
18
|
return "System theme";
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
16
21
|
* Cycles system → light → dark. Intended for the host identity dock (or any
|
|
17
22
|
* chrome that needs a one-control theme switch). Uses `useTheme`, so it must
|
|
18
23
|
* sit under `ThemeProvider`.
|
|
19
|
-
*/
|
|
24
|
+
*/
|
|
25
|
+
export function ThemeToggle({ label, variant = "ghost", size = "sm", ...props }) {
|
|
20
26
|
const { mode, cycleMode } = useTheme();
|
|
21
27
|
const name = label ?? labelFor(mode);
|
|
22
|
-
return
|
|
23
|
-
...props,
|
|
24
|
-
type: "button",
|
|
25
|
-
variant: variant,
|
|
26
|
-
size: size,
|
|
27
|
-
onClick: cycleMode,
|
|
28
|
-
title: name,
|
|
29
|
-
"aria-label": name,
|
|
30
|
-
children: iconFor(mode)
|
|
31
|
-
});
|
|
28
|
+
return (_jsx(Button, { ...props, type: "button", variant: variant, size: size, onClick: cycleMode, title: name, "aria-label": name, children: iconFor(mode) }));
|
|
32
29
|
}
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { ThinkingMark } from "./thinking-mark.js";
|
|
4
|
-
/** A thinking status with Silk, Strata, or Echo motion. Defaults to Silk. */
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
className: cn("inline-flex items-center gap-1.5 text-sm text-muted-foreground", className),
|
|
8
|
-
style: style,
|
|
9
|
-
children: [
|
|
10
|
-
/*#__PURE__*/ _jsx(ThinkingMark, {
|
|
11
|
-
...markProps,
|
|
12
|
-
className: "-translate-y-[8%]"
|
|
13
|
-
}),
|
|
14
|
-
/*#__PURE__*/ _jsx("span", {
|
|
15
|
-
children: label
|
|
16
|
-
})
|
|
17
|
-
]
|
|
18
|
-
});
|
|
4
|
+
/** A thinking status with Silk, Strata, or Echo motion. Defaults to Silk. */
|
|
5
|
+
export function ThinkingIndicator({ label = "Thinking...", className, style, ...markProps }) {
|
|
6
|
+
return (_jsxs("span", { role: "status", className: cn("inline-flex items-center gap-1.5 text-sm text-muted-foreground", className), style: style, children: [_jsx(ThinkingMark, { ...markProps, className: "-translate-y-[8%]" }), _jsx("span", { children: label })] }));
|
|
19
7
|
}
|