@corbits/react-ui 0.1.0 → 0.2.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 +11 -11
- package/dist/test/render-hook.js +11 -12
- package/dist/theme.css +51 -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 +1 -1
- package/dist/ui/avatar.js +11 -48
- 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 +71 -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 +94 -535
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Bell } from "lucide-react";
|
|
3
4
|
import { useEffect, useId, useRef } from "react";
|
|
@@ -16,54 +17,19 @@ import { cn } from "../lib/utils.js";
|
|
|
16
17
|
*
|
|
17
18
|
* Uncontrolled by default. Pass `open`/`onOpenChange` to drive it from a
|
|
18
19
|
* parent — closing every other panel when this one opens, say.
|
|
19
|
-
*/
|
|
20
|
+
*/
|
|
21
|
+
export function NotificationsBell({ count, children, maxCount = 99, open: openProp, onOpenChange, className, }) {
|
|
20
22
|
const { open, setOpen, rootRef, triggerRef } = useDismissablePopover({
|
|
21
23
|
open: openProp,
|
|
22
|
-
onOpenChange
|
|
24
|
+
onOpenChange,
|
|
23
25
|
});
|
|
24
26
|
const panelRef = useRef(null);
|
|
25
27
|
const panelId = useId();
|
|
26
|
-
useEffect(()=>{
|
|
27
|
-
if (!open)
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!open)
|
|
30
|
+
return;
|
|
28
31
|
panelRef.current?.focus();
|
|
29
|
-
}, [
|
|
30
|
-
open
|
|
31
|
-
]);
|
|
32
|
+
}, [open]);
|
|
32
33
|
const label = count === 0 ? "Notifications" : `Notifications, ${count} unread`;
|
|
33
|
-
return
|
|
34
|
-
ref: rootRef,
|
|
35
|
-
className: cn("relative", className),
|
|
36
|
-
children: [
|
|
37
|
-
/*#__PURE__*/ _jsxs("button", {
|
|
38
|
-
ref: triggerRef,
|
|
39
|
-
type: "button",
|
|
40
|
-
"aria-haspopup": "dialog",
|
|
41
|
-
"aria-expanded": open,
|
|
42
|
-
"aria-controls": open ? panelId : undefined,
|
|
43
|
-
"aria-label": label,
|
|
44
|
-
onClick: ()=>setOpen((value)=>!value),
|
|
45
|
-
className: "relative grid size-9 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
46
|
-
children: [
|
|
47
|
-
/*#__PURE__*/ _jsx(Bell, {
|
|
48
|
-
className: "size-4",
|
|
49
|
-
"aria-hidden": true
|
|
50
|
-
}),
|
|
51
|
-
count > 0 ? /*#__PURE__*/ _jsx("span", {
|
|
52
|
-
"aria-hidden": true,
|
|
53
|
-
className: "absolute top-1 right-1 min-w-4 rounded-full bg-primary px-1 text-center font-mono text-[10px] leading-4 text-primary-foreground",
|
|
54
|
-
children: count > maxCount ? `${maxCount}+` : count
|
|
55
|
-
}) : null
|
|
56
|
-
]
|
|
57
|
-
}),
|
|
58
|
-
open ? /*#__PURE__*/ _jsx("div", {
|
|
59
|
-
ref: panelRef,
|
|
60
|
-
id: panelId,
|
|
61
|
-
role: "dialog",
|
|
62
|
-
"aria-label": label,
|
|
63
|
-
tabIndex: -1,
|
|
64
|
-
className: "absolute top-full right-0 z-50 mt-1 max-h-96 w-80 overflow-y-auto rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-lg",
|
|
65
|
-
children: children
|
|
66
|
-
}) : null
|
|
67
|
-
]
|
|
68
|
-
});
|
|
34
|
+
return (_jsxs("div", { ref: rootRef, className: cn("relative", className), children: [_jsxs("button", { ref: triggerRef, type: "button", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? panelId : undefined, "aria-label": label, onClick: () => setOpen((value) => !value), className: "relative grid size-9 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", children: [_jsx(Bell, { className: "size-4", "aria-hidden": true }), count > 0 ? (_jsx("span", { "aria-hidden": true, className: "absolute top-1 right-1 min-w-4 rounded-full bg-primary px-1 text-center font-mono text-[10px] leading-4 text-primary-foreground", children: count > maxCount ? `${maxCount}+` : count })) : null] }), open ? (_jsx("div", { ref: panelRef, id: panelId, role: "dialog", "aria-label": label, tabIndex: -1, className: "absolute top-full right-0 z-50 mt-1 max-h-96 w-80 overflow-y-auto rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-lg", children: children })) : null] }));
|
|
69
35
|
}
|
package/dist/ui/now-cards.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type NowSectionProps = {
|
|
|
20
20
|
* still resolve) but marks it `inert` — `aria-hidden` alone would hide it from
|
|
21
21
|
* a screen reader while leaving every card in the tab order.
|
|
22
22
|
*/
|
|
23
|
-
export declare function NowSection({ count, collapsed, children, className }: NowSectionProps): import("react").JSX.Element;
|
|
23
|
+
export declare function NowSection({ count, collapsed, children, className, }: NowSectionProps): import("react").JSX.Element;
|
|
24
24
|
export type NowCardsProps = {
|
|
25
25
|
readonly items: readonly NowItem[];
|
|
26
26
|
/** Cap on cards shown. The rest belong in the queue below, not here. */
|
|
@@ -34,4 +34,4 @@ export type NowCardsProps = {
|
|
|
34
34
|
* the list is sorted here by priority then recency, so two surfaces fed the
|
|
35
35
|
* same items always agree on what is most urgent.
|
|
36
36
|
*/
|
|
37
|
-
export declare function NowCards({ items, limit, selectedId, now, className }: NowCardsProps): import("react").JSX.Element | null;
|
|
37
|
+
export declare function NowCards({ items, limit, selectedId, now, className, }: NowCardsProps): import("react").JSX.Element | null;
|
package/dist/ui/now-cards.js
CHANGED
|
@@ -11,106 +11,28 @@ import { Badge } from "./badge.js";
|
|
|
11
11
|
* Collapsing keeps the body mounted (so it can animate, and so anchors into it
|
|
12
12
|
* still resolve) but marks it `inert` — `aria-hidden` alone would hide it from
|
|
13
13
|
* a screen reader while leaving every card in the tab order.
|
|
14
|
-
*/
|
|
14
|
+
*/
|
|
15
|
+
export function NowSection({ count, collapsed = false, children, className, }) {
|
|
15
16
|
const countLabel = count === 1 ? "1 thing needs you" : `${count} things need you`;
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
className: cn("border-b border-border bg-card px-4 py-3 text-card-foreground", className),
|
|
20
|
-
children: [
|
|
21
|
-
/*#__PURE__*/ _jsx("div", {
|
|
22
|
-
className: "flex items-end justify-between gap-2",
|
|
23
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
24
|
-
className: "min-w-0",
|
|
25
|
-
children: [
|
|
26
|
-
/*#__PURE__*/ _jsx("p", {
|
|
27
|
-
className: "text-xs font-semibold tracking-[0.14em] text-primary-emphasis uppercase",
|
|
28
|
-
children: "Now"
|
|
29
|
-
}),
|
|
30
|
-
/*#__PURE__*/ _jsx("p", {
|
|
31
|
-
className: "text-sm font-semibold tracking-tight",
|
|
32
|
-
children: count === 0 ? "Clear" : countLabel
|
|
33
|
-
})
|
|
34
|
-
]
|
|
35
|
-
})
|
|
36
|
-
}),
|
|
37
|
-
/*#__PURE__*/ _jsx("div", {
|
|
38
|
-
className: cn("grid transition-[grid-template-rows,opacity] duration-300", collapsed ? "grid-rows-[0fr] opacity-0" : "mt-3 grid-rows-[1fr] opacity-100"),
|
|
39
|
-
...collapsed ? {
|
|
40
|
-
inert: true
|
|
41
|
-
} : {},
|
|
42
|
-
children: /*#__PURE__*/ _jsx("div", {
|
|
43
|
-
className: "min-h-0 overflow-hidden",
|
|
44
|
-
children: children
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
]
|
|
48
|
-
});
|
|
17
|
+
return (_jsxs("section", { "data-slot": "now-section", "aria-label": "Now", className: cn("border-b border-border bg-card px-4 py-3 text-card-foreground", className), children: [_jsx("div", { className: "flex items-end justify-between gap-2", children: _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-xs font-semibold tracking-[0.14em] text-primary-emphasis uppercase", children: "Now" }), _jsx("p", { className: "text-sm font-semibold tracking-tight", children: count === 0 ? "Clear" : countLabel })] }) }), _jsx("div", { className: cn("grid transition-[grid-template-rows,opacity] duration-300", collapsed
|
|
18
|
+
? "grid-rows-[0fr] opacity-0"
|
|
19
|
+
: "mt-3 grid-rows-[1fr] opacity-100"), ...(collapsed ? { inert: true } : {}), children: _jsx("div", { className: "min-h-0 overflow-hidden", children: children }) })] }));
|
|
49
20
|
}
|
|
50
21
|
/**
|
|
51
22
|
* The highest-attention items as cards. Ordering is not the caller's problem —
|
|
52
23
|
* the list is sorted here by priority then recency, so two surfaces fed the
|
|
53
24
|
* same items always agree on what is most urgent.
|
|
54
|
-
*/
|
|
25
|
+
*/
|
|
26
|
+
export function NowCards({ items, limit = 3, selectedId = null, now, className, }) {
|
|
55
27
|
const shown = sortNowItems(items).slice(0, limit);
|
|
56
|
-
if (shown.length === 0)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
className: cn("grid gap-2.5 sm:grid-cols-2 lg:grid-cols-3", className),
|
|
60
|
-
children: shown.map((item, index)=>{
|
|
28
|
+
if (shown.length === 0)
|
|
29
|
+
return null;
|
|
30
|
+
return (_jsx("ul", { "aria-label": "Needs you now", className: cn("grid gap-2.5 sm:grid-cols-2 lg:grid-cols-3", className), children: shown.map((item, index) => {
|
|
61
31
|
const selected = item.id === selectedId;
|
|
62
|
-
const body =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
className: "grid size-5 shrink-0 place-items-center rounded-full bg-primary/15 text-xs font-bold text-primary-emphasis",
|
|
69
|
-
children: index + 1
|
|
70
|
-
}),
|
|
71
|
-
/*#__PURE__*/ _jsx(Badge, {
|
|
72
|
-
tone: item.priority === "now" ? "accent" : "neutral",
|
|
73
|
-
children: item.classification
|
|
74
|
-
}),
|
|
75
|
-
item.type === "mail" && !item.read ? /*#__PURE__*/ _jsx("span", {
|
|
76
|
-
className: "size-2 shrink-0 rounded-full bg-primary",
|
|
77
|
-
"aria-label": "Unread",
|
|
78
|
-
role: "img"
|
|
79
|
-
}) : null,
|
|
80
|
-
/*#__PURE__*/ _jsx("time", {
|
|
81
|
-
dateTime: item.when,
|
|
82
|
-
className: "ml-auto shrink-0 text-xs text-muted-foreground",
|
|
83
|
-
children: formatRelativeTime(item.when, now)
|
|
84
|
-
})
|
|
85
|
-
]
|
|
86
|
-
}),
|
|
87
|
-
/*#__PURE__*/ _jsx("p", {
|
|
88
|
-
className: "mt-2 line-clamp-2 text-sm leading-snug font-semibold",
|
|
89
|
-
children: item.title
|
|
90
|
-
}),
|
|
91
|
-
item.summary === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
92
|
-
className: "mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground",
|
|
93
|
-
children: item.summary
|
|
94
|
-
}),
|
|
95
|
-
/*#__PURE__*/ _jsx("p", {
|
|
96
|
-
className: "mt-2.5 text-xs font-medium text-primary-emphasis",
|
|
97
|
-
children: item.type === "gate" ? `${item.action} →` : `From ${item.from}`
|
|
98
|
-
})
|
|
99
|
-
]
|
|
100
|
-
});
|
|
101
|
-
const shell = cn("block h-full w-full rounded-lg border p-3.5 text-left transition-colors", selected ? "border-primary-emphasis bg-primary/10" : "border-border bg-background hover:bg-muted");
|
|
102
|
-
return /*#__PURE__*/ _jsx("li", {
|
|
103
|
-
children: item.href === undefined ? /*#__PURE__*/ _jsx("div", {
|
|
104
|
-
"aria-current": selected ? "true" : undefined,
|
|
105
|
-
className: shell,
|
|
106
|
-
children: body
|
|
107
|
-
}) : /*#__PURE__*/ _jsx("a", {
|
|
108
|
-
href: item.href,
|
|
109
|
-
"aria-current": selected ? "true" : undefined,
|
|
110
|
-
className: shell,
|
|
111
|
-
children: body
|
|
112
|
-
})
|
|
113
|
-
}, `${item.type}:${item.id}`);
|
|
114
|
-
})
|
|
115
|
-
});
|
|
32
|
+
const body = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "grid size-5 shrink-0 place-items-center rounded-full bg-primary/15 text-xs font-bold text-primary-emphasis", children: index + 1 }), _jsx(Badge, { tone: item.priority === "now" ? "accent" : "neutral", children: item.classification }), item.type === "mail" && !item.read ? (_jsx("span", { className: "size-2 shrink-0 rounded-full bg-primary", "aria-label": "Unread", role: "img" })) : null, _jsx("time", { dateTime: item.when, className: "ml-auto shrink-0 text-xs text-muted-foreground", children: formatRelativeTime(item.when, now) })] }), _jsx("p", { className: "mt-2 line-clamp-2 text-sm leading-snug font-semibold", children: item.title }), item.summary === undefined ? null : (_jsx("p", { className: "mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground", children: item.summary })), _jsx("p", { className: "mt-2.5 text-xs font-medium text-primary-emphasis", children: item.type === "gate" ? `${item.action} →` : `From ${item.from}` })] }));
|
|
33
|
+
const shell = cn("block h-full w-full rounded-lg border p-3.5 text-left transition-colors", selected
|
|
34
|
+
? "border-primary-emphasis bg-primary/10"
|
|
35
|
+
: "border-border bg-background hover:bg-muted");
|
|
36
|
+
return (_jsx("li", { children: item.href === undefined ? (_jsx("div", { "aria-current": selected ? "true" : undefined, className: shell, children: body })) : (_jsx("a", { href: item.href, "aria-current": selected ? "true" : undefined, className: shell, children: body })) }, `${item.type}:${item.id}`));
|
|
37
|
+
}) }));
|
|
116
38
|
}
|
package/dist/ui/page-panel.js
CHANGED
|
@@ -8,16 +8,16 @@ import { cn } from "../lib/utils.js";
|
|
|
8
8
|
* It is not `PageShell` — that caps and centres a measure of content — and not
|
|
9
9
|
* `Card`, which is a surface within a page. This is the boundary between the
|
|
10
10
|
* app shell and whatever the route renders.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const
|
|
11
|
+
*/
|
|
12
|
+
export function PagePanel({ children, scroll = true, flat = false, fitContent = false, surface = "background", className, }) {
|
|
13
|
+
const sizing = fitContent
|
|
14
|
+
? "w-full max-h-full self-start"
|
|
15
|
+
: "flex-1 border border-border";
|
|
16
|
+
const overflow = scroll
|
|
17
|
+
? fitContent
|
|
18
|
+
? "overflow-y-auto"
|
|
19
|
+
: "min-h-full overflow-y-auto"
|
|
20
|
+
: "overflow-hidden";
|
|
14
21
|
const fill = surface === "card" ? "bg-card" : "bg-background";
|
|
15
|
-
return
|
|
16
|
-
className: cn("flex h-full overflow-hidden", fill),
|
|
17
|
-
children: /*#__PURE__*/ _jsx("section", {
|
|
18
|
-
"data-slot": "page-panel",
|
|
19
|
-
className: cn("flex flex-col", fill, sizing, overflow, !flat && "shadow-sm", className),
|
|
20
|
-
children: children
|
|
21
|
-
})
|
|
22
|
-
});
|
|
22
|
+
return (_jsx("div", { className: cn("flex h-full overflow-hidden", fill), children: _jsx("section", { "data-slot": "page-panel", className: cn("flex flex-col", fill, sizing, overflow, !flat && "shadow-sm", className), children: children }) }));
|
|
23
23
|
}
|
package/dist/ui/page-shell.js
CHANGED
|
@@ -3,7 +3,7 @@ import { cn } from "../lib/utils.js";
|
|
|
3
3
|
const WIDTH_CLASS = {
|
|
4
4
|
prose: "mx-auto w-full max-w-3xl",
|
|
5
5
|
wide: "mx-auto w-full max-w-5xl",
|
|
6
|
-
full: "w-full"
|
|
6
|
+
full: "w-full",
|
|
7
7
|
};
|
|
8
8
|
/**
|
|
9
9
|
* The frame a top-level page sits in: full height, one scroll container, and a
|
|
@@ -13,14 +13,7 @@ const WIDTH_CLASS = {
|
|
|
13
13
|
* of by whichever page was written most recently. It is not `Section` — that is
|
|
14
14
|
* a titled block *within* a page — and it is not `Card`, which is a surface.
|
|
15
15
|
* This is the page's outer boundary and it deliberately draws nothing.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
className: cn("flex h-full flex-col", scroll ? "overflow-y-auto" : "overflow-hidden", className),
|
|
20
|
-
...props,
|
|
21
|
-
children: /*#__PURE__*/ _jsx("div", {
|
|
22
|
-
className: cn("flex min-h-0 flex-1 flex-col gap-6 px-4 py-6", WIDTH_CLASS[width]),
|
|
23
|
-
children: children
|
|
24
|
-
})
|
|
25
|
-
});
|
|
16
|
+
*/
|
|
17
|
+
export function PageShell({ scroll = true, width = "wide", className, children, ...props }) {
|
|
18
|
+
return (_jsx("div", { "data-slot": "page-shell", className: cn("flex h-full flex-col", scroll ? "overflow-y-auto" : "overflow-hidden", className), ...props, children: _jsx("div", { className: cn("flex min-h-0 flex-1 flex-col gap-6 px-4 py-6", WIDTH_CLASS[width]), children: children }) }));
|
|
26
19
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
3
|
import { ChevronRight, Paperclip } from "lucide-react";
|
|
3
4
|
import { useState } from "react";
|
|
4
5
|
import { cn } from "../lib/utils.js";
|
|
5
6
|
import { BlockCard } from "./block-card.js";
|
|
6
7
|
import { ToolBlock } from "./tool-block.js";
|
|
7
|
-
/** `1.2s`, `45s`, `3m 05s` — never a raw millisecond count. */
|
|
8
|
+
/** `1.2s`, `45s`, `3m 05s` — never a raw millisecond count. */
|
|
9
|
+
function formatDuration(ms) {
|
|
8
10
|
const totalSeconds = ms / 1000;
|
|
9
11
|
if (totalSeconds < 60) {
|
|
10
12
|
return `${totalSeconds < 10 ? totalSeconds.toFixed(1) : Math.round(totalSeconds)}s`;
|
|
@@ -13,49 +15,40 @@ import { ToolBlock } from "./tool-block.js";
|
|
|
13
15
|
const seconds = Math.round(totalSeconds % 60);
|
|
14
16
|
return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
|
|
15
17
|
}
|
|
16
|
-
/** Maps the wire's four-outcome status onto `ToolBlock`'s render-side state. */
|
|
17
|
-
|
|
18
|
+
/** Maps the wire's four-outcome status onto `ToolBlock`'s render-side state. */
|
|
19
|
+
export function toolTraceToBlockState(part) {
|
|
20
|
+
switch (part.status) {
|
|
18
21
|
case "pending":
|
|
19
|
-
return {
|
|
20
|
-
status: "pending"
|
|
21
|
-
};
|
|
22
|
+
return { status: "pending" };
|
|
22
23
|
case "running":
|
|
23
|
-
return {
|
|
24
|
-
status: "running"
|
|
25
|
-
};
|
|
24
|
+
return { status: "running" };
|
|
26
25
|
case "output-available":
|
|
27
26
|
return {
|
|
28
27
|
status: "output-available",
|
|
29
|
-
output: stringifyOutput(part.output)
|
|
28
|
+
output: stringifyOutput(part.output),
|
|
30
29
|
};
|
|
31
30
|
case "error":
|
|
32
|
-
return {
|
|
33
|
-
status: "error",
|
|
34
|
-
message: stringifyOutput(part.output)
|
|
35
|
-
};
|
|
31
|
+
return { status: "error", message: stringifyOutput(part.output) };
|
|
36
32
|
case "approval-requested":
|
|
37
33
|
return {
|
|
38
34
|
status: "approval-requested",
|
|
39
|
-
reason: stringifyOutput(part.output)
|
|
35
|
+
reason: stringifyOutput(part.output),
|
|
40
36
|
};
|
|
41
37
|
case "output-denied":
|
|
42
|
-
return {
|
|
43
|
-
status: "output-denied",
|
|
44
|
-
reason: stringifyOutput(part.output)
|
|
45
|
-
};
|
|
38
|
+
return { status: "output-denied", reason: stringifyOutput(part.output) };
|
|
46
39
|
}
|
|
47
40
|
}
|
|
48
41
|
function stringifyOutput(output) {
|
|
49
|
-
if (output === undefined)
|
|
50
|
-
|
|
42
|
+
if (output === undefined)
|
|
43
|
+
return "";
|
|
44
|
+
if (typeof output === "string")
|
|
45
|
+
return output;
|
|
51
46
|
return JSON.stringify(output, null, 2);
|
|
52
47
|
}
|
|
53
48
|
function TextPartView({ text }) {
|
|
54
|
-
if (text.trim().length === 0)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
children: text
|
|
58
|
-
});
|
|
49
|
+
if (text.trim().length === 0)
|
|
50
|
+
return null;
|
|
51
|
+
return (_jsx("p", { className: "whitespace-pre-wrap text-sm leading-relaxed text-foreground", children: text }));
|
|
59
52
|
}
|
|
60
53
|
/**
|
|
61
54
|
* The agent's thinking, folded away, with how long it took once known.
|
|
@@ -64,95 +57,35 @@ function TextPartView({ text }) {
|
|
|
64
57
|
* mid-stream makes the transcript jump under the reader. A native
|
|
65
58
|
* `<details>`, not a div with state — it gets disclosure semantics, keyboard
|
|
66
59
|
* behaviour and find-in-page expansion from the browser for free.
|
|
67
|
-
*/
|
|
60
|
+
*/
|
|
61
|
+
function ReasoningPartView({ text, durationMs, }) {
|
|
68
62
|
const [open, setOpen] = useState(false);
|
|
69
|
-
if (text.trim().length === 0)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
onToggle: (event)=>setOpen(event.currentTarget.open),
|
|
74
|
-
className: "text-xs",
|
|
75
|
-
children: [
|
|
76
|
-
/*#__PURE__*/ _jsxs("summary", {
|
|
77
|
-
className: cn(// `relative` plus the `::after` pseudo-element extends the
|
|
63
|
+
if (text.trim().length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
return (_jsxs("details", { "data-slot": "reasoning-part", open: open, onToggle: (event) => setOpen(event.currentTarget.open), className: "text-xs", children: [_jsxs("summary", { className: cn(
|
|
66
|
+
// `relative` plus the `::after` pseudo-element extends the
|
|
78
67
|
// effective hit area to 40px tall without changing the row's own
|
|
79
68
|
// padding/density — see design-engineering.md's hit-area pattern.
|
|
80
|
-
"relative flex cursor-pointer list-none items-center gap-1.5 rounded-md px-2 py-1.5 text-muted-foreground transition-colors after:absolute after:inset-x-0 after:top-1/2 after:h-10 after:-translate-y-1/2 hover:bg-muted hover:text-foreground", "[&::-webkit-details-marker]:hidden"),
|
|
81
|
-
children: [
|
|
82
|
-
/*#__PURE__*/ _jsx(ChevronRight, {
|
|
83
|
-
className: cn("size-3.5 shrink-0 transition-transform duration-200 ease-out", open && "rotate-90"),
|
|
84
|
-
"aria-hidden": true
|
|
85
|
-
}),
|
|
86
|
-
/*#__PURE__*/ _jsxs("span", {
|
|
87
|
-
children: [
|
|
88
|
-
"Thought",
|
|
89
|
-
durationMs === undefined ? "" : ` for ${formatDuration(durationMs)}`
|
|
90
|
-
]
|
|
91
|
-
})
|
|
92
|
-
]
|
|
93
|
-
}),
|
|
94
|
-
/*#__PURE__*/ _jsx("p", {
|
|
95
|
-
className: "mt-1 ml-4 border-l border-border pl-3 leading-relaxed whitespace-pre-wrap text-muted-foreground [animation:corbits-rail-block-in_200ms_var(--ease-out)_both]",
|
|
96
|
-
children: text
|
|
97
|
-
}, open ? "open" : "closed")
|
|
98
|
-
]
|
|
99
|
-
});
|
|
69
|
+
"relative flex cursor-pointer list-none items-center gap-1.5 rounded-md px-2 py-1.5 text-muted-foreground transition-colors after:absolute after:inset-x-0 after:top-1/2 after:h-10 after:-translate-y-1/2 hover:bg-muted hover:text-foreground", "[&::-webkit-details-marker]:hidden"), children: [_jsx(ChevronRight, { className: cn("size-3.5 shrink-0 transition-transform duration-200 ease-out", open && "rotate-90"), "aria-hidden": true }), _jsxs("span", { children: ["Thought", durationMs === undefined ? "" : ` for ${formatDuration(durationMs)}`] })] }), _jsx("p", { className: "mt-1 ml-4 border-l border-border pl-3 leading-relaxed whitespace-pre-wrap text-muted-foreground [animation:corbits-rail-block-in_200ms_var(--ease-out)_both]", children: text }, open ? "open" : "closed")] }));
|
|
100
70
|
}
|
|
101
|
-
function FilePartView({ name, mediaType, url }) {
|
|
102
|
-
const content =
|
|
103
|
-
children: [
|
|
104
|
-
/*#__PURE__*/ _jsx(Paperclip, {
|
|
105
|
-
className: "size-3.5 shrink-0",
|
|
106
|
-
"aria-hidden": true
|
|
107
|
-
}),
|
|
108
|
-
/*#__PURE__*/ _jsx("span", {
|
|
109
|
-
className: "min-w-0 flex-1 truncate",
|
|
110
|
-
children: name
|
|
111
|
-
}),
|
|
112
|
-
/*#__PURE__*/ _jsx("span", {
|
|
113
|
-
className: "shrink-0 text-muted-foreground",
|
|
114
|
-
children: mediaType
|
|
115
|
-
})
|
|
116
|
-
]
|
|
117
|
-
});
|
|
71
|
+
function FilePartView({ name, mediaType, url, }) {
|
|
72
|
+
const content = (_jsxs(_Fragment, { children: [_jsx(Paperclip, { className: "size-3.5 shrink-0", "aria-hidden": true }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: name }), _jsx("span", { className: "shrink-0 text-muted-foreground", children: mediaType })] }));
|
|
118
73
|
if (url === undefined) {
|
|
119
|
-
return
|
|
120
|
-
"data-slot": "file-part",
|
|
121
|
-
className: "inline-flex max-w-full items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5 text-xs text-foreground",
|
|
122
|
-
children: content
|
|
123
|
-
});
|
|
74
|
+
return (_jsx("span", { "data-slot": "file-part", className: "inline-flex max-w-full items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5 text-xs text-foreground", children: content }));
|
|
124
75
|
}
|
|
125
|
-
return
|
|
126
|
-
"data-slot": "file-part",
|
|
127
|
-
href: url,
|
|
128
|
-
target: "_blank",
|
|
129
|
-
rel: "noreferrer",
|
|
130
|
-
className: "inline-flex max-w-full items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5 text-xs text-foreground transition-colors hover:bg-muted",
|
|
131
|
-
children: content
|
|
132
|
-
});
|
|
76
|
+
return (_jsx("a", { "data-slot": "file-part", href: url, target: "_blank", rel: "noreferrer", className: "inline-flex max-w-full items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5 text-xs text-foreground transition-colors hover:bg-muted", children: content }));
|
|
133
77
|
}
|
|
134
78
|
function EventPartView({ event }) {
|
|
135
79
|
const label = event.replace(/[.-]+/g, " ");
|
|
136
|
-
return
|
|
137
|
-
"data-slot": "event-part",
|
|
138
|
-
className: "flex items-center gap-2 py-1 text-xs text-muted-foreground italic",
|
|
139
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
140
|
-
children: label
|
|
141
|
-
})
|
|
142
|
-
});
|
|
80
|
+
return (_jsx("div", { "data-slot": "event-part", className: "flex items-center gap-2 py-1 text-xs text-muted-foreground italic", children: _jsx("span", { children: label }) }));
|
|
143
81
|
}
|
|
144
82
|
/**
|
|
145
83
|
* A block part this renderer has no host-specific registry for. Phase 1
|
|
146
84
|
* ships no generative-UI JSON-spec renderer, so a block always falls back to
|
|
147
85
|
* its type name and raw payload rather than going unrendered.
|
|
148
|
-
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
children: /*#__PURE__*/ _jsx("pre", {
|
|
152
|
-
className: "max-h-64 overflow-auto font-mono text-[11px] whitespace-pre-wrap text-muted-foreground",
|
|
153
|
-
children: JSON.stringify(data, null, 2)
|
|
154
|
-
})
|
|
155
|
-
});
|
|
86
|
+
*/
|
|
87
|
+
function BlockPartFallback({ type, data }) {
|
|
88
|
+
return (_jsx(BlockCard, { title: type, children: _jsx("pre", { className: "max-h-64 overflow-auto font-mono text-[11px] whitespace-pre-wrap text-muted-foreground", children: JSON.stringify(data, null, 2) }) }));
|
|
156
89
|
}
|
|
157
90
|
/**
|
|
158
91
|
* A `Part` whose `kind` this renderer has never heard of — a wire value
|
|
@@ -161,18 +94,9 @@ function EventPartView({ event }) {
|
|
|
161
94
|
* neutral labeled card naming the unrecognized kind rather than rendering
|
|
162
95
|
* nothing, so a producer/consumer version skew is visible in the transcript
|
|
163
96
|
* instead of silently swallowing content.
|
|
164
|
-
*/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
className: "rounded-md border border-dashed border-border bg-muted/30 px-2.5 py-1.5 text-xs text-muted-foreground",
|
|
168
|
-
children: [
|
|
169
|
-
"Unsupported part: ",
|
|
170
|
-
/*#__PURE__*/ _jsx("span", {
|
|
171
|
-
className: "font-mono",
|
|
172
|
-
children: kind
|
|
173
|
-
})
|
|
174
|
-
]
|
|
175
|
-
});
|
|
97
|
+
*/
|
|
98
|
+
function UnknownPartFallback({ kind }) {
|
|
99
|
+
return (_jsxs("div", { "data-slot": "unknown-part", className: "rounded-md border border-dashed border-border bg-muted/30 px-2.5 py-1.5 text-xs text-muted-foreground", children: ["Unsupported part: ", _jsx("span", { className: "font-mono", children: kind })] }));
|
|
176
100
|
}
|
|
177
101
|
/**
|
|
178
102
|
* Renders an ordered `Part[]` in the order the model produced it: text as
|
|
@@ -186,49 +110,27 @@ function EventPartView({ event }) {
|
|
|
186
110
|
* An empty `parts` array renders nothing — no placeholder, no empty-state
|
|
187
111
|
* chrome. A message with no parts is not a message a transcript should show
|
|
188
112
|
* anything for.
|
|
189
|
-
*/
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
children: parts.map((part, index)=>{
|
|
113
|
+
*/
|
|
114
|
+
export function PartsRenderer({ parts, className }) {
|
|
115
|
+
if (parts.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
return (_jsx("div", { "data-slot": "parts-renderer", className: cn("flex flex-col gap-1.5", className), children: parts.map((part, index) => {
|
|
195
118
|
const key = `${part.kind}-${index}`;
|
|
196
|
-
switch(part.kind){
|
|
119
|
+
switch (part.kind) {
|
|
197
120
|
case "text":
|
|
198
|
-
return
|
|
199
|
-
text: part.text
|
|
200
|
-
}, key);
|
|
121
|
+
return _jsx(TextPartView, { text: part.text }, key);
|
|
201
122
|
case "reasoning":
|
|
202
|
-
return
|
|
203
|
-
text: part.text,
|
|
204
|
-
durationMs: part.durationMs
|
|
205
|
-
}, key);
|
|
123
|
+
return (_jsx(ReasoningPartView, { text: part.text, durationMs: part.durationMs }, key));
|
|
206
124
|
case "tool-trace":
|
|
207
|
-
return
|
|
208
|
-
name: part.name,
|
|
209
|
-
input: part.input,
|
|
210
|
-
state: toolTraceToBlockState(part)
|
|
211
|
-
}, key);
|
|
125
|
+
return (_jsx(ToolBlock, { name: part.name, input: part.input, state: toolTraceToBlockState(part) }, key));
|
|
212
126
|
case "file":
|
|
213
|
-
return
|
|
214
|
-
name: part.name,
|
|
215
|
-
mediaType: part.mediaType,
|
|
216
|
-
url: part.url
|
|
217
|
-
}, key);
|
|
127
|
+
return (_jsx(FilePartView, { name: part.name, mediaType: part.mediaType, url: part.url }, key));
|
|
218
128
|
case "event":
|
|
219
|
-
return
|
|
220
|
-
event: part.event
|
|
221
|
-
}, key);
|
|
129
|
+
return _jsx(EventPartView, { event: part.event }, key);
|
|
222
130
|
case "block":
|
|
223
|
-
return
|
|
224
|
-
type: part.block.type,
|
|
225
|
-
data: part.block.data
|
|
226
|
-
}, key);
|
|
131
|
+
return (_jsx(BlockPartFallback, { type: part.block.type, data: part.block.data }, key));
|
|
227
132
|
default:
|
|
228
|
-
return
|
|
229
|
-
kind: part.kind
|
|
230
|
-
}, key);
|
|
133
|
+
return (_jsx(UnknownPartFallback, { kind: part.kind }, key));
|
|
231
134
|
}
|
|
232
|
-
})
|
|
233
|
-
});
|
|
135
|
+
}) }));
|
|
234
136
|
}
|