@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 { useEffect, useState } from "react";
3
4
  import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
@@ -12,43 +13,23 @@ const DEFAULT_INTERVAL_MS = 3_000;
12
13
  * announces "Working" once, so a screen reader is not re-interrupted every
13
14
  * few seconds. Reduced motion is live via `usePrefersReducedMotion` and
14
15
  * stops the rotation rather than just stilling the shimmer.
15
- */ export function ThinkingLabel({ verbs, intervalMs = DEFAULT_INTERVAL_MS, statusLabel = "Working", className }) {
16
+ */
17
+ export function ThinkingLabel({ verbs, intervalMs = DEFAULT_INTERVAL_MS, statusLabel = "Working", className, }) {
16
18
  const [at, setAt] = useState(0);
17
19
  const reduceMotion = usePrefersReducedMotion();
18
- useEffect(()=>{
19
- if (verbs.length < 2 || !reduceMotion) return;
20
+ useEffect(() => {
21
+ if (verbs.length < 2 || !reduceMotion)
22
+ return;
20
23
  setAt(0);
21
- }, [
22
- verbs.length,
23
- reduceMotion
24
- ]);
25
- useEffect(()=>{
26
- if (verbs.length < 2 || reduceMotion) return;
27
- const timer = setInterval(()=>setAt((current)=>(current + 1) % verbs.length), intervalMs);
28
- return ()=>clearInterval(timer);
29
- }, [
30
- verbs.length,
31
- intervalMs,
32
- reduceMotion
33
- ]);
34
- if (verbs.length === 0) return null;
24
+ }, [verbs.length, reduceMotion]);
25
+ useEffect(() => {
26
+ if (verbs.length < 2 || reduceMotion)
27
+ return;
28
+ const timer = setInterval(() => setAt((current) => (current + 1) % verbs.length), intervalMs);
29
+ return () => clearInterval(timer);
30
+ }, [verbs.length, intervalMs, reduceMotion]);
31
+ if (verbs.length === 0)
32
+ return null;
35
33
  const active = at % verbs.length;
36
- return /*#__PURE__*/ _jsxs("span", {
37
- className: cn("text-sm", className),
38
- children: [
39
- /*#__PURE__*/ _jsx("span", {
40
- role: "status",
41
- className: "sr-only",
42
- children: statusLabel
43
- }),
44
- /*#__PURE__*/ _jsx("span", {
45
- "aria-hidden": true,
46
- className: "inline-grid",
47
- children: verbs.map((verb, index)=>/*#__PURE__*/ _jsx(ShimmerText, {
48
- className: cn("col-start-1 row-start-1 transition-opacity duration-200", index === active ? "opacity-100" : "opacity-0"),
49
- children: verb
50
- }, index))
51
- })
52
- ]
53
- });
34
+ return (_jsxs("span", { className: cn("text-sm", className), children: [_jsx("span", { role: "status", className: "sr-only", children: statusLabel }), _jsx("span", { "aria-hidden": true, className: "inline-grid", children: verbs.map((verb, index) => (_jsx(ShimmerText, { className: cn("col-start-1 row-start-1 transition-opacity duration-200", index === active ? "opacity-100" : "opacity-0"), children: verb }, index))) })] }));
54
35
  }
@@ -7,4 +7,4 @@ export interface ThinkingMarkProps {
7
7
  style?: CSSProperties;
8
8
  }
9
9
  /** Decorative animated Corbits mark. Pair with visible text or ThinkingIndicator. */
10
- export declare function ThinkingMark({ variant, paused, className, style }: ThinkingMarkProps): import("react").JSX.Element;
10
+ export declare function ThinkingMark({ variant, paused, className, style, }: ThinkingMarkProps): import("react").JSX.Element;
@@ -1,153 +1,12 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useId } from "react";
3
4
  import { cn } from "../lib/utils.js";
4
5
  import { CORBITS_MARK_PATH } from "./corbits-mark.js";
5
- /** Decorative animated Corbits mark. Pair with visible text or ThinkingIndicator. */ export function ThinkingMark({ variant = "silk", paused = false, className, style }) {
6
+ /** Decorative animated Corbits mark. Pair with visible text or ThinkingIndicator. */
7
+ export function ThinkingMark({ variant = "silk", paused = false, className, style, }) {
6
8
  const id = useId();
7
9
  const gradient = `${id}-gradient`;
8
10
  const mask = `${id}-mask`;
9
- return /*#__PURE__*/ _jsxs("svg", {
10
- viewBox: "0 0 100 70",
11
- width: "30",
12
- height: "21",
13
- "aria-hidden": "true",
14
- focusable: "false",
15
- className: cn("inline-block h-auto w-[30px] flex-none overflow-visible", paused && "[&_*]:[animation-play-state:paused]", className),
16
- style: style,
17
- children: [
18
- /*#__PURE__*/ _jsxs("defs", {
19
- children: [
20
- variant === "echo" ? /*#__PURE__*/ _jsxs("radialGradient", {
21
- id: gradient,
22
- children: [
23
- /*#__PURE__*/ _jsx("stop", {
24
- offset: ".38",
25
- stopColor: "white",
26
- stopOpacity: "0"
27
- }),
28
- /*#__PURE__*/ _jsx("stop", {
29
- offset: ".65",
30
- stopColor: "white",
31
- stopOpacity: ".65"
32
- }),
33
- /*#__PURE__*/ _jsx("stop", {
34
- offset: ".9",
35
- stopColor: "white"
36
- }),
37
- /*#__PURE__*/ _jsx("stop", {
38
- offset: "1",
39
- stopColor: "white",
40
- stopOpacity: "0"
41
- })
42
- ]
43
- }) : /*#__PURE__*/ _jsxs("linearGradient", {
44
- id: gradient,
45
- x1: "0",
46
- y1: "0",
47
- x2: variant === "silk" ? "1" : "0",
48
- y2: variant === "silk" ? "0" : "1",
49
- children: [
50
- /*#__PURE__*/ _jsx("stop", {
51
- stopColor: "white",
52
- stopOpacity: "0"
53
- }),
54
- variant === "silk" ? /*#__PURE__*/ _jsxs(_Fragment, {
55
- children: [
56
- /*#__PURE__*/ _jsx("stop", {
57
- offset: ".45",
58
- stopColor: "white",
59
- stopOpacity: ".65"
60
- }),
61
- /*#__PURE__*/ _jsx("stop", {
62
- offset: ".78",
63
- stopColor: "white"
64
- })
65
- ]
66
- }) : /*#__PURE__*/ _jsxs(_Fragment, {
67
- children: [
68
- /*#__PURE__*/ _jsx("stop", {
69
- offset: ".25",
70
- stopColor: "white"
71
- }),
72
- /*#__PURE__*/ _jsx("stop", {
73
- offset: ".42",
74
- stopColor: "white",
75
- stopOpacity: ".7"
76
- })
77
- ]
78
- }),
79
- /*#__PURE__*/ _jsx("stop", {
80
- offset: "1",
81
- stopColor: "white",
82
- stopOpacity: "0"
83
- })
84
- ]
85
- }),
86
- /*#__PURE__*/ _jsxs("mask", {
87
- id: mask,
88
- maskUnits: "userSpaceOnUse",
89
- x: "-10",
90
- y: "-10",
91
- width: "120",
92
- height: "90",
93
- style: {
94
- maskType: "luminance"
95
- },
96
- children: [
97
- variant === "silk" && /*#__PURE__*/ _jsx("rect", {
98
- className: "corbits-thinking-silk [animation:corbits-thinking-sweep_1.45s_cubic-bezier(.22,.05,.38,1)_infinite]",
99
- x: "-35",
100
- y: "0",
101
- width: "48",
102
- height: "75",
103
- fill: `url(#${gradient})`
104
- }),
105
- variant === "strata" && /*#__PURE__*/ _jsx("rect", {
106
- className: "corbits-thinking-strata [animation:corbits-thinking-rise_3.2s_cubic-bezier(.3,0,.3,1)_infinite]",
107
- x: "0",
108
- y: "-12",
109
- width: "100",
110
- height: "33",
111
- fill: `url(#${gradient})`
112
- }),
113
- variant === "echo" && /*#__PURE__*/ _jsxs(_Fragment, {
114
- children: [
115
- /*#__PURE__*/ _jsx("circle", {
116
- className: "corbits-thinking-echo origin-[62px_9px] [animation:corbits-thinking-wave_3.6s_cubic-bezier(.15,.3,.3,1)_infinite]",
117
- cx: "62",
118
- cy: "9",
119
- r: "80",
120
- fill: `url(#${gradient})`
121
- }),
122
- /*#__PURE__*/ _jsx("circle", {
123
- className: "corbits-thinking-echo origin-[62px_9px] motion-reduce:hidden [animation:corbits-thinking-wave_3.6s_cubic-bezier(.15,.3,.3,1)_-1.8s_infinite]",
124
- cx: "62",
125
- cy: "9",
126
- r: "80",
127
- fill: `url(#${gradient})`
128
- })
129
- ]
130
- })
131
- ]
132
- })
133
- ]
134
- }),
135
- /*#__PURE__*/ _jsx("g", {
136
- className: "fill-thinking-base",
137
- opacity: ".7",
138
- children: /*#__PURE__*/ _jsx("path", {
139
- d: CORBITS_MARK_PATH,
140
- transform: "translate(-2.3 -15) scale(.213)"
141
- })
142
- }),
143
- /*#__PURE__*/ _jsx("g", {
144
- className: "fill-primary",
145
- mask: `url(#${mask})`,
146
- children: /*#__PURE__*/ _jsx("path", {
147
- d: CORBITS_MARK_PATH,
148
- transform: "translate(-2.3 -15) scale(.213)"
149
- })
150
- })
151
- ]
152
- });
11
+ return (_jsxs("svg", { viewBox: "0 0 100 70", width: "30", height: "21", "aria-hidden": "true", focusable: "false", className: cn("inline-block h-auto w-[30px] flex-none overflow-visible", paused && "[&_*]:[animation-play-state:paused]", className), style: style, children: [_jsxs("defs", { children: [variant === "echo" ? (_jsxs("radialGradient", { id: gradient, children: [_jsx("stop", { offset: ".38", stopColor: "white", stopOpacity: "0" }), _jsx("stop", { offset: ".65", stopColor: "white", stopOpacity: ".65" }), _jsx("stop", { offset: ".9", stopColor: "white" }), _jsx("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })] })) : (_jsxs("linearGradient", { id: gradient, x1: "0", y1: "0", x2: variant === "silk" ? "1" : "0", y2: variant === "silk" ? "0" : "1", children: [_jsx("stop", { stopColor: "white", stopOpacity: "0" }), variant === "silk" ? (_jsxs(_Fragment, { children: [_jsx("stop", { offset: ".45", stopColor: "white", stopOpacity: ".65" }), _jsx("stop", { offset: ".78", stopColor: "white" })] })) : (_jsxs(_Fragment, { children: [_jsx("stop", { offset: ".25", stopColor: "white" }), _jsx("stop", { offset: ".42", stopColor: "white", stopOpacity: ".7" })] })), _jsx("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })] })), _jsxs("mask", { id: mask, maskUnits: "userSpaceOnUse", x: "-10", y: "-10", width: "120", height: "90", style: { maskType: "luminance" }, children: [variant === "silk" && (_jsx("rect", { className: "corbits-thinking-silk [animation:corbits-thinking-sweep_1.45s_cubic-bezier(.22,.05,.38,1)_infinite]", x: "-35", y: "0", width: "48", height: "75", fill: `url(#${gradient})` })), variant === "strata" && (_jsx("rect", { className: "corbits-thinking-strata [animation:corbits-thinking-rise_3.2s_cubic-bezier(.3,0,.3,1)_infinite]", x: "0", y: "-12", width: "100", height: "33", fill: `url(#${gradient})` })), variant === "echo" && (_jsxs(_Fragment, { children: [_jsx("circle", { className: "corbits-thinking-echo origin-[62px_9px] [animation:corbits-thinking-wave_3.6s_cubic-bezier(.15,.3,.3,1)_infinite]", cx: "62", cy: "9", r: "80", fill: `url(#${gradient})` }), _jsx("circle", { className: "corbits-thinking-echo origin-[62px_9px] motion-reduce:hidden [animation:corbits-thinking-wave_3.6s_cubic-bezier(.15,.3,.3,1)_-1.8s_infinite]", cx: "62", cy: "9", r: "80", fill: `url(#${gradient})` })] }))] })] }), _jsx("g", { className: "fill-thinking-base", opacity: ".7", children: _jsx("path", { d: CORBITS_MARK_PATH, transform: "translate(-2.3 -15) scale(.213)" }) }), _jsx("g", { className: "fill-primary", mask: `url(#${mask})`, children: _jsx("path", { d: CORBITS_MARK_PATH, transform: "translate(-2.3 -15) scale(.213)" }) })] }));
153
12
  }
@@ -1,7 +1,8 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useRef, useState } from "react";
3
- import { areaPath, formatCompact, linePath, niceTicks, scaleLinear } from "../lib/chart-geometry.js";
4
- import { CHART_SERIES_SLOTS, seriesColor, seriesDash } from "../lib/chart-palette.js";
4
+ import { areaPath, formatCompact, linePath, niceTicks, scaleLinear, } from "../lib/chart-geometry.js";
5
+ import { CHART_SERIES_SLOTS, seriesColor, seriesDash, } from "../lib/chart-palette.js";
5
6
  import { cn } from "../lib/utils.js";
6
7
  import { ChartFrame } from "./chart-frame.js";
7
8
  const WIDTH = 480;
@@ -41,11 +42,12 @@ const PAD_BOTTOM = 22;
41
42
  * from the theme, the arrow keys move the same index the pointer moves, and the
42
43
  * readout is echoed into a `role="status"` so it is announced rather than merely
43
44
  * drawn. Anything less makes precise values mouse-only.
44
- */ export function TimeSeriesChart({ title, description, labels, series, variant = "line", format = formatCompact, className }) {
45
+ */
46
+ export function TimeSeriesChart({ title, description, labels, series, variant = "line", format = formatCompact, className, }) {
45
47
  const [active, setActive] = useState(null);
46
48
  const svgRef = useRef(null);
47
49
  const drawn = series.slice(0, CHART_SERIES_SLOTS);
48
- const max = Math.max(0, ...drawn.flatMap((entry)=>entry.values));
50
+ const max = Math.max(0, ...drawn.flatMap((entry) => entry.values));
49
51
  const ticks = niceTicks(max);
50
52
  const axisMax = ticks[ticks.length - 1] ?? 1;
51
53
  const plotWidth = WIDTH - PAD_LEFT - PAD_RIGHT;
@@ -53,197 +55,64 @@ const PAD_BOTTOM = 22;
53
55
  // A single point has nowhere to travel; dividing by zero would put every mark
54
56
  // at NaN and blank the chart.
55
57
  const stepX = labels.length > 1 ? plotWidth / (labels.length - 1) : 0;
56
- const xAt = (index)=>PAD_LEFT + index * stepX;
57
- const yAt = (value)=>PAD_TOP + plotHeight - scaleLinear(value, axisMax, plotHeight);
58
+ const xAt = (index) => PAD_LEFT + index * stepX;
59
+ const yAt = (value) => PAD_TOP + plotHeight - scaleLinear(value, axisMax, plotHeight);
58
60
  function indexFromClientX(clientX) {
59
61
  const box = svgRef.current?.getBoundingClientRect();
60
- if (box === undefined || labels.length === 0) return 0;
62
+ if (box === undefined || labels.length === 0)
63
+ return 0;
61
64
  // The SVG scales to its container, so client pixels must be converted back
62
65
  // into viewBox units before they mean anything.
63
- const viewX = (clientX - box.left) / box.width * WIDTH;
66
+ const viewX = ((clientX - box.left) / box.width) * WIDTH;
64
67
  const raw = stepX === 0 ? 0 : Math.round((viewX - PAD_LEFT) / stepX);
65
68
  return Math.min(labels.length - 1, Math.max(0, raw));
66
69
  }
67
70
  function step(delta) {
68
- setActive((current)=>{
71
+ setActive((current) => {
69
72
  const next = (current ?? 0) + delta;
70
73
  return Math.min(labels.length - 1, Math.max(0, next));
71
74
  });
72
75
  }
73
- const readout = active === null ? "" : `${labels[active]}: ${drawn.map((entry)=>`${entry.label} ${format(entry.values[active] ?? 0)}`).join(", ")}`;
74
- return /*#__PURE__*/ _jsxs(ChartFrame, {
75
- title: title,
76
- ...description === undefined ? {} : {
77
- description
78
- },
76
+ const readout = active === null
77
+ ? ""
78
+ : `${labels[active]}: ${drawn.map((entry) => `${entry.label} ${format(entry.values[active] ?? 0)}`).join(", ")}`;
79
+ return (_jsxs(ChartFrame, { title: title, ...(description === undefined ? {} : { description }),
79
80
  // `dash` as well as `color`: the lines carry two channels, so the legend
80
81
  // has to cross-reference both or it only explains half the plot.
81
- legend: drawn.map((entry, index)=>({
82
- label: entry.label,
83
- color: seriesColor(index),
84
- dash: seriesDash(index) ?? "none"
85
- })),
86
- table: {
87
- columns: [
88
- "Period",
89
- ...drawn.map((entry)=>entry.label)
90
- ],
91
- rows: labels.map((label, index)=>[
92
- label,
93
- ...drawn.map((entry)=>format(entry.values[index] ?? 0))
94
- ])
95
- },
96
- className: className,
97
- children: [
98
- /*#__PURE__*/ _jsxs("div", {
99
- className: "relative",
100
- children: [
101
- /*#__PURE__*/ _jsxs("svg", {
102
- ref: svgRef,
103
- viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
104
- className: "h-auto w-full touch-none",
105
- role: "img",
106
- "aria-label": `${title}. Use the arrow keys to read each period.`,
107
- tabIndex: 0,
108
- onPointerMove: (event)=>setActive(indexFromClientX(event.clientX)),
109
- onPointerLeave: ()=>setActive(null),
110
- onFocus: ()=>setActive((current)=>current ?? 0),
111
- onBlur: ()=>setActive(null),
112
- onKeyDown: (event)=>{
82
+ legend: drawn.map((entry, index) => ({
83
+ label: entry.label,
84
+ color: seriesColor(index),
85
+ dash: seriesDash(index) ?? "none",
86
+ })), table: {
87
+ columns: ["Period", ...drawn.map((entry) => entry.label)],
88
+ rows: labels.map((label, index) => [
89
+ label,
90
+ ...drawn.map((entry) => format(entry.values[index] ?? 0)),
91
+ ]),
92
+ }, className: className, children: [_jsxs("div", { className: "relative", children: [_jsxs("svg", { ref: svgRef, viewBox: `0 0 ${WIDTH} ${HEIGHT}`, className: "h-auto w-full touch-none", role: "img", "aria-label": `${title}. Use the arrow keys to read each period.`, tabIndex: 0, onPointerMove: (event) => setActive(indexFromClientX(event.clientX)), onPointerLeave: () => setActive(null), onFocus: () => setActive((current) => current ?? 0), onBlur: () => setActive(null), onKeyDown: (event) => {
113
93
  if (event.key === "ArrowRight") {
114
94
  event.preventDefault();
115
95
  step(1);
116
- } else if (event.key === "ArrowLeft") {
96
+ }
97
+ else if (event.key === "ArrowLeft") {
117
98
  event.preventDefault();
118
99
  step(-1);
119
- } else if (event.key === "Escape") {
100
+ }
101
+ else if (event.key === "Escape") {
120
102
  setActive(null);
121
103
  }
122
- },
123
- children: [
124
- ticks.map((tick)=>/*#__PURE__*/ _jsxs("g", {
125
- children: [
126
- /*#__PURE__*/ _jsx("line", {
127
- x1: PAD_LEFT,
128
- y1: yAt(tick),
129
- x2: WIDTH - PAD_RIGHT,
130
- y2: yAt(tick),
131
- stroke: "var(--border)",
132
- strokeWidth: 1
133
- }),
134
- /*#__PURE__*/ _jsx("text", {
135
- x: PAD_LEFT - 8,
136
- y: yAt(tick),
137
- textAnchor: "end",
138
- dominantBaseline: "middle",
139
- className: "fill-muted-foreground text-[10px] tabular-nums",
140
- children: format(tick)
141
- })
142
- ]
143
- }, tick)),
144
- labels.length === 0 ? null : /*#__PURE__*/ _jsxs(_Fragment, {
145
- children: [
146
- /*#__PURE__*/ _jsx("text", {
147
- x: PAD_LEFT,
148
- y: HEIGHT - 6,
149
- className: "fill-muted-foreground text-[10px]",
150
- children: labels[0]
151
- }),
152
- /*#__PURE__*/ _jsx("text", {
153
- x: WIDTH - PAD_RIGHT,
154
- y: HEIGHT - 6,
155
- textAnchor: "end",
156
- className: "fill-muted-foreground text-[10px]",
157
- children: labels[labels.length - 1]
158
- })
159
- ]
160
- }),
161
- active === null ? null : /*#__PURE__*/ _jsx("line", {
162
- x1: xAt(active),
163
- y1: PAD_TOP,
164
- x2: xAt(active),
165
- y2: PAD_TOP + plotHeight,
166
- stroke: "var(--muted-foreground)",
167
- strokeWidth: 1
168
- }),
169
- variant === "area" ? drawn.map((entry, index)=>/*#__PURE__*/ _jsx("path", {
170
- d: areaPath(entry.values.map((value, pointIndex)=>({
171
- x: xAt(pointIndex),
172
- y: yAt(value)
173
- })), yAt(0)),
174
- fill: seriesColor(index),
175
- opacity: 0.14
176
- }, entry.label)) : null,
177
- drawn.map((entry, index)=>/*#__PURE__*/ _jsx("path", {
178
- d: linePath(entry.values.map((value, pointIndex)=>({
179
- x: xAt(pointIndex),
180
- y: yAt(value)
181
- }))),
182
- fill: "none",
183
- stroke: seriesColor(index),
184
- strokeDasharray: seriesDash(index),
185
- strokeWidth: 2,
186
- strokeLinecap: "butt",
187
- strokeLinejoin: "round"
188
- }, entry.label)),
189
- active === null ? null : drawn.map((entry, index)=>/*#__PURE__*/ _jsx("circle", {
190
- cx: xAt(active),
191
- cy: yAt(entry.values[active] ?? 0),
192
- r: 4,
193
- fill: seriesColor(index),
104
+ }, children: [ticks.map((tick) => (_jsxs("g", { children: [_jsx("line", { x1: PAD_LEFT, y1: yAt(tick), x2: WIDTH - PAD_RIGHT, y2: yAt(tick), stroke: "var(--border)", strokeWidth: 1 }), _jsx("text", { x: PAD_LEFT - 8, y: yAt(tick), textAnchor: "end", dominantBaseline: "middle", className: "fill-muted-foreground text-[10px] tabular-nums", children: format(tick) })] }, tick))), labels.length === 0 ? null : (_jsxs(_Fragment, { children: [_jsx("text", { x: PAD_LEFT, y: HEIGHT - 6, className: "fill-muted-foreground text-[10px]", children: labels[0] }), _jsx("text", { x: WIDTH - PAD_RIGHT, y: HEIGHT - 6, textAnchor: "end", className: "fill-muted-foreground text-[10px]", children: labels[labels.length - 1] })] })), active === null ? null : (_jsx("line", { x1: xAt(active), y1: PAD_TOP, x2: xAt(active), y2: PAD_TOP + plotHeight, stroke: "var(--muted-foreground)", strokeWidth: 1 })), variant === "area"
105
+ ? drawn.map((entry, index) => (_jsx("path", { d: areaPath(entry.values.map((value, pointIndex) => ({
106
+ x: xAt(pointIndex),
107
+ y: yAt(value),
108
+ })), yAt(0)), fill: seriesColor(index), opacity: 0.14 }, entry.label)))
109
+ : null, drawn.map((entry, index) => (_jsx("path", { d: linePath(entry.values.map((value, pointIndex) => ({
110
+ x: xAt(pointIndex),
111
+ y: yAt(value),
112
+ }))), fill: "none", stroke: seriesColor(index), strokeDasharray: seriesDash(index), strokeWidth: 2, strokeLinecap: "butt", strokeLinejoin: "round" }, entry.label))), active === null
113
+ ? null
114
+ : drawn.map((entry, index) => (_jsx("circle", { cx: xAt(active), cy: yAt(entry.values[active] ?? 0), r: 4, fill: seriesColor(index),
194
115
  // A 2px ring in the surface colour, so a marker stays legible
195
116
  // where it crosses a line or another marker.
196
- stroke: "var(--card)",
197
- strokeWidth: 2
198
- }, entry.label))
199
- ]
200
- }),
201
- active === null ? null : /*#__PURE__*/ _jsxs("div", {
202
- className: "pointer-events-none absolute top-0 left-0 rounded-md border border-border bg-popover px-2 py-1.5 text-xs text-popover-foreground shadow-lg",
203
- children: [
204
- /*#__PURE__*/ _jsx("p", {
205
- className: "font-medium",
206
- children: labels[active]
207
- }),
208
- /*#__PURE__*/ _jsx("ul", {
209
- className: "mt-1 flex flex-col gap-0.5",
210
- children: drawn.map((entry, index)=>/*#__PURE__*/ _jsxs("li", {
211
- className: "flex items-center gap-1.5",
212
- children: [
213
- /*#__PURE__*/ _jsx("svg", {
214
- "aria-hidden": true,
215
- viewBox: "0 0 20 4",
216
- className: "h-1 w-5 shrink-0 overflow-visible",
217
- children: /*#__PURE__*/ _jsx("line", {
218
- x1: 0,
219
- y1: 2,
220
- x2: 20,
221
- y2: 2,
222
- stroke: seriesColor(index),
223
- strokeDasharray: seriesDash(index),
224
- strokeWidth: 2.5
225
- })
226
- }),
227
- /*#__PURE__*/ _jsx("span", {
228
- className: "text-muted-foreground",
229
- children: entry.label
230
- }),
231
- /*#__PURE__*/ _jsx("span", {
232
- className: "ml-auto pl-3 font-mono tabular-nums",
233
- children: format(entry.values[active] ?? 0)
234
- })
235
- ]
236
- }, entry.label))
237
- })
238
- ]
239
- })
240
- ]
241
- }),
242
- /*#__PURE__*/ _jsx("p", {
243
- role: "status",
244
- className: cn("sr-only"),
245
- children: readout
246
- })
247
- ]
248
- });
117
+ stroke: "var(--card)", strokeWidth: 2 }, entry.label)))] }), active === null ? null : (_jsxs("div", { className: "pointer-events-none absolute top-0 left-0 rounded-md border border-border bg-popover px-2 py-1.5 text-xs text-popover-foreground shadow-lg", children: [_jsx("p", { className: "font-medium", children: labels[active] }), _jsx("ul", { className: "mt-1 flex flex-col gap-0.5", children: drawn.map((entry, index) => (_jsxs("li", { className: "flex items-center gap-1.5", children: [_jsx("svg", { "aria-hidden": true, viewBox: "0 0 20 4", className: "h-1 w-5 shrink-0 overflow-visible", children: _jsx("line", { x1: 0, y1: 2, x2: 20, y2: 2, stroke: seriesColor(index), strokeDasharray: seriesDash(index), strokeWidth: 2.5 }) }), _jsx("span", { className: "text-muted-foreground", children: entry.label }), _jsx("span", { className: "ml-auto pl-3 font-mono tabular-nums", children: format(entry.values[active] ?? 0) })] }, entry.label))) })] }))] }), _jsx("p", { role: "status", className: cn("sr-only"), children: readout })] }));
249
118
  }
package/dist/ui/toast.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { Toaster as Sonner, toast as sonnerToast } from "sonner";
3
4
  const TOAST_DURATION_MS = 1800;
@@ -9,33 +10,24 @@ let sequence = 0;
9
10
  * as a single status line rather than a growing stack. Each call takes a
10
11
  * fresh id — reusing one id would inherit the unmount sonner schedules while
11
12
  * a dismissed toast is still animating out, swallowing the new message.
12
- */ export function toast(message) {
13
- if (activeId !== null) sonnerToast.dismiss(activeId);
13
+ */
14
+ export function toast(message) {
15
+ if (activeId !== null)
16
+ sonnerToast.dismiss(activeId);
14
17
  sequence += 1;
15
18
  activeId = `corbits-toast-${sequence}`;
16
- sonnerToast(message, {
17
- id: activeId,
18
- duration: TOAST_DURATION_MS
19
- });
19
+ sonnerToast(message, { id: activeId, duration: TOAST_DURATION_MS });
20
20
  }
21
21
  /**
22
22
  * Mount once at the app root. Sonner renders in a portal, so the toast is
23
23
  * styled here (unstyled + utility classes) instead of inheriting from a
24
24
  * surrounding component; its motion overrides live in theme.css under
25
25
  * `.corbits-toast`.
26
- */ export function Toaster(props) {
27
- return /*#__PURE__*/ _jsx(Sonner, {
28
- position: "bottom-center",
29
- offset: {
30
- bottom: "1.25rem"
31
- },
32
- mobileOffset: {
33
- bottom: "1.25rem"
34
- },
35
- toastOptions: {
26
+ */
27
+ export function Toaster(props) {
28
+ return (_jsx(Sonner, { position: "bottom-center", offset: { bottom: "1.25rem" }, mobileOffset: { bottom: "1.25rem" }, toastOptions: {
36
29
  unstyled: true,
37
- className: "corbits-toast border border-border-strong bg-card shadow-surface " + "px-[0.9rem] py-[0.55rem] font-sans text-[0.8rem] font-[550] text-foreground"
38
- },
39
- ...props
40
- });
30
+ className: "corbits-toast border border-border-strong bg-card shadow-surface " +
31
+ "px-[0.9rem] py-[0.55rem] font-sans text-[0.8rem] font-[550] text-foreground",
32
+ }, ...props }));
41
33
  }
@@ -12,53 +12,15 @@ import { cn } from "../lib/utils.js";
12
12
  * than asking a reader to track more hues. The legend is the accessible half:
13
13
  * the strip itself is one `role="img"` and each legend entry carries the
14
14
  * segment's label and compact value.
15
- */ export function TokenMosaic({ parts, label, className }) {
15
+ */
16
+ export function TokenMosaic({ parts, label, className }) {
16
17
  const segments = buildMosaic(parts);
17
- return /*#__PURE__*/ _jsxs("div", {
18
- className: cn("flex flex-col gap-2", className),
19
- children: [
20
- /*#__PURE__*/ _jsx("div", {
21
- className: "flex h-2.5 w-full overflow-hidden rounded-[3px] bg-muted",
22
- role: "img",
23
- "aria-label": label,
24
- children: /*#__PURE__*/ _jsx("svg", {
25
- className: "h-full w-full",
26
- viewBox: "0 0 100 10",
27
- preserveAspectRatio: "none",
28
- children: renderSegmentRects(segments)
29
- })
30
- }),
31
- /*#__PURE__*/ _jsx("div", {
32
- className: "flex flex-wrap gap-x-4 gap-y-1",
33
- children: segments.map((segment, index)=>/*#__PURE__*/ _jsxs("span", {
34
- className: "flex items-center gap-1.5 font-mono text-[11px] tracking-[0.08em] uppercase text-muted-foreground",
35
- children: [
36
- /*#__PURE__*/ _jsx("span", {
37
- "aria-hidden": true,
38
- className: "size-2 rounded-[1px]",
39
- style: {
40
- backgroundColor: seriesColor(index)
41
- }
42
- }),
43
- segment.label,
44
- " ",
45
- formatCompact(segment.value)
46
- ]
47
- }, segment.label))
48
- })
49
- ]
50
- });
18
+ return (_jsxs("div", { className: cn("flex flex-col gap-2", className), children: [_jsx("div", { className: "flex h-2.5 w-full overflow-hidden rounded-[3px] bg-muted", role: "img", "aria-label": label, children: _jsx("svg", { className: "h-full w-full", viewBox: "0 0 100 10", preserveAspectRatio: "none", children: renderSegmentRects(segments) }) }), _jsx("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: segments.map((segment, index) => (_jsxs("span", { className: "flex items-center gap-1.5 font-mono text-[11px] tracking-[0.08em] uppercase text-muted-foreground", children: [_jsx("span", { "aria-hidden": true, className: "size-2 rounded-[1px]", style: { backgroundColor: seriesColor(index) } }), segment.label, " ", formatCompact(segment.value)] }, segment.label))) })] }));
51
19
  }
52
20
  function renderSegmentRects(segments) {
53
21
  let x = 0;
54
- return segments.map((segment, index)=>{
55
- const rect = /*#__PURE__*/ _jsx("rect", {
56
- x: x,
57
- y: 0,
58
- width: segment.pct,
59
- height: 10,
60
- fill: seriesColor(index)
61
- }, segment.label);
22
+ return segments.map((segment, index) => {
23
+ const rect = (_jsx("rect", { x: x, y: 0, width: segment.pct, height: 10, fill: seriesColor(index) }, segment.label));
62
24
  x += segment.pct;
63
25
  return rect;
64
26
  });