@corbits/react-ui 0.1.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 (255) hide show
  1. package/LICENSE +501 -0
  2. package/README.md +125 -0
  3. package/dist/blocks/login/auth-layout.d.ts +38 -0
  4. package/dist/blocks/login/auth-layout.js +52 -0
  5. package/dist/blocks/login/login-form.d.ts +60 -0
  6. package/dist/blocks/login/login-form.js +151 -0
  7. package/dist/hooks/use-command-palette-navigation.d.ts +25 -0
  8. package/dist/hooks/use-command-palette-navigation.js +52 -0
  9. package/dist/hooks/use-controllable-state.d.ts +28 -0
  10. package/dist/hooks/use-controllable-state.js +44 -0
  11. package/dist/hooks/use-delayed-autofocus.d.ts +9 -0
  12. package/dist/hooks/use-delayed-autofocus.js +20 -0
  13. package/dist/hooks/use-dismissable-popover.d.ts +37 -0
  14. package/dist/hooks/use-dismissable-popover.js +64 -0
  15. package/dist/hooks/use-list-selection.d.ts +66 -0
  16. package/dist/hooks/use-list-selection.js +147 -0
  17. package/dist/hooks/use-prefers-reduced-motion.d.ts +13 -0
  18. package/dist/hooks/use-prefers-reduced-motion.js +28 -0
  19. package/dist/hooks/use-scroll-current-into-view.d.ts +19 -0
  20. package/dist/hooks/use-scroll-current-into-view.js +33 -0
  21. package/dist/index.d.ts +122 -0
  22. package/dist/index.js +131 -0
  23. package/dist/lib/artifact.d.ts +72 -0
  24. package/dist/lib/artifact.js +67 -0
  25. package/dist/lib/chart-geometry.d.ts +62 -0
  26. package/dist/lib/chart-geometry.js +86 -0
  27. package/dist/lib/chart-palette.d.ts +76 -0
  28. package/dist/lib/chart-palette.js +92 -0
  29. package/dist/lib/chat-message.d.ts +115 -0
  30. package/dist/lib/chat-message.js +41 -0
  31. package/dist/lib/chat-parts.d.ts +64 -0
  32. package/dist/lib/chat-parts.js +16 -0
  33. package/dist/lib/csv.d.ts +60 -0
  34. package/dist/lib/csv.js +108 -0
  35. package/dist/lib/metrics.d.ts +70 -0
  36. package/dist/lib/metrics.js +101 -0
  37. package/dist/lib/now-item.d.ts +65 -0
  38. package/dist/lib/now-item.js +59 -0
  39. package/dist/lib/relative-time.d.ts +9 -0
  40. package/dist/lib/relative-time.js +47 -0
  41. package/dist/lib/theme.d.ts +30 -0
  42. package/dist/lib/theme.js +77 -0
  43. package/dist/lib/url.d.ts +26 -0
  44. package/dist/lib/url.js +37 -0
  45. package/dist/lib/utils.d.ts +2 -0
  46. package/dist/lib/utils.js +5 -0
  47. package/dist/lib/workflow-display-flow.d.ts +83 -0
  48. package/dist/lib/workflow-display-flow.js +104 -0
  49. package/dist/lib/workflow-registry.d.ts +90 -0
  50. package/dist/lib/workflow-registry.js +56 -0
  51. package/dist/lib/workflow-run-error.d.ts +31 -0
  52. package/dist/lib/workflow-run-error.js +105 -0
  53. package/dist/lib/workflow-run-progress.d.ts +97 -0
  54. package/dist/lib/workflow-run-progress.js +135 -0
  55. package/dist/lib/workflow-run.d.ts +43 -0
  56. package/dist/lib/workflow-run.js +56 -0
  57. package/dist/styles.css +3080 -0
  58. package/dist/test/render-hook.d.ts +14 -0
  59. package/dist/test/render-hook.js +38 -0
  60. package/dist/theme.css +628 -0
  61. package/dist/ui/agent-turn.d.ts +26 -0
  62. package/dist/ui/agent-turn.js +60 -0
  63. package/dist/ui/animated-number.d.ts +25 -0
  64. package/dist/ui/animated-number.js +63 -0
  65. package/dist/ui/approval-card.d.ts +46 -0
  66. package/dist/ui/approval-card.js +116 -0
  67. package/dist/ui/artifact-body.d.ts +26 -0
  68. package/dist/ui/artifact-body.js +170 -0
  69. package/dist/ui/artifact-notice.d.ts +21 -0
  70. package/dist/ui/artifact-notice.js +36 -0
  71. package/dist/ui/avatar.d.ts +37 -0
  72. package/dist/ui/avatar.js +73 -0
  73. package/dist/ui/badge.d.ts +10 -0
  74. package/dist/ui/badge.js +35 -0
  75. package/dist/ui/bar-chart.d.ts +38 -0
  76. package/dist/ui/bar-chart.js +118 -0
  77. package/dist/ui/block-card.d.ts +29 -0
  78. package/dist/ui/block-card.js +66 -0
  79. package/dist/ui/boot-screen.d.ts +23 -0
  80. package/dist/ui/boot-screen.js +49 -0
  81. package/dist/ui/bulk-action-bar.d.ts +37 -0
  82. package/dist/ui/bulk-action-bar.js +64 -0
  83. package/dist/ui/button.d.ts +12 -0
  84. package/dist/ui/button.js +73 -0
  85. package/dist/ui/card.d.ts +6 -0
  86. package/dist/ui/card.js +37 -0
  87. package/dist/ui/category-bars.d.ts +20 -0
  88. package/dist/ui/category-bars.js +69 -0
  89. package/dist/ui/chart-frame.d.ts +54 -0
  90. package/dist/ui/chart-frame.js +108 -0
  91. package/dist/ui/chat-composer.d.ts +24 -0
  92. package/dist/ui/chat-composer.js +32 -0
  93. package/dist/ui/chat-dock-timing.d.ts +15 -0
  94. package/dist/ui/chat-dock-timing.js +11 -0
  95. package/dist/ui/chat-input.d.ts +38 -0
  96. package/dist/ui/chat-input.js +138 -0
  97. package/dist/ui/chat-panel.d.ts +20 -0
  98. package/dist/ui/chat-panel.js +56 -0
  99. package/dist/ui/chat-thread.d.ts +28 -0
  100. package/dist/ui/chat-thread.js +61 -0
  101. package/dist/ui/checkbox.d.ts +25 -0
  102. package/dist/ui/checkbox.js +63 -0
  103. package/dist/ui/command-palette.d.ts +100 -0
  104. package/dist/ui/command-palette.js +341 -0
  105. package/dist/ui/compare-body.d.ts +52 -0
  106. package/dist/ui/compare-body.js +140 -0
  107. package/dist/ui/confirm-button.d.ts +29 -0
  108. package/dist/ui/confirm-button.js +41 -0
  109. package/dist/ui/corbits-mark.d.ts +29 -0
  110. package/dist/ui/corbits-mark.js +33 -0
  111. package/dist/ui/csv-table.d.ts +22 -0
  112. package/dist/ui/csv-table.js +85 -0
  113. package/dist/ui/dialog.d.ts +37 -0
  114. package/dist/ui/dialog.js +105 -0
  115. package/dist/ui/dictation-status-line.d.ts +20 -0
  116. package/dist/ui/dictation-status-line.js +38 -0
  117. package/dist/ui/dither-canvas.d.ts +31 -0
  118. package/dist/ui/dither-canvas.js +169 -0
  119. package/dist/ui/embed-body.d.ts +30 -0
  120. package/dist/ui/embed-body.js +92 -0
  121. package/dist/ui/empty-state.d.ts +26 -0
  122. package/dist/ui/empty-state.js +42 -0
  123. package/dist/ui/file-input.d.ts +25 -0
  124. package/dist/ui/file-input.js +66 -0
  125. package/dist/ui/filter-bar.d.ts +42 -0
  126. package/dist/ui/filter-bar.js +95 -0
  127. package/dist/ui/filter-chip.d.ts +14 -0
  128. package/dist/ui/filter-chip.js +23 -0
  129. package/dist/ui/gate-block.d.ts +16 -0
  130. package/dist/ui/gate-block.js +37 -0
  131. package/dist/ui/horizontal-stepper.d.ts +13 -0
  132. package/dist/ui/horizontal-stepper.js +65 -0
  133. package/dist/ui/input.d.ts +2 -0
  134. package/dist/ui/input.js +14 -0
  135. package/dist/ui/inspector-shell.d.ts +47 -0
  136. package/dist/ui/inspector-shell.js +98 -0
  137. package/dist/ui/intake-form.d.ts +39 -0
  138. package/dist/ui/intake-form.js +119 -0
  139. package/dist/ui/kind-card-grid.d.ts +23 -0
  140. package/dist/ui/kind-card-grid.js +43 -0
  141. package/dist/ui/library-page-header.d.ts +20 -0
  142. package/dist/ui/library-page-header.js +43 -0
  143. package/dist/ui/list-detail.d.ts +28 -0
  144. package/dist/ui/list-detail.js +55 -0
  145. package/dist/ui/live-run-banner.d.ts +13 -0
  146. package/dist/ui/live-run-banner.js +23 -0
  147. package/dist/ui/live-status-line.d.ts +22 -0
  148. package/dist/ui/live-status-line.js +37 -0
  149. package/dist/ui/menu.d.ts +18 -0
  150. package/dist/ui/menu.js +45 -0
  151. package/dist/ui/message-bubble.d.ts +24 -0
  152. package/dist/ui/message-bubble.js +64 -0
  153. package/dist/ui/mic-button.d.ts +35 -0
  154. package/dist/ui/mic-button.js +61 -0
  155. package/dist/ui/mic-permission-dialog.d.ts +20 -0
  156. package/dist/ui/mic-permission-dialog.js +50 -0
  157. package/dist/ui/notifications-bell.d.ts +28 -0
  158. package/dist/ui/notifications-bell.js +69 -0
  159. package/dist/ui/now-cards.d.ts +37 -0
  160. package/dist/ui/now-cards.js +116 -0
  161. package/dist/ui/page-panel.d.ts +37 -0
  162. package/dist/ui/page-panel.js +23 -0
  163. package/dist/ui/page-shell.d.ts +21 -0
  164. package/dist/ui/page-shell.js +26 -0
  165. package/dist/ui/parts-renderer.d.ts +22 -0
  166. package/dist/ui/parts-renderer.js +234 -0
  167. package/dist/ui/profile-card.d.ts +39 -0
  168. package/dist/ui/profile-card.js +124 -0
  169. package/dist/ui/progress-checklist.d.ts +25 -0
  170. package/dist/ui/progress-checklist.js +70 -0
  171. package/dist/ui/provider-mark.d.ts +32 -0
  172. package/dist/ui/provider-mark.js +31 -0
  173. package/dist/ui/quick-reply-chips.d.ts +14 -0
  174. package/dist/ui/quick-reply-chips.js +24 -0
  175. package/dist/ui/quote-card.d.ts +39 -0
  176. package/dist/ui/quote-card.js +66 -0
  177. package/dist/ui/reasoning-block.d.ts +35 -0
  178. package/dist/ui/reasoning-block.js +60 -0
  179. package/dist/ui/research-body.d.ts +70 -0
  180. package/dist/ui/research-body.js +270 -0
  181. package/dist/ui/rich-empty-state.d.ts +35 -0
  182. package/dist/ui/rich-empty-state.js +65 -0
  183. package/dist/ui/run-now-button.d.ts +23 -0
  184. package/dist/ui/run-now-button.js +42 -0
  185. package/dist/ui/section.d.ts +20 -0
  186. package/dist/ui/section.js +48 -0
  187. package/dist/ui/select.d.ts +2 -0
  188. package/dist/ui/select.js +22 -0
  189. package/dist/ui/selection-checkbox.d.ts +42 -0
  190. package/dist/ui/selection-checkbox.js +54 -0
  191. package/dist/ui/settings-panel.d.ts +35 -0
  192. package/dist/ui/settings-panel.js +68 -0
  193. package/dist/ui/shimmer-text.d.ts +19 -0
  194. package/dist/ui/shimmer-text.js +20 -0
  195. package/dist/ui/sidebar-item-row.d.ts +40 -0
  196. package/dist/ui/sidebar-item-row.js +64 -0
  197. package/dist/ui/sidebar-panel.d.ts +25 -0
  198. package/dist/ui/sidebar-panel.js +56 -0
  199. package/dist/ui/sidebar.d.ts +46 -0
  200. package/dist/ui/sidebar.js +111 -0
  201. package/dist/ui/skeleton.d.ts +13 -0
  202. package/dist/ui/skeleton.js +20 -0
  203. package/dist/ui/sortable-table.d.ts +43 -0
  204. package/dist/ui/sortable-table.js +103 -0
  205. package/dist/ui/sparkline.d.ts +27 -0
  206. package/dist/ui/sparkline.js +68 -0
  207. package/dist/ui/stat-grid.d.ts +55 -0
  208. package/dist/ui/stat-grid.js +87 -0
  209. package/dist/ui/status-dot.d.ts +13 -0
  210. package/dist/ui/status-dot.js +51 -0
  211. package/dist/ui/step-list.d.ts +21 -0
  212. package/dist/ui/step-list.js +77 -0
  213. package/dist/ui/switch.d.ts +23 -0
  214. package/dist/ui/switch.js +29 -0
  215. package/dist/ui/table.d.ts +8 -0
  216. package/dist/ui/table.js +48 -0
  217. package/dist/ui/tabs.d.ts +44 -0
  218. package/dist/ui/tabs.js +101 -0
  219. package/dist/ui/textarea.d.ts +8 -0
  220. package/dist/ui/textarea.js +32 -0
  221. package/dist/ui/theme-provider.d.ts +36 -0
  222. package/dist/ui/theme-provider.js +126 -0
  223. package/dist/ui/theme-toggle.d.ts +11 -0
  224. package/dist/ui/theme-toggle.js +32 -0
  225. package/dist/ui/thinking-indicator.d.ts +7 -0
  226. package/dist/ui/thinking-indicator.js +19 -0
  227. package/dist/ui/thinking-label.d.ts +19 -0
  228. package/dist/ui/thinking-label.js +54 -0
  229. package/dist/ui/thinking-mark.d.ts +10 -0
  230. package/dist/ui/thinking-mark.js +153 -0
  231. package/dist/ui/time-series-chart.d.ts +55 -0
  232. package/dist/ui/time-series-chart.js +249 -0
  233. package/dist/ui/toast.d.ts +16 -0
  234. package/dist/ui/toast.js +41 -0
  235. package/dist/ui/token-mosaic.d.ts +22 -0
  236. package/dist/ui/token-mosaic.js +65 -0
  237. package/dist/ui/tool-block.d.ts +54 -0
  238. package/dist/ui/tool-block.js +170 -0
  239. package/dist/ui/tool-narrative.d.ts +22 -0
  240. package/dist/ui/tool-narrative.js +77 -0
  241. package/dist/ui/tool-picker.d.ts +44 -0
  242. package/dist/ui/tool-picker.js +200 -0
  243. package/dist/ui/tooltip.d.ts +21 -0
  244. package/dist/ui/tooltip.js +48 -0
  245. package/dist/ui/top-bar.d.ts +53 -0
  246. package/dist/ui/top-bar.js +98 -0
  247. package/dist/ui/trace-waterfall.d.ts +38 -0
  248. package/dist/ui/trace-waterfall.js +121 -0
  249. package/dist/ui/typing-indicator.d.ts +9 -0
  250. package/dist/ui/typing-indicator.js +25 -0
  251. package/dist/ui/view-toggle.d.ts +17 -0
  252. package/dist/ui/view-toggle.js +46 -0
  253. package/dist/ui/voice-waveform.d.ts +16 -0
  254. package/dist/ui/voice-waveform.js +23 -0
  255. package/package.json +583 -0
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { agentInitials, isTurnWorking, messageText, reasoningText, toolParts } from "../lib/chat-message.js";
3
+ import { cn } from "../lib/utils.js";
4
+ import { MessageBubble } from "./message-bubble.js";
5
+ import { ReasoningBlock } from "./reasoning-block.js";
6
+ import { ToolNarrative } from "./tool-narrative.js";
7
+ /**
8
+ * One agent reply, in the order it happened: what it thought, what it did, what
9
+ * it said.
10
+ *
11
+ * Parts are rendered in the order the model produced them rather than grouped
12
+ * by kind — a turn that thinks, calls a tool, thinks again and then answers
13
+ * reads as a sequence, and sorting it into "all thinking / all tools / answer"
14
+ * describes a turn that never happened.
15
+ *
16
+ * The avatar is `aria-hidden` and the turn is a labelled region instead: a
17
+ * screen reader should hear "Ada said …" once, not two initials followed by the
18
+ * name.
19
+ */ export function AgentTurn({ message, identity, children, now, reasoningDurationLabel, className }) {
20
+ const working = isTurnWorking(message);
21
+ const answer = messageText(message);
22
+ const thinking = reasoningText(message);
23
+ const tools = toolParts(message);
24
+ const hasWork = thinking.length > 0 || tools.length > 0;
25
+ return /*#__PURE__*/ _jsxs("article", {
26
+ "data-slot": "agent-turn",
27
+ "aria-label": `${identity.name} replied`,
28
+ className: cn("flex gap-3", className),
29
+ children: [
30
+ /*#__PURE__*/ _jsx("span", {
31
+ "aria-hidden": true,
32
+ className: "mt-0.5 grid size-7 shrink-0 place-items-center rounded-full bg-secondary text-[10px] font-bold text-secondary-foreground",
33
+ children: agentInitials(identity)
34
+ }),
35
+ /*#__PURE__*/ _jsxs("div", {
36
+ className: "flex min-w-0 flex-1 flex-col gap-1.5",
37
+ children: [
38
+ hasWork ? /*#__PURE__*/ _jsxs("div", {
39
+ className: "flex flex-col",
40
+ children: [
41
+ /*#__PURE__*/ _jsx(ReasoningBlock, {
42
+ text: thinking,
43
+ streaming: working,
44
+ durationLabel: reasoningDurationLabel
45
+ }),
46
+ tools.map((part)=>/*#__PURE__*/ _jsx(ToolNarrative, {
47
+ part: part
48
+ }, part.toolCallId))
49
+ ]
50
+ }) : null,
51
+ answer.length > 0 || children !== undefined ? /*#__PURE__*/ _jsx(MessageBubble, {
52
+ message: message,
53
+ now: now,
54
+ children: children
55
+ }) : null
56
+ ]
57
+ })
58
+ ]
59
+ });
60
+ }
@@ -0,0 +1,25 @@
1
+ export type AnimatedNumberProps = {
2
+ readonly value: number;
3
+ /** Decimal places for the default formatting. Ignored when `format` is given. */
4
+ readonly decimals?: number;
5
+ readonly format?: (value: number) => string;
6
+ /** Duration of the count, in ms. */
7
+ readonly durationMs?: number;
8
+ readonly className?: string;
9
+ };
10
+ /**
11
+ * A number that counts to its new value instead of snapping.
12
+ *
13
+ * Hand-rolled on `requestAnimationFrame` rather than pulling in a motion
14
+ * library: this registry has no animation dependency, and one component's
15
+ * count-up is not a good reason to add ~30kB that a consumer then owns.
16
+ *
17
+ * `tabular-nums`, so the digits do not change width mid-count and shove the
18
+ * layout around — a KPI row that jitters while it settles is worse than one
19
+ * that does not animate at all.
20
+ *
21
+ * Reduced motion is checked here rather than left to the theme's CSS override.
22
+ * The theme can collapse a CSS animation, but this is a JS interpolation the
23
+ * cascade cannot reach, so it snaps to the value instead.
24
+ */
25
+ export declare function AnimatedNumber({ value, decimals, format, durationMs, className, }: AnimatedNumberProps): import("react").JSX.Element;
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
4
+ import { cn } from "../lib/utils.js";
5
+ const easeOut = (t)=>1 - (1 - t) ** 3;
6
+ /**
7
+ * A number that counts to its new value instead of snapping.
8
+ *
9
+ * Hand-rolled on `requestAnimationFrame` rather than pulling in a motion
10
+ * library: this registry has no animation dependency, and one component's
11
+ * count-up is not a good reason to add ~30kB that a consumer then owns.
12
+ *
13
+ * `tabular-nums`, so the digits do not change width mid-count and shove the
14
+ * layout around — a KPI row that jitters while it settles is worse than one
15
+ * that does not animate at all.
16
+ *
17
+ * Reduced motion is checked here rather than left to the theme's CSS override.
18
+ * The theme can collapse a CSS animation, but this is a JS interpolation the
19
+ * cascade cannot reach, so it snaps to the value instead.
20
+ */ export function AnimatedNumber({ value, decimals = 0, format, durationMs = 600, className }) {
21
+ const [display, setDisplay] = useState(value);
22
+ const fromRef = useRef(value);
23
+ const frameRef = useRef(0);
24
+ const reduce = usePrefersReducedMotion();
25
+ useEffect(()=>{
26
+ const from = fromRef.current;
27
+ if (reduce || from === value) {
28
+ fromRef.current = value;
29
+ setDisplay(value);
30
+ return;
31
+ }
32
+ const start = performance.now();
33
+ const step = (now)=>{
34
+ const t = Math.min((now - start) / durationMs, 1);
35
+ setDisplay(from + (value - from) * easeOut(t));
36
+ if (t < 1) {
37
+ frameRef.current = requestAnimationFrame(step);
38
+ } else {
39
+ fromRef.current = value;
40
+ }
41
+ };
42
+ frameRef.current = requestAnimationFrame(step);
43
+ return ()=>cancelAnimationFrame(frameRef.current);
44
+ }, [
45
+ value,
46
+ durationMs,
47
+ reduce
48
+ ]);
49
+ const text = format === undefined ? display.toFixed(decimals) : format(display);
50
+ return /*#__PURE__*/ _jsxs("span", {
51
+ className: cn("tabular-nums", className),
52
+ children: [
53
+ /*#__PURE__*/ _jsx("span", {
54
+ "aria-hidden": true,
55
+ children: text
56
+ }),
57
+ /*#__PURE__*/ _jsx("span", {
58
+ className: "sr-only",
59
+ children: format === undefined ? value.toFixed(decimals) : format(value)
60
+ })
61
+ ]
62
+ });
63
+ }
@@ -0,0 +1,46 @@
1
+ import type { ReactNode } from "react";
2
+ /** One argument of the action being approved, already resolved for display. */
3
+ export type ApprovalDetail = {
4
+ readonly label: string;
5
+ readonly value: string;
6
+ };
7
+ export type ApprovalRequest = {
8
+ readonly id: string;
9
+ /** What will happen if approved, in plain language: "Post to #sales (Slack)". */
10
+ readonly headline: string;
11
+ /** Who or what is asking. */
12
+ readonly requestedBy: string;
13
+ readonly details?: readonly ApprovalDetail[];
14
+ /**
15
+ * A stable name for the *class* of action, so "always allow" can mean
16
+ * something. Without it the always-allow control is not offered at all.
17
+ */
18
+ readonly actionKey?: string;
19
+ };
20
+ export type ApprovalState = "idle" | "approving" | "rejecting" | "allowing-always";
21
+ export type ApprovalCardProps = {
22
+ readonly request: ApprovalRequest;
23
+ readonly onApprove: () => void;
24
+ readonly onReject: () => void;
25
+ /** Durably allow this action class. Omit to hide the control entirely. */
26
+ readonly onAllowAlways?: (actionKey: string) => void;
27
+ readonly state?: ApprovalState;
28
+ readonly error?: string | null;
29
+ readonly className?: string;
30
+ readonly children?: ReactNode;
31
+ };
32
+ /**
33
+ * The human decision surface: something is blocked until a person says yes or
34
+ * no, and this is where they say it.
35
+ *
36
+ * Approve is the primary and reject is an outline button — not because approval
37
+ * is the expected answer, but because a destructive-styled reject would read as
38
+ * the dangerous option when it is the *safe* one. Neither is pre-focused, and
39
+ * there is no default: a gate whose buttons can be dismissed by a stray Enter
40
+ * is not a gate.
41
+ *
42
+ * Every argument the action will run with is shown. A card that hides them
43
+ * behind "details" is asking for approval of something the user cannot see, so
44
+ * long values clamp rather than disappear.
45
+ */
46
+ export declare function ApprovalCard({ request, onApprove, onReject, onAllowAlways, state, error, className, children, }: ApprovalCardProps): import("react").JSX.Element;
@@ -0,0 +1,116 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ import { Badge } from "./badge.js";
5
+ import { Button } from "./button.js";
6
+ const LONG_VALUE = 140;
7
+ /**
8
+ * The human decision surface: something is blocked until a person says yes or
9
+ * no, and this is where they say it.
10
+ *
11
+ * Approve is the primary and reject is an outline button — not because approval
12
+ * is the expected answer, but because a destructive-styled reject would read as
13
+ * the dangerous option when it is the *safe* one. Neither is pre-focused, and
14
+ * there is no default: a gate whose buttons can be dismissed by a stray Enter
15
+ * is not a gate.
16
+ *
17
+ * Every argument the action will run with is shown. A card that hides them
18
+ * behind "details" is asking for approval of something the user cannot see, so
19
+ * long values clamp rather than disappear.
20
+ */ export function ApprovalCard({ request, onApprove, onReject, onAllowAlways, state = "idle", error = null, className, children }) {
21
+ const busy = state !== "idle";
22
+ const canAllowAlways = onAllowAlways !== undefined && request.actionKey !== undefined;
23
+ return /*#__PURE__*/ _jsxs("section", {
24
+ "aria-label": `Approval requested: ${request.headline}`,
25
+ className: cn("flex flex-col gap-3 rounded-lg border border-primary-emphasis bg-card p-4", className),
26
+ children: [
27
+ /*#__PURE__*/ _jsxs("div", {
28
+ className: "flex flex-wrap items-center gap-2",
29
+ children: [
30
+ /*#__PURE__*/ _jsx(Badge, {
31
+ tone: "accent",
32
+ children: "Needs your approval"
33
+ }),
34
+ /*#__PURE__*/ _jsxs("span", {
35
+ className: "text-xs text-muted-foreground",
36
+ children: [
37
+ "Requested by ",
38
+ request.requestedBy
39
+ ]
40
+ })
41
+ ]
42
+ }),
43
+ /*#__PURE__*/ _jsx("p", {
44
+ className: "text-sm font-semibold",
45
+ children: request.headline
46
+ }),
47
+ request.details === undefined || request.details.length === 0 ? null : /*#__PURE__*/ _jsx("dl", {
48
+ className: "flex flex-col gap-1.5 rounded-md bg-muted p-3 text-xs",
49
+ children: request.details.map((detail)=>/*#__PURE__*/ _jsx(ApprovalDetailRow, {
50
+ detail: detail
51
+ }, detail.label))
52
+ }),
53
+ children,
54
+ error === null ? null : /*#__PURE__*/ _jsx("p", {
55
+ role: "alert",
56
+ className: "text-sm text-destructive",
57
+ children: error
58
+ }),
59
+ /*#__PURE__*/ _jsxs("div", {
60
+ className: "flex flex-wrap items-center gap-2",
61
+ children: [
62
+ /*#__PURE__*/ _jsx(Button, {
63
+ size: "sm",
64
+ onClick: onApprove,
65
+ disabled: busy,
66
+ children: state === "approving" ? "Approving…" : "Approve"
67
+ }),
68
+ /*#__PURE__*/ _jsx(Button, {
69
+ variant: "outline",
70
+ size: "sm",
71
+ onClick: onReject,
72
+ disabled: busy,
73
+ children: state === "rejecting" ? "Rejecting…" : "Reject"
74
+ }),
75
+ canAllowAlways ? /*#__PURE__*/ _jsx(Button, {
76
+ variant: "ghost",
77
+ size: "sm",
78
+ onClick: ()=>onAllowAlways(request.actionKey),
79
+ disabled: busy,
80
+ className: "ml-auto",
81
+ children: state === "allowing-always" ? "Saving…" : "Always allow this"
82
+ }) : null
83
+ ]
84
+ })
85
+ ]
86
+ });
87
+ }
88
+ function ApprovalDetailRow({ detail }) {
89
+ const [expanded, setExpanded] = useState(false);
90
+ const long = detail.value.length > LONG_VALUE;
91
+ return /*#__PURE__*/ _jsxs("div", {
92
+ className: "grid grid-cols-[minmax(0,8rem)_1fr] gap-2",
93
+ children: [
94
+ /*#__PURE__*/ _jsx("dt", {
95
+ className: "truncate text-muted-foreground",
96
+ children: detail.label
97
+ }),
98
+ /*#__PURE__*/ _jsxs("dd", {
99
+ className: "min-w-0",
100
+ children: [
101
+ /*#__PURE__*/ _jsx("span", {
102
+ className: cn("block break-words whitespace-pre-wrap", long && !expanded && "line-clamp-2"),
103
+ children: detail.value
104
+ }),
105
+ long ? /*#__PURE__*/ _jsx("button", {
106
+ type: "button",
107
+ onClick: ()=>setExpanded((value)=>!value),
108
+ "aria-expanded": expanded,
109
+ className: "mt-0.5 text-primary-emphasis hover:underline",
110
+ children: expanded ? "Show less" : "Show more"
111
+ }) : null
112
+ ]
113
+ })
114
+ ]
115
+ });
116
+ }
@@ -0,0 +1,26 @@
1
+ import { type Artifact, type ArtifactForm } from "../lib/artifact.js";
2
+ export type ArtifactBodyProps = {
3
+ readonly artifact: Artifact;
4
+ /**
5
+ * Overrides the form derived from `artifact.kind`. Use it when the kind
6
+ * string cannot tell the whole story — a `file` that turned out to be an
7
+ * image, say — rather than teaching `artifactForm` about one caller's edge.
8
+ */
9
+ readonly form?: ArtifactForm;
10
+ readonly className?: string;
11
+ };
12
+ /**
13
+ * One artifact, drawn the way its kind implies.
14
+ *
15
+ * A `switch` over seven forms, not a renderer registry. The dispatch is the
16
+ * whole component and it fits on a screen, which is the point: you own this
17
+ * file after `shadcn add`, and a lookup table of injected components would mean
18
+ * reading two files and a type parameter to answer "what happens to a CSV". Add
19
+ * a case; delete the ones you do not have.
20
+ *
21
+ * Every branch that can receive an empty payload checks for one first, because
22
+ * "empty" is a real state here — artifacts are frequently rendered while they
23
+ * are still being written — and each form's own empty rendering (a table with
24
+ * no rows, a frame with no src) reads as a fault rather than as progress.
25
+ */
26
+ export declare function ArtifactBody({ artifact, form, className }: ArtifactBodyProps): import("react").JSX.Element;
@@ -0,0 +1,170 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Download } from "lucide-react";
3
+ import { artifactForm } from "../lib/artifact.js";
4
+ import { toSafeHref } from "../lib/url.js";
5
+ import { cn } from "../lib/utils.js";
6
+ import { ArtifactNotice } from "./artifact-notice.js";
7
+ import { buttonVariants } from "./button.js";
8
+ import { CompareBodyFromJson } from "./compare-body.js";
9
+ import { CsvTable } from "./csv-table.js";
10
+ import { EmbedBody } from "./embed-body.js";
11
+ import { ResearchBody, parseResearchArtifact } from "./research-body.js";
12
+ /**
13
+ * One artifact, drawn the way its kind implies.
14
+ *
15
+ * A `switch` over seven forms, not a renderer registry. The dispatch is the
16
+ * whole component and it fits on a screen, which is the point: you own this
17
+ * file after `shadcn add`, and a lookup table of injected components would mean
18
+ * reading two files and a type parameter to answer "what happens to a CSV". Add
19
+ * a case; delete the ones you do not have.
20
+ *
21
+ * Every branch that can receive an empty payload checks for one first, because
22
+ * "empty" is a real state here — artifacts are frequently rendered while they
23
+ * are still being written — and each form's own empty rendering (a table with
24
+ * no rows, a frame with no src) reads as a fault rather than as progress.
25
+ */ export function ArtifactBody({ artifact, form, className }) {
26
+ const resolved = form ?? artifactForm(artifact.kind);
27
+ const content = artifact.content;
28
+ const blank = content.trim() === "";
29
+ return /*#__PURE__*/ _jsx("div", {
30
+ className: cn("w-full", className),
31
+ children: body()
32
+ });
33
+ function body() {
34
+ switch(resolved){
35
+ case "image":
36
+ {
37
+ // The thumbnail URL, not the content: an image artifact's bytes live
38
+ // behind a route, and `content` for one is either empty or a caption.
39
+ const src = toSafeHref(artifact.thumbnailUrl ?? artifact.downloadUrl);
40
+ if (src === undefined) return /*#__PURE__*/ _jsx(ArtifactNotice, {
41
+ pending: artifact.pending
42
+ });
43
+ return /*#__PURE__*/ _jsxs("figure", {
44
+ className: "flex flex-col gap-3",
45
+ children: [
46
+ /*#__PURE__*/ _jsx("img", {
47
+ src: src,
48
+ alt: artifact.title,
49
+ className: "max-h-[480px] max-w-full rounded-lg border border-border"
50
+ }),
51
+ blank ? null : /*#__PURE__*/ _jsx("figcaption", {
52
+ className: "text-xs text-muted-foreground",
53
+ children: content
54
+ })
55
+ ]
56
+ });
57
+ }
58
+ case "download":
59
+ {
60
+ const downloadUrl = toSafeHref(artifact.downloadUrl);
61
+ if (downloadUrl === undefined) {
62
+ return /*#__PURE__*/ _jsx(ArtifactNotice, {
63
+ message: "This file has no download location, so it cannot be retrieved."
64
+ });
65
+ }
66
+ return /*#__PURE__*/ _jsxs("a", {
67
+ href: downloadUrl,
68
+ download: true,
69
+ className: buttonVariants({
70
+ variant: "outline",
71
+ size: "sm"
72
+ }),
73
+ children: [
74
+ /*#__PURE__*/ _jsx(Download, {
75
+ "aria-hidden": true
76
+ }),
77
+ "Download ",
78
+ artifact.title
79
+ ]
80
+ });
81
+ }
82
+ case "embed":
83
+ return /*#__PURE__*/ _jsx(EmbedBody, {
84
+ url: content.trim(),
85
+ title: artifact.title,
86
+ ...artifact.downloadUrl === undefined ? {} : {
87
+ downloadUrl: artifact.downloadUrl
88
+ }
89
+ });
90
+ case "table":
91
+ {
92
+ if (blank) return /*#__PURE__*/ _jsx(ArtifactNotice, {
93
+ pending: artifact.pending
94
+ });
95
+ const downloadUrl = toSafeHref(artifact.downloadUrl);
96
+ return /*#__PURE__*/ _jsxs("div", {
97
+ className: "flex flex-col gap-3",
98
+ children: [
99
+ downloadUrl === undefined ? null : /*#__PURE__*/ _jsxs("a", {
100
+ href: downloadUrl,
101
+ download: true,
102
+ className: cn(buttonVariants({
103
+ variant: "outline",
104
+ size: "sm"
105
+ }), "self-start"),
106
+ children: [
107
+ /*#__PURE__*/ _jsx(Download, {
108
+ "aria-hidden": true
109
+ }),
110
+ "Download"
111
+ ]
112
+ }),
113
+ /*#__PURE__*/ _jsx(CsvTable, {
114
+ text: content,
115
+ caption: artifact.title
116
+ })
117
+ ]
118
+ });
119
+ }
120
+ case "comparison":
121
+ return /*#__PURE__*/ _jsx(CompareBodyFromJson, {
122
+ content: content,
123
+ ...artifact.pending === undefined ? {} : {
124
+ pending: artifact.pending
125
+ }
126
+ });
127
+ case "research":
128
+ {
129
+ if (blank) return /*#__PURE__*/ _jsx(ArtifactNotice, {
130
+ pending: artifact.pending
131
+ });
132
+ const parsed = parseResearchArtifact(content);
133
+ // Unparseable is not a failure worth an error card — the payload is
134
+ // still the artifact, so fall through to prose and let it be read.
135
+ if (parsed === null) return /*#__PURE__*/ _jsx(Prose, {
136
+ text: content
137
+ });
138
+ return /*#__PURE__*/ _jsx(ResearchBody, {
139
+ brief: parsed.brief,
140
+ ...parsed.body === undefined ? {} : {
141
+ body: parsed.body
142
+ }
143
+ });
144
+ }
145
+ case "prose":
146
+ if (blank) return /*#__PURE__*/ _jsx(ArtifactNotice, {
147
+ pending: artifact.pending
148
+ });
149
+ return /*#__PURE__*/ _jsx(Prose, {
150
+ text: content
151
+ });
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Plain text, wrapped and measured.
157
+ *
158
+ * Not a Markdown renderer: this registry ships no Markdown dependency, and
159
+ * choosing one on a consumer's behalf — with its sanitiser, its plugin surface
160
+ * and its bundle — is not a decision a body component gets to make. Replace
161
+ * this function with your renderer; it is four lines and one import away.
162
+ *
163
+ * `max-w-prose` caps the measure at roughly 65 characters. Full-bleed body text
164
+ * on a wide screen is unreadable regardless of how good the type is.
165
+ */ function Prose({ text }) {
166
+ return /*#__PURE__*/ _jsx("div", {
167
+ className: "max-w-prose text-sm leading-relaxed whitespace-pre-wrap",
168
+ children: text
169
+ });
170
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The one place an artifact viewer says "there is nothing to draw here".
3
+ *
4
+ * It exists as its own file, above `EmptyState` rather than beside it, for one
5
+ * reason: every kind renderer needs it, and `ArtifactBody` imports every kind
6
+ * renderer. Putting it in `artifact-body.tsx` would make each renderer import
7
+ * back into its own dispatcher — a cycle. One shared leaf breaks it.
8
+ *
9
+ * The distinction it encodes is the whole point. An empty payload on a *pending*
10
+ * artifact means the producer has not finished writing; an empty payload on a
11
+ * settled one means there is genuinely nothing. Same markup, opposite message,
12
+ * and getting it backwards makes a working system look broken.
13
+ */
14
+ export declare function ArtifactNotice({
15
+ /** The producer is still writing. Changes the message, not the layout. */
16
+ pending,
17
+ /** Overrides the pending/empty wording — use for a specific failure. */
18
+ message, }: {
19
+ readonly pending?: boolean;
20
+ readonly message?: string;
21
+ }): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { FileWarning, Loader2 } from "lucide-react";
3
+ import { EmptyState } from "./empty-state.js";
4
+ /**
5
+ * The one place an artifact viewer says "there is nothing to draw here".
6
+ *
7
+ * It exists as its own file, above `EmptyState` rather than beside it, for one
8
+ * reason: every kind renderer needs it, and `ArtifactBody` imports every kind
9
+ * renderer. Putting it in `artifact-body.tsx` would make each renderer import
10
+ * back into its own dispatcher — a cycle. One shared leaf breaks it.
11
+ *
12
+ * The distinction it encodes is the whole point. An empty payload on a *pending*
13
+ * artifact means the producer has not finished writing; an empty payload on a
14
+ * settled one means there is genuinely nothing. Same markup, opposite message,
15
+ * and getting it backwards makes a working system look broken.
16
+ */ export function ArtifactNotice({ /** The producer is still writing. Changes the message, not the layout. */ pending = false, /** Overrides the pending/empty wording — use for a specific failure. */ message }) {
17
+ if (message !== undefined) {
18
+ return /*#__PURE__*/ _jsx(EmptyState, {
19
+ icon: /*#__PURE__*/ _jsx(FileWarning, {}),
20
+ title: "Nothing to preview",
21
+ description: message
22
+ });
23
+ }
24
+ if (pending) {
25
+ return /*#__PURE__*/ _jsx(EmptyState, {
26
+ icon: /*#__PURE__*/ _jsx(Loader2, {}),
27
+ title: "Still being produced",
28
+ description: "This artifact has no content yet — check back shortly."
29
+ });
30
+ }
31
+ return /*#__PURE__*/ _jsx(EmptyState, {
32
+ icon: /*#__PURE__*/ _jsx(FileWarning, {}),
33
+ title: "No content",
34
+ description: "This artifact is empty."
35
+ });
36
+ }
@@ -0,0 +1,37 @@
1
+ import type { ReactNode } from "react";
2
+ export type AvatarTone = "neutral" | "agent" | "agent2" | "agent3";
3
+ export type AvatarProps = {
4
+ /** One or two characters shown when no image is provided. */
5
+ readonly initials: string;
6
+ readonly label: string;
7
+ readonly tone?: AvatarTone;
8
+ readonly size?: "sm" | "md" | "lg";
9
+ /** Tenant monogram badge overlaid on the corner. */
10
+ readonly tenantMonogram?: string;
11
+ readonly className?: string;
12
+ };
13
+ /**
14
+ * Initials avatar with optional tenant monogram badge. Images are
15
+ * intentionally unsupported here: identity headers should not flash or
16
+ * depend on third-party avatar hosts.
17
+ */
18
+ export declare function Avatar({ initials, label, tone, size, tenantMonogram, className, }: AvatarProps): import("react").JSX.Element;
19
+ export type AvatarStackItem = {
20
+ readonly id: string;
21
+ readonly initials: string;
22
+ readonly label: string;
23
+ readonly tone?: AvatarTone;
24
+ readonly tenantMonogram?: string;
25
+ };
26
+ export type AvatarStackProps = {
27
+ readonly items: readonly AvatarStackItem[];
28
+ /** Cap visible avatars; remainder becomes a +N chip. */
29
+ readonly max?: number;
30
+ readonly size?: "sm" | "md";
31
+ readonly className?: string;
32
+ };
33
+ /**
34
+ * Overlapping avatar stack for thread participants and channel members.
35
+ */
36
+ export declare function AvatarStack({ items, max, size, className }: AvatarStackProps): import("react").JSX.Element;
37
+ export type AvatarStackSlot = ReactNode;