@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.
Files changed (170) hide show
  1. package/LICENSE +174 -499
  2. package/README.md +228 -85
  3. package/dist/blocks/login/auth-layout.d.ts +1 -1
  4. package/dist/blocks/login/auth-layout.js +3 -33
  5. package/dist/blocks/login/login-form.js +6 -119
  6. package/dist/hooks/use-command-palette-navigation.js +18 -17
  7. package/dist/hooks/use-controllable-state.d.ts +2 -2
  8. package/dist/hooks/use-controllable-state.js +23 -15
  9. package/dist/hooks/use-delayed-autofocus.js +10 -11
  10. package/dist/hooks/use-dismissable-popover.d.ts +2 -2
  11. package/dist/hooks/use-dismissable-popover.js +21 -29
  12. package/dist/hooks/use-list-selection.js +54 -85
  13. package/dist/hooks/use-prefers-reduced-motion.js +8 -4
  14. package/dist/hooks/use-scroll-current-into-view.js +5 -7
  15. package/dist/index.d.ts +0 -11
  16. package/dist/index.js +10 -19
  17. package/dist/lib/artifact.js +16 -35
  18. package/dist/lib/chart-geometry.js +33 -20
  19. package/dist/lib/chart-palette.js +17 -31
  20. package/dist/lib/chat-message.js +32 -13
  21. package/dist/lib/chat-parts.js +2 -1
  22. package/dist/lib/csv.js +42 -26
  23. package/dist/lib/metrics.js +30 -48
  24. package/dist/lib/now-item.js +26 -25
  25. package/dist/lib/relative-time.js +14 -29
  26. package/dist/lib/theme.js +32 -30
  27. package/dist/lib/url.js +9 -8
  28. package/dist/lib/workflow-display-flow.js +37 -33
  29. package/dist/lib/workflow-registry.d.ts +0 -41
  30. package/dist/lib/workflow-registry.js +13 -23
  31. package/dist/lib/workflow-run-error.js +16 -20
  32. package/dist/lib/workflow-run-progress.js +94 -52
  33. package/dist/lib/workflow-run.js +17 -13
  34. package/dist/styles.css +11 -11
  35. package/dist/test/render-hook.js +11 -12
  36. package/dist/theme.css +51 -14
  37. package/dist/ui/agent-turn.d.ts +1 -1
  38. package/dist/ui/agent-turn.js +4 -37
  39. package/dist/ui/animated-number.js +15 -28
  40. package/dist/ui/approval-card.js +5 -90
  41. package/dist/ui/artifact-body.js +47 -125
  42. package/dist/ui/artifact-notice.d.ts +1 -1
  43. package/dist/ui/artifact-notice.js +9 -16
  44. package/dist/ui/avatar.d.ts +1 -1
  45. package/dist/ui/avatar.js +11 -48
  46. package/dist/ui/badge.js +4 -12
  47. package/dist/ui/bar-chart.d.ts +1 -1
  48. package/dist/ui/bar-chart.js +22 -74
  49. package/dist/ui/block-card.js +7 -45
  50. package/dist/ui/boot-screen.d.ts +1 -1
  51. package/dist/ui/boot-screen.js +4 -33
  52. package/dist/ui/bulk-action-bar.d.ts +1 -1
  53. package/dist/ui/bulk-action-bar.js +14 -33
  54. package/dist/ui/button.js +10 -24
  55. package/dist/ui/card.js +5 -25
  56. package/dist/ui/category-bars.js +14 -59
  57. package/dist/ui/chart-frame.d.ts +2 -2
  58. package/dist/ui/chart-frame.js +6 -85
  59. package/dist/ui/chat-composer.js +4 -16
  60. package/dist/ui/chat-dock-timing.js +7 -3
  61. package/dist/ui/chat-input.js +20 -100
  62. package/dist/ui/chat-panel.d.ts +1 -1
  63. package/dist/ui/chat-panel.js +7 -41
  64. package/dist/ui/chat-thread.js +13 -31
  65. package/dist/ui/checkbox.js +10 -52
  66. package/dist/ui/command-palette.js +71 -246
  67. package/dist/ui/compare-body.d.ts +1 -1
  68. package/dist/ui/compare-body.js +28 -98
  69. package/dist/ui/confirm-button.js +8 -15
  70. package/dist/ui/corbits-mark.js +7 -16
  71. package/dist/ui/csv-table.d.ts +1 -1
  72. package/dist/ui/csv-table.js +8 -50
  73. package/dist/ui/dialog.d.ts +1 -1
  74. package/dist/ui/dialog.js +32 -62
  75. package/dist/ui/dictation-status-line.js +7 -25
  76. package/dist/ui/dither-canvas.d.ts +1 -1
  77. package/dist/ui/dither-canvas.js +31 -56
  78. package/dist/ui/embed-body.d.ts +1 -1
  79. package/dist/ui/embed-body.js +7 -56
  80. package/dist/ui/empty-state.d.ts +1 -1
  81. package/dist/ui/empty-state.js +3 -26
  82. package/dist/ui/file-input.js +17 -37
  83. package/dist/ui/filter-bar.d.ts +1 -1
  84. package/dist/ui/filter-bar.js +11 -71
  85. package/dist/ui/filter-chip.d.ts +1 -1
  86. package/dist/ui/filter-chip.js +7 -15
  87. package/dist/ui/gate-block.d.ts +2 -1
  88. package/dist/ui/gate-block.js +4 -29
  89. package/dist/ui/horizontal-stepper.d.ts +1 -1
  90. package/dist/ui/horizontal-stepper.js +18 -45
  91. package/dist/ui/input.js +1 -6
  92. package/dist/ui/inspector-shell.d.ts +5 -5
  93. package/dist/ui/inspector-shell.js +14 -81
  94. package/dist/ui/intake-form.d.ts +1 -1
  95. package/dist/ui/intake-form.js +21 -97
  96. package/dist/ui/kind-card-grid.js +9 -31
  97. package/dist/ui/library-page-header.js +4 -32
  98. package/dist/ui/list-detail.js +5 -35
  99. package/dist/ui/live-run-banner.d.ts +1 -1
  100. package/dist/ui/live-run-banner.js +5 -15
  101. package/dist/ui/live-status-line.js +6 -24
  102. package/dist/ui/menu.js +7 -24
  103. package/dist/ui/message-bubble.d.ts +3 -3
  104. package/dist/ui/message-bubble.js +5 -44
  105. package/dist/ui/mic-button.d.ts +1 -1
  106. package/dist/ui/mic-button.js +15 -21
  107. package/dist/ui/mic-permission-dialog.js +5 -41
  108. package/dist/ui/notifications-bell.js +9 -43
  109. package/dist/ui/now-cards.d.ts +2 -2
  110. package/dist/ui/now-cards.js +16 -94
  111. package/dist/ui/page-panel.js +11 -11
  112. package/dist/ui/page-shell.js +4 -11
  113. package/dist/ui/parts-renderer.js +51 -149
  114. package/dist/ui/profile-card.js +7 -111
  115. package/dist/ui/progress-checklist.js +11 -49
  116. package/dist/ui/provider-mark.d.ts +2 -2
  117. package/dist/ui/provider-mark.js +9 -16
  118. package/dist/ui/quick-reply-chips.js +6 -14
  119. package/dist/ui/quote-card.js +12 -32
  120. package/dist/ui/reasoning-block.d.ts +1 -1
  121. package/dist/ui/reasoning-block.js +14 -33
  122. package/dist/ui/research-body.js +32 -213
  123. package/dist/ui/rich-empty-state.js +6 -48
  124. package/dist/ui/run-now-button.js +14 -15
  125. package/dist/ui/section.d.ts +1 -1
  126. package/dist/ui/section.js +4 -40
  127. package/dist/ui/select.js +1 -15
  128. package/dist/ui/selection-checkbox.d.ts +1 -1
  129. package/dist/ui/selection-checkbox.js +11 -29
  130. package/dist/ui/settings-panel.js +7 -50
  131. package/dist/ui/shimmer-text.js +3 -6
  132. package/dist/ui/sidebar-item-row.d.ts +1 -1
  133. package/dist/ui/sidebar-item-row.js +9 -39
  134. package/dist/ui/sidebar-panel.js +15 -40
  135. package/dist/ui/sidebar.js +15 -76
  136. package/dist/ui/skeleton.js +3 -7
  137. package/dist/ui/sortable-table.js +29 -72
  138. package/dist/ui/sparkline.js +14 -38
  139. package/dist/ui/stat-grid.js +17 -52
  140. package/dist/ui/status-dot.d.ts +1 -1
  141. package/dist/ui/status-dot.js +8 -20
  142. package/dist/ui/step-list.d.ts +1 -0
  143. package/dist/ui/step-list.js +11 -50
  144. package/dist/ui/switch.js +4 -16
  145. package/dist/ui/table.js +7 -32
  146. package/dist/ui/tabs.js +39 -54
  147. package/dist/ui/textarea.js +10 -19
  148. package/dist/ui/theme-provider.js +54 -78
  149. package/dist/ui/theme-toggle.js +13 -16
  150. package/dist/ui/thinking-indicator.js +3 -15
  151. package/dist/ui/thinking-label.js +16 -35
  152. package/dist/ui/thinking-mark.d.ts +1 -1
  153. package/dist/ui/thinking-mark.js +4 -145
  154. package/dist/ui/time-series-chart.js +43 -174
  155. package/dist/ui/toast.js +12 -20
  156. package/dist/ui/token-mosaic.js +5 -43
  157. package/dist/ui/tool-block.js +18 -70
  158. package/dist/ui/tool-narrative.d.ts +1 -1
  159. package/dist/ui/tool-narrative.js +10 -57
  160. package/dist/ui/tool-picker.js +57 -145
  161. package/dist/ui/tooltip.d.ts +1 -1
  162. package/dist/ui/tooltip.js +4 -33
  163. package/dist/ui/top-bar.js +14 -72
  164. package/dist/ui/trace-waterfall.d.ts +1 -1
  165. package/dist/ui/trace-waterfall.js +21 -93
  166. package/dist/ui/typing-indicator.d.ts +1 -1
  167. package/dist/ui/typing-indicator.js +4 -18
  168. package/dist/ui/view-toggle.js +10 -29
  169. package/dist/ui/voice-waveform.js +3 -11
  170. 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
- */ export function NotificationsBell({ count, children, maxCount = 99, open: openProp, onOpenChange, className }) {
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) return;
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 /*#__PURE__*/ _jsxs("div", {
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
  }
@@ -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;
@@ -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
- */ export function NowSection({ count, collapsed = false, children, className }) {
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 /*#__PURE__*/ _jsxs("section", {
17
- "data-slot": "now-section",
18
- "aria-label": "Now",
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
- */ export function NowCards({ items, limit = 3, selectedId = null, now, className }) {
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) return null;
57
- return /*#__PURE__*/ _jsx("ul", {
58
- "aria-label": "Needs you now",
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 = /*#__PURE__*/ _jsxs(_Fragment, {
63
- children: [
64
- /*#__PURE__*/ _jsxs("div", {
65
- className: "flex items-center gap-2",
66
- children: [
67
- /*#__PURE__*/ _jsx("span", {
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
  }
@@ -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
- */ export function PagePanel({ children, scroll = true, flat = false, fitContent = false, surface = "background", className }) {
12
- const sizing = fitContent ? "w-full max-h-full self-start" : "flex-1 border border-border";
13
- const overflow = scroll ? fitContent ? "overflow-y-auto" : "min-h-full overflow-y-auto" : "overflow-hidden";
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 /*#__PURE__*/ _jsx("div", {
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
  }
@@ -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
- */ export function PageShell({ scroll = true, width = "wide", className, children, ...props }) {
17
- return /*#__PURE__*/ _jsx("div", {
18
- "data-slot": "page-shell",
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. */ function formatDuration(ms) {
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. */ export function toolTraceToBlockState(part) {
17
- switch(part.status){
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) return "";
50
- if (typeof output === "string") return output;
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) return null;
55
- return /*#__PURE__*/ _jsx("p", {
56
- className: "whitespace-pre-wrap text-sm leading-relaxed text-foreground",
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
- */ function ReasoningPartView({ text, durationMs }) {
60
+ */
61
+ function ReasoningPartView({ text, durationMs, }) {
68
62
  const [open, setOpen] = useState(false);
69
- if (text.trim().length === 0) return null;
70
- return /*#__PURE__*/ _jsxs("details", {
71
- "data-slot": "reasoning-part",
72
- open: open,
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 = /*#__PURE__*/ _jsxs(_Fragment, {
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 /*#__PURE__*/ _jsx("span", {
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 /*#__PURE__*/ _jsx("a", {
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 /*#__PURE__*/ _jsx("div", {
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
- */ function BlockPartFallback({ type, data }) {
149
- return /*#__PURE__*/ _jsx(BlockCard, {
150
- title: type,
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
- */ function UnknownPartFallback({ kind }) {
165
- return /*#__PURE__*/ _jsxs("div", {
166
- "data-slot": "unknown-part",
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
- */ export function PartsRenderer({ parts, className }) {
190
- if (parts.length === 0) return null;
191
- return /*#__PURE__*/ _jsx("div", {
192
- "data-slot": "parts-renderer",
193
- className: cn("flex flex-col gap-1.5", className),
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 /*#__PURE__*/ _jsx(TextPartView, {
199
- text: part.text
200
- }, key);
121
+ return _jsx(TextPartView, { text: part.text }, key);
201
122
  case "reasoning":
202
- return /*#__PURE__*/ _jsx(ReasoningPartView, {
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 /*#__PURE__*/ _jsx(ToolBlock, {
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 /*#__PURE__*/ _jsx(FilePartView, {
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 /*#__PURE__*/ _jsx(EventPartView, {
220
- event: part.event
221
- }, key);
129
+ return _jsx(EventPartView, { event: part.event }, key);
222
130
  case "block":
223
- return /*#__PURE__*/ _jsx(BlockPartFallback, {
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 /*#__PURE__*/ _jsx(UnknownPartFallback, {
229
- kind: part.kind
230
- }, key);
133
+ return (_jsx(UnknownPartFallback, { kind: part.kind }, key));
231
134
  }
232
- })
233
- });
135
+ }) }));
234
136
  }