@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.
- package/LICENSE +501 -0
- package/README.md +125 -0
- package/dist/blocks/login/auth-layout.d.ts +38 -0
- package/dist/blocks/login/auth-layout.js +52 -0
- package/dist/blocks/login/login-form.d.ts +60 -0
- package/dist/blocks/login/login-form.js +151 -0
- package/dist/hooks/use-command-palette-navigation.d.ts +25 -0
- package/dist/hooks/use-command-palette-navigation.js +52 -0
- package/dist/hooks/use-controllable-state.d.ts +28 -0
- package/dist/hooks/use-controllable-state.js +44 -0
- package/dist/hooks/use-delayed-autofocus.d.ts +9 -0
- package/dist/hooks/use-delayed-autofocus.js +20 -0
- package/dist/hooks/use-dismissable-popover.d.ts +37 -0
- package/dist/hooks/use-dismissable-popover.js +64 -0
- package/dist/hooks/use-list-selection.d.ts +66 -0
- package/dist/hooks/use-list-selection.js +147 -0
- package/dist/hooks/use-prefers-reduced-motion.d.ts +13 -0
- package/dist/hooks/use-prefers-reduced-motion.js +28 -0
- package/dist/hooks/use-scroll-current-into-view.d.ts +19 -0
- package/dist/hooks/use-scroll-current-into-view.js +33 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +131 -0
- package/dist/lib/artifact.d.ts +72 -0
- package/dist/lib/artifact.js +67 -0
- package/dist/lib/chart-geometry.d.ts +62 -0
- package/dist/lib/chart-geometry.js +86 -0
- package/dist/lib/chart-palette.d.ts +76 -0
- package/dist/lib/chart-palette.js +92 -0
- package/dist/lib/chat-message.d.ts +115 -0
- package/dist/lib/chat-message.js +41 -0
- package/dist/lib/chat-parts.d.ts +64 -0
- package/dist/lib/chat-parts.js +16 -0
- package/dist/lib/csv.d.ts +60 -0
- package/dist/lib/csv.js +108 -0
- package/dist/lib/metrics.d.ts +70 -0
- package/dist/lib/metrics.js +101 -0
- package/dist/lib/now-item.d.ts +65 -0
- package/dist/lib/now-item.js +59 -0
- package/dist/lib/relative-time.d.ts +9 -0
- package/dist/lib/relative-time.js +47 -0
- package/dist/lib/theme.d.ts +30 -0
- package/dist/lib/theme.js +77 -0
- package/dist/lib/url.d.ts +26 -0
- package/dist/lib/url.js +37 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lib/workflow-display-flow.d.ts +83 -0
- package/dist/lib/workflow-display-flow.js +104 -0
- package/dist/lib/workflow-registry.d.ts +90 -0
- package/dist/lib/workflow-registry.js +56 -0
- package/dist/lib/workflow-run-error.d.ts +31 -0
- package/dist/lib/workflow-run-error.js +105 -0
- package/dist/lib/workflow-run-progress.d.ts +97 -0
- package/dist/lib/workflow-run-progress.js +135 -0
- package/dist/lib/workflow-run.d.ts +43 -0
- package/dist/lib/workflow-run.js +56 -0
- package/dist/styles.css +3080 -0
- package/dist/test/render-hook.d.ts +14 -0
- package/dist/test/render-hook.js +38 -0
- package/dist/theme.css +628 -0
- package/dist/ui/agent-turn.d.ts +26 -0
- package/dist/ui/agent-turn.js +60 -0
- package/dist/ui/animated-number.d.ts +25 -0
- package/dist/ui/animated-number.js +63 -0
- package/dist/ui/approval-card.d.ts +46 -0
- package/dist/ui/approval-card.js +116 -0
- package/dist/ui/artifact-body.d.ts +26 -0
- package/dist/ui/artifact-body.js +170 -0
- package/dist/ui/artifact-notice.d.ts +21 -0
- package/dist/ui/artifact-notice.js +36 -0
- package/dist/ui/avatar.d.ts +37 -0
- package/dist/ui/avatar.js +73 -0
- package/dist/ui/badge.d.ts +10 -0
- package/dist/ui/badge.js +35 -0
- package/dist/ui/bar-chart.d.ts +38 -0
- package/dist/ui/bar-chart.js +118 -0
- package/dist/ui/block-card.d.ts +29 -0
- package/dist/ui/block-card.js +66 -0
- package/dist/ui/boot-screen.d.ts +23 -0
- package/dist/ui/boot-screen.js +49 -0
- package/dist/ui/bulk-action-bar.d.ts +37 -0
- package/dist/ui/bulk-action-bar.js +64 -0
- package/dist/ui/button.d.ts +12 -0
- package/dist/ui/button.js +73 -0
- package/dist/ui/card.d.ts +6 -0
- package/dist/ui/card.js +37 -0
- package/dist/ui/category-bars.d.ts +20 -0
- package/dist/ui/category-bars.js +69 -0
- package/dist/ui/chart-frame.d.ts +54 -0
- package/dist/ui/chart-frame.js +108 -0
- package/dist/ui/chat-composer.d.ts +24 -0
- package/dist/ui/chat-composer.js +32 -0
- package/dist/ui/chat-dock-timing.d.ts +15 -0
- package/dist/ui/chat-dock-timing.js +11 -0
- package/dist/ui/chat-input.d.ts +38 -0
- package/dist/ui/chat-input.js +138 -0
- package/dist/ui/chat-panel.d.ts +20 -0
- package/dist/ui/chat-panel.js +56 -0
- package/dist/ui/chat-thread.d.ts +28 -0
- package/dist/ui/chat-thread.js +61 -0
- package/dist/ui/checkbox.d.ts +25 -0
- package/dist/ui/checkbox.js +63 -0
- package/dist/ui/command-palette.d.ts +100 -0
- package/dist/ui/command-palette.js +341 -0
- package/dist/ui/compare-body.d.ts +52 -0
- package/dist/ui/compare-body.js +140 -0
- package/dist/ui/confirm-button.d.ts +29 -0
- package/dist/ui/confirm-button.js +41 -0
- package/dist/ui/corbits-mark.d.ts +29 -0
- package/dist/ui/corbits-mark.js +33 -0
- package/dist/ui/csv-table.d.ts +22 -0
- package/dist/ui/csv-table.js +85 -0
- package/dist/ui/dialog.d.ts +37 -0
- package/dist/ui/dialog.js +105 -0
- package/dist/ui/dictation-status-line.d.ts +20 -0
- package/dist/ui/dictation-status-line.js +38 -0
- package/dist/ui/dither-canvas.d.ts +31 -0
- package/dist/ui/dither-canvas.js +169 -0
- package/dist/ui/embed-body.d.ts +30 -0
- package/dist/ui/embed-body.js +92 -0
- package/dist/ui/empty-state.d.ts +26 -0
- package/dist/ui/empty-state.js +42 -0
- package/dist/ui/file-input.d.ts +25 -0
- package/dist/ui/file-input.js +66 -0
- package/dist/ui/filter-bar.d.ts +42 -0
- package/dist/ui/filter-bar.js +95 -0
- package/dist/ui/filter-chip.d.ts +14 -0
- package/dist/ui/filter-chip.js +23 -0
- package/dist/ui/gate-block.d.ts +16 -0
- package/dist/ui/gate-block.js +37 -0
- package/dist/ui/horizontal-stepper.d.ts +13 -0
- package/dist/ui/horizontal-stepper.js +65 -0
- package/dist/ui/input.d.ts +2 -0
- package/dist/ui/input.js +14 -0
- package/dist/ui/inspector-shell.d.ts +47 -0
- package/dist/ui/inspector-shell.js +98 -0
- package/dist/ui/intake-form.d.ts +39 -0
- package/dist/ui/intake-form.js +119 -0
- package/dist/ui/kind-card-grid.d.ts +23 -0
- package/dist/ui/kind-card-grid.js +43 -0
- package/dist/ui/library-page-header.d.ts +20 -0
- package/dist/ui/library-page-header.js +43 -0
- package/dist/ui/list-detail.d.ts +28 -0
- package/dist/ui/list-detail.js +55 -0
- package/dist/ui/live-run-banner.d.ts +13 -0
- package/dist/ui/live-run-banner.js +23 -0
- package/dist/ui/live-status-line.d.ts +22 -0
- package/dist/ui/live-status-line.js +37 -0
- package/dist/ui/menu.d.ts +18 -0
- package/dist/ui/menu.js +45 -0
- package/dist/ui/message-bubble.d.ts +24 -0
- package/dist/ui/message-bubble.js +64 -0
- package/dist/ui/mic-button.d.ts +35 -0
- package/dist/ui/mic-button.js +61 -0
- package/dist/ui/mic-permission-dialog.d.ts +20 -0
- package/dist/ui/mic-permission-dialog.js +50 -0
- package/dist/ui/notifications-bell.d.ts +28 -0
- package/dist/ui/notifications-bell.js +69 -0
- package/dist/ui/now-cards.d.ts +37 -0
- package/dist/ui/now-cards.js +116 -0
- package/dist/ui/page-panel.d.ts +37 -0
- package/dist/ui/page-panel.js +23 -0
- package/dist/ui/page-shell.d.ts +21 -0
- package/dist/ui/page-shell.js +26 -0
- package/dist/ui/parts-renderer.d.ts +22 -0
- package/dist/ui/parts-renderer.js +234 -0
- package/dist/ui/profile-card.d.ts +39 -0
- package/dist/ui/profile-card.js +124 -0
- package/dist/ui/progress-checklist.d.ts +25 -0
- package/dist/ui/progress-checklist.js +70 -0
- package/dist/ui/provider-mark.d.ts +32 -0
- package/dist/ui/provider-mark.js +31 -0
- package/dist/ui/quick-reply-chips.d.ts +14 -0
- package/dist/ui/quick-reply-chips.js +24 -0
- package/dist/ui/quote-card.d.ts +39 -0
- package/dist/ui/quote-card.js +66 -0
- package/dist/ui/reasoning-block.d.ts +35 -0
- package/dist/ui/reasoning-block.js +60 -0
- package/dist/ui/research-body.d.ts +70 -0
- package/dist/ui/research-body.js +270 -0
- package/dist/ui/rich-empty-state.d.ts +35 -0
- package/dist/ui/rich-empty-state.js +65 -0
- package/dist/ui/run-now-button.d.ts +23 -0
- package/dist/ui/run-now-button.js +42 -0
- package/dist/ui/section.d.ts +20 -0
- package/dist/ui/section.js +48 -0
- package/dist/ui/select.d.ts +2 -0
- package/dist/ui/select.js +22 -0
- package/dist/ui/selection-checkbox.d.ts +42 -0
- package/dist/ui/selection-checkbox.js +54 -0
- package/dist/ui/settings-panel.d.ts +35 -0
- package/dist/ui/settings-panel.js +68 -0
- package/dist/ui/shimmer-text.d.ts +19 -0
- package/dist/ui/shimmer-text.js +20 -0
- package/dist/ui/sidebar-item-row.d.ts +40 -0
- package/dist/ui/sidebar-item-row.js +64 -0
- package/dist/ui/sidebar-panel.d.ts +25 -0
- package/dist/ui/sidebar-panel.js +56 -0
- package/dist/ui/sidebar.d.ts +46 -0
- package/dist/ui/sidebar.js +111 -0
- package/dist/ui/skeleton.d.ts +13 -0
- package/dist/ui/skeleton.js +20 -0
- package/dist/ui/sortable-table.d.ts +43 -0
- package/dist/ui/sortable-table.js +103 -0
- package/dist/ui/sparkline.d.ts +27 -0
- package/dist/ui/sparkline.js +68 -0
- package/dist/ui/stat-grid.d.ts +55 -0
- package/dist/ui/stat-grid.js +87 -0
- package/dist/ui/status-dot.d.ts +13 -0
- package/dist/ui/status-dot.js +51 -0
- package/dist/ui/step-list.d.ts +21 -0
- package/dist/ui/step-list.js +77 -0
- package/dist/ui/switch.d.ts +23 -0
- package/dist/ui/switch.js +29 -0
- package/dist/ui/table.d.ts +8 -0
- package/dist/ui/table.js +48 -0
- package/dist/ui/tabs.d.ts +44 -0
- package/dist/ui/tabs.js +101 -0
- package/dist/ui/textarea.d.ts +8 -0
- package/dist/ui/textarea.js +32 -0
- package/dist/ui/theme-provider.d.ts +36 -0
- package/dist/ui/theme-provider.js +126 -0
- package/dist/ui/theme-toggle.d.ts +11 -0
- package/dist/ui/theme-toggle.js +32 -0
- package/dist/ui/thinking-indicator.d.ts +7 -0
- package/dist/ui/thinking-indicator.js +19 -0
- package/dist/ui/thinking-label.d.ts +19 -0
- package/dist/ui/thinking-label.js +54 -0
- package/dist/ui/thinking-mark.d.ts +10 -0
- package/dist/ui/thinking-mark.js +153 -0
- package/dist/ui/time-series-chart.d.ts +55 -0
- package/dist/ui/time-series-chart.js +249 -0
- package/dist/ui/toast.d.ts +16 -0
- package/dist/ui/toast.js +41 -0
- package/dist/ui/token-mosaic.d.ts +22 -0
- package/dist/ui/token-mosaic.js +65 -0
- package/dist/ui/tool-block.d.ts +54 -0
- package/dist/ui/tool-block.js +170 -0
- package/dist/ui/tool-narrative.d.ts +22 -0
- package/dist/ui/tool-narrative.js +77 -0
- package/dist/ui/tool-picker.d.ts +44 -0
- package/dist/ui/tool-picker.js +200 -0
- package/dist/ui/tooltip.d.ts +21 -0
- package/dist/ui/tooltip.js +48 -0
- package/dist/ui/top-bar.d.ts +53 -0
- package/dist/ui/top-bar.js +98 -0
- package/dist/ui/trace-waterfall.d.ts +38 -0
- package/dist/ui/trace-waterfall.js +121 -0
- package/dist/ui/typing-indicator.d.ts +9 -0
- package/dist/ui/typing-indicator.js +25 -0
- package/dist/ui/view-toggle.d.ts +17 -0
- package/dist/ui/view-toggle.js +46 -0
- package/dist/ui/voice-waveform.d.ts +16 -0
- package/dist/ui/voice-waveform.js +23 -0
- package/package.json +583 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
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
|
+
const id = useId();
|
|
7
|
+
const gradient = `${id}-gradient`;
|
|
8
|
+
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
|
+
});
|
|
153
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type TimeSeries = {
|
|
2
|
+
readonly label: string;
|
|
3
|
+
/** One value per entry in `labels`, same order. */
|
|
4
|
+
readonly values: readonly number[];
|
|
5
|
+
};
|
|
6
|
+
export type TimeSeriesChartProps = {
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly description?: string;
|
|
9
|
+
/** The shared x axis — pre-formatted period labels. */
|
|
10
|
+
readonly labels: readonly string[];
|
|
11
|
+
/** At most five. Past that, sum the tail into an "Other" series first. */
|
|
12
|
+
readonly series: readonly TimeSeries[];
|
|
13
|
+
/**
|
|
14
|
+
* `area` fills under each line at low opacity — the treatment for volume
|
|
15
|
+
* ("how much accumulated") where a bare line reads as a rate. The dash and
|
|
16
|
+
* the stroke stay exactly as in `line`, so the two variants read as one chart
|
|
17
|
+
* family. Identity still comes from the stroke, never the wash.
|
|
18
|
+
*/
|
|
19
|
+
readonly variant?: "line" | "area";
|
|
20
|
+
readonly format?: (value: number) => string;
|
|
21
|
+
readonly className?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Several series over one shared x axis.
|
|
25
|
+
*
|
|
26
|
+
* The x axis is a list of labels shared by every series rather than a date per
|
|
27
|
+
* point, so the series are aligned by construction. Per-series timestamps mean
|
|
28
|
+
* every consumer gets to invent their own alignment rule, and the ones that get
|
|
29
|
+
* it wrong draw two lines that silently disagree about what "March" means.
|
|
30
|
+
*
|
|
31
|
+
* **One y axis, always.** Two series of different magnitude go in two charts or
|
|
32
|
+
* get indexed to a common base — never a second axis. A dual-axis chart lets
|
|
33
|
+
* whoever chose the two scales decide where the lines cross, which means the
|
|
34
|
+
* crossing carries no information while looking like it carries the most.
|
|
35
|
+
*
|
|
36
|
+
* Series are told apart by two channels, not one. Colour is the fast one, but
|
|
37
|
+
* the five palette steps are only guaranteed to separate under simulated
|
|
38
|
+
* protanopia and deuteranopia by a margin that assumes a second channel exists,
|
|
39
|
+
* so each slot also gets its own `seriesDash` stroke pattern — carried on the
|
|
40
|
+
* line, in the legend swatch and in the tooltip swatch, so the cross-reference
|
|
41
|
+
* works wherever the reader is looking. The data table underneath is the
|
|
42
|
+
* fallback; the dash is the encoding.
|
|
43
|
+
*
|
|
44
|
+
* The hover layer is not optional polish; without it there is no way to read an
|
|
45
|
+
* exact value off a line. It reads the nearest index from the pointer's x and
|
|
46
|
+
* shows every series at once, so the tooltip answers "what happened in March"
|
|
47
|
+
* rather than "what was this one line in March".
|
|
48
|
+
*
|
|
49
|
+
* The same read is available from the keyboard. The plot is a `tabbable`
|
|
50
|
+
* `role="application"`-free group with arrow-key stepping — the focus ring comes
|
|
51
|
+
* from the theme, the arrow keys move the same index the pointer moves, and the
|
|
52
|
+
* readout is echoed into a `role="status"` so it is announced rather than merely
|
|
53
|
+
* drawn. Anything less makes precise values mouse-only.
|
|
54
|
+
*/
|
|
55
|
+
export declare function TimeSeriesChart({ title, description, labels, series, variant, format, className, }: TimeSeriesChartProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
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";
|
|
5
|
+
import { cn } from "../lib/utils.js";
|
|
6
|
+
import { ChartFrame } from "./chart-frame.js";
|
|
7
|
+
const WIDTH = 480;
|
|
8
|
+
const HEIGHT = 180;
|
|
9
|
+
const PAD_LEFT = 44;
|
|
10
|
+
const PAD_RIGHT = 12;
|
|
11
|
+
const PAD_TOP = 8;
|
|
12
|
+
const PAD_BOTTOM = 22;
|
|
13
|
+
/**
|
|
14
|
+
* Several series over one shared x axis.
|
|
15
|
+
*
|
|
16
|
+
* The x axis is a list of labels shared by every series rather than a date per
|
|
17
|
+
* point, so the series are aligned by construction. Per-series timestamps mean
|
|
18
|
+
* every consumer gets to invent their own alignment rule, and the ones that get
|
|
19
|
+
* it wrong draw two lines that silently disagree about what "March" means.
|
|
20
|
+
*
|
|
21
|
+
* **One y axis, always.** Two series of different magnitude go in two charts or
|
|
22
|
+
* get indexed to a common base — never a second axis. A dual-axis chart lets
|
|
23
|
+
* whoever chose the two scales decide where the lines cross, which means the
|
|
24
|
+
* crossing carries no information while looking like it carries the most.
|
|
25
|
+
*
|
|
26
|
+
* Series are told apart by two channels, not one. Colour is the fast one, but
|
|
27
|
+
* the five palette steps are only guaranteed to separate under simulated
|
|
28
|
+
* protanopia and deuteranopia by a margin that assumes a second channel exists,
|
|
29
|
+
* so each slot also gets its own `seriesDash` stroke pattern — carried on the
|
|
30
|
+
* line, in the legend swatch and in the tooltip swatch, so the cross-reference
|
|
31
|
+
* works wherever the reader is looking. The data table underneath is the
|
|
32
|
+
* fallback; the dash is the encoding.
|
|
33
|
+
*
|
|
34
|
+
* The hover layer is not optional polish; without it there is no way to read an
|
|
35
|
+
* exact value off a line. It reads the nearest index from the pointer's x and
|
|
36
|
+
* shows every series at once, so the tooltip answers "what happened in March"
|
|
37
|
+
* rather than "what was this one line in March".
|
|
38
|
+
*
|
|
39
|
+
* The same read is available from the keyboard. The plot is a `tabbable`
|
|
40
|
+
* `role="application"`-free group with arrow-key stepping — the focus ring comes
|
|
41
|
+
* from the theme, the arrow keys move the same index the pointer moves, and the
|
|
42
|
+
* readout is echoed into a `role="status"` so it is announced rather than merely
|
|
43
|
+
* drawn. Anything less makes precise values mouse-only.
|
|
44
|
+
*/ export function TimeSeriesChart({ title, description, labels, series, variant = "line", format = formatCompact, className }) {
|
|
45
|
+
const [active, setActive] = useState(null);
|
|
46
|
+
const svgRef = useRef(null);
|
|
47
|
+
const drawn = series.slice(0, CHART_SERIES_SLOTS);
|
|
48
|
+
const max = Math.max(0, ...drawn.flatMap((entry)=>entry.values));
|
|
49
|
+
const ticks = niceTicks(max);
|
|
50
|
+
const axisMax = ticks[ticks.length - 1] ?? 1;
|
|
51
|
+
const plotWidth = WIDTH - PAD_LEFT - PAD_RIGHT;
|
|
52
|
+
const plotHeight = HEIGHT - PAD_TOP - PAD_BOTTOM;
|
|
53
|
+
// A single point has nowhere to travel; dividing by zero would put every mark
|
|
54
|
+
// at NaN and blank the chart.
|
|
55
|
+
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
|
+
function indexFromClientX(clientX) {
|
|
59
|
+
const box = svgRef.current?.getBoundingClientRect();
|
|
60
|
+
if (box === undefined || labels.length === 0) return 0;
|
|
61
|
+
// The SVG scales to its container, so client pixels must be converted back
|
|
62
|
+
// into viewBox units before they mean anything.
|
|
63
|
+
const viewX = (clientX - box.left) / box.width * WIDTH;
|
|
64
|
+
const raw = stepX === 0 ? 0 : Math.round((viewX - PAD_LEFT) / stepX);
|
|
65
|
+
return Math.min(labels.length - 1, Math.max(0, raw));
|
|
66
|
+
}
|
|
67
|
+
function step(delta) {
|
|
68
|
+
setActive((current)=>{
|
|
69
|
+
const next = (current ?? 0) + delta;
|
|
70
|
+
return Math.min(labels.length - 1, Math.max(0, next));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
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
|
+
},
|
|
79
|
+
// `dash` as well as `color`: the lines carry two channels, so the legend
|
|
80
|
+
// 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)=>{
|
|
113
|
+
if (event.key === "ArrowRight") {
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
step(1);
|
|
116
|
+
} else if (event.key === "ArrowLeft") {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
step(-1);
|
|
119
|
+
} else if (event.key === "Escape") {
|
|
120
|
+
setActive(null);
|
|
121
|
+
}
|
|
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),
|
|
194
|
+
// A 2px ring in the surface colour, so a marker stays legible
|
|
195
|
+
// 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
|
+
});
|
|
249
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Toaster as Sonner } from "sonner";
|
|
2
|
+
/**
|
|
3
|
+
* Show a short plain-text confirmation. One toast at a time: a new call
|
|
4
|
+
* dismisses the current one and restarts the timer, so rapid mutations read
|
|
5
|
+
* as a single status line rather than a growing stack. Each call takes a
|
|
6
|
+
* fresh id — reusing one id would inherit the unmount sonner schedules while
|
|
7
|
+
* a dismissed toast is still animating out, swallowing the new message.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toast(message: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Mount once at the app root. Sonner renders in a portal, so the toast is
|
|
12
|
+
* styled here (unstyled + utility classes) instead of inheriting from a
|
|
13
|
+
* surrounding component; its motion overrides live in theme.css under
|
|
14
|
+
* `.corbits-toast`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Toaster(props: React.ComponentProps<typeof Sonner>): import("react").JSX.Element;
|
package/dist/ui/toast.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Toaster as Sonner, toast as sonnerToast } from "sonner";
|
|
3
|
+
const TOAST_DURATION_MS = 1800;
|
|
4
|
+
let activeId = null;
|
|
5
|
+
let sequence = 0;
|
|
6
|
+
/**
|
|
7
|
+
* Show a short plain-text confirmation. One toast at a time: a new call
|
|
8
|
+
* dismisses the current one and restarts the timer, so rapid mutations read
|
|
9
|
+
* as a single status line rather than a growing stack. Each call takes a
|
|
10
|
+
* fresh id — reusing one id would inherit the unmount sonner schedules while
|
|
11
|
+
* a dismissed toast is still animating out, swallowing the new message.
|
|
12
|
+
*/ export function toast(message) {
|
|
13
|
+
if (activeId !== null) sonnerToast.dismiss(activeId);
|
|
14
|
+
sequence += 1;
|
|
15
|
+
activeId = `corbits-toast-${sequence}`;
|
|
16
|
+
sonnerToast(message, {
|
|
17
|
+
id: activeId,
|
|
18
|
+
duration: TOAST_DURATION_MS
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Mount once at the app root. Sonner renders in a portal, so the toast is
|
|
23
|
+
* styled here (unstyled + utility classes) instead of inheriting from a
|
|
24
|
+
* surrounding component; its motion overrides live in theme.css under
|
|
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: {
|
|
36
|
+
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
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type TokenMosaicProps = {
|
|
2
|
+
/** Shares of one whole, in a stable order — colour is assigned by position. */
|
|
3
|
+
readonly parts: readonly {
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly value: number;
|
|
6
|
+
}[];
|
|
7
|
+
/** What the whole is — "Token usage by class". */
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly className?: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Shares of a whole as one stacked strip, with a legend that names and
|
|
13
|
+
* quantifies every segment.
|
|
14
|
+
*
|
|
15
|
+
* Segments are painted from the fixed series ramp by position, never cycled:
|
|
16
|
+
* past the last slot a segment goes muted, which honestly says "lumped
|
|
17
|
+
* together" — fold small parts into an "Other" entry before rendering rather
|
|
18
|
+
* than asking a reader to track more hues. The legend is the accessible half:
|
|
19
|
+
* the strip itself is one `role="img"` and each legend entry carries the
|
|
20
|
+
* segment's label and compact value.
|
|
21
|
+
*/
|
|
22
|
+
export declare function TokenMosaic({ parts, label, className }: TokenMosaicProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { buildMosaic, formatCompact } from "../lib/metrics.js";
|
|
3
|
+
import { seriesColor } from "../lib/chart-palette.js";
|
|
4
|
+
import { cn } from "../lib/utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Shares of a whole as one stacked strip, with a legend that names and
|
|
7
|
+
* quantifies every segment.
|
|
8
|
+
*
|
|
9
|
+
* Segments are painted from the fixed series ramp by position, never cycled:
|
|
10
|
+
* past the last slot a segment goes muted, which honestly says "lumped
|
|
11
|
+
* together" — fold small parts into an "Other" entry before rendering rather
|
|
12
|
+
* than asking a reader to track more hues. The legend is the accessible half:
|
|
13
|
+
* the strip itself is one `role="img"` and each legend entry carries the
|
|
14
|
+
* segment's label and compact value.
|
|
15
|
+
*/ export function TokenMosaic({ parts, label, className }) {
|
|
16
|
+
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
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function renderSegmentRects(segments) {
|
|
53
|
+
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);
|
|
62
|
+
x += segment.pct;
|
|
63
|
+
return rect;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tool call's lifecycle, as a discriminated union rather than a status
|
|
3
|
+
* string plus optional fields — each state carries exactly the data that
|
|
4
|
+
* exists at that point and nothing a caller could set inconsistently (an
|
|
5
|
+
* `error` with an `output`, a `pending` with a `deniedReason`).
|
|
6
|
+
*
|
|
7
|
+
* `approval-requested` and `output-denied` are distinct from `error`: a
|
|
8
|
+
* refused approval is an outcome the human chose, not a failure the tool
|
|
9
|
+
* suffered, and the two read very differently in a transcript.
|
|
10
|
+
*/
|
|
11
|
+
export type ToolBlockState = {
|
|
12
|
+
readonly status: "pending";
|
|
13
|
+
} | {
|
|
14
|
+
readonly status: "running";
|
|
15
|
+
} | {
|
|
16
|
+
readonly status: "output-available";
|
|
17
|
+
readonly output: string;
|
|
18
|
+
} | {
|
|
19
|
+
readonly status: "error";
|
|
20
|
+
readonly message: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly status: "approval-requested";
|
|
23
|
+
readonly reason?: string;
|
|
24
|
+
} | {
|
|
25
|
+
readonly status: "output-denied";
|
|
26
|
+
readonly reason?: string;
|
|
27
|
+
};
|
|
28
|
+
export type ToolBlockProps = {
|
|
29
|
+
/** Raw tool identifier, e.g. `slack__post_message`. */
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/** Human phrasing for what the tool did. Falls back to `name` when absent. */
|
|
32
|
+
readonly label?: string;
|
|
33
|
+
readonly state: ToolBlockState;
|
|
34
|
+
/** Call arguments, shown in the expanded detail when present. */
|
|
35
|
+
readonly input?: unknown;
|
|
36
|
+
readonly defaultOpen?: boolean;
|
|
37
|
+
/** Controlled open state. Pair with `onOpenChange` to lift it to a parent. */
|
|
38
|
+
readonly open?: boolean;
|
|
39
|
+
readonly onOpenChange?: (open: boolean) => void;
|
|
40
|
+
readonly className?: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* What a tool call did, as one line the transcript can open.
|
|
44
|
+
*
|
|
45
|
+
* Collapsed by default and collapsed even while running: a transcript where
|
|
46
|
+
* every tool call expands itself pushes the answer off screen, and the
|
|
47
|
+
* answer is what the user is waiting for. A failed call, a denied approval
|
|
48
|
+
* and a pending approval request all open by default — those are the three
|
|
49
|
+
* states where the detail is the point, not an afterthought.
|
|
50
|
+
*
|
|
51
|
+
* Uncontrolled by default, seeded from `defaultOpen` (or the state's own
|
|
52
|
+
* default). Pass `open`/`onOpenChange` to drive it from a parent.
|
|
53
|
+
*/
|
|
54
|
+
export declare function ToolBlock({ name, label, state, input, defaultOpen, open: openProp, onOpenChange, className, }: ToolBlockProps): import("react").JSX.Element;
|