@corbits/react-ui 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +174 -499
- package/README.md +228 -85
- package/dist/blocks/login/auth-layout.d.ts +1 -1
- package/dist/blocks/login/auth-layout.js +3 -33
- package/dist/blocks/login/login-form.js +6 -119
- package/dist/hooks/use-command-palette-navigation.js +18 -17
- package/dist/hooks/use-controllable-state.d.ts +2 -2
- package/dist/hooks/use-controllable-state.js +23 -15
- package/dist/hooks/use-delayed-autofocus.js +10 -11
- package/dist/hooks/use-dismissable-popover.d.ts +2 -2
- package/dist/hooks/use-dismissable-popover.js +21 -29
- package/dist/hooks/use-list-selection.js +54 -85
- package/dist/hooks/use-prefers-reduced-motion.js +8 -4
- package/dist/hooks/use-scroll-current-into-view.js +5 -7
- package/dist/index.d.ts +0 -11
- package/dist/index.js +10 -19
- package/dist/lib/artifact.js +16 -35
- package/dist/lib/chart-geometry.js +33 -20
- package/dist/lib/chart-palette.js +17 -31
- package/dist/lib/chat-message.js +32 -13
- package/dist/lib/chat-parts.js +2 -1
- package/dist/lib/csv.js +42 -26
- package/dist/lib/metrics.js +30 -48
- package/dist/lib/now-item.js +26 -25
- package/dist/lib/relative-time.js +14 -29
- package/dist/lib/theme.js +32 -30
- package/dist/lib/url.js +9 -8
- package/dist/lib/workflow-display-flow.js +37 -33
- package/dist/lib/workflow-registry.d.ts +0 -41
- package/dist/lib/workflow-registry.js +13 -23
- package/dist/lib/workflow-run-error.js +16 -20
- package/dist/lib/workflow-run-progress.js +94 -52
- package/dist/lib/workflow-run.js +17 -13
- package/dist/styles.css +257 -19
- package/dist/test/render-hook.js +11 -12
- package/dist/theme.css +92 -14
- package/dist/ui/agent-turn.d.ts +1 -1
- package/dist/ui/agent-turn.js +4 -37
- package/dist/ui/animated-number.js +15 -28
- package/dist/ui/approval-card.js +5 -90
- package/dist/ui/artifact-body.js +47 -125
- package/dist/ui/artifact-notice.d.ts +1 -1
- package/dist/ui/artifact-notice.js +9 -16
- package/dist/ui/avatar.d.ts +21 -4
- package/dist/ui/avatar.js +40 -49
- package/dist/ui/badge.js +4 -12
- package/dist/ui/bar-chart.d.ts +1 -1
- package/dist/ui/bar-chart.js +22 -74
- package/dist/ui/block-card.js +7 -45
- package/dist/ui/boot-screen.d.ts +1 -1
- package/dist/ui/boot-screen.js +4 -33
- package/dist/ui/bulk-action-bar.d.ts +1 -1
- package/dist/ui/bulk-action-bar.js +14 -33
- package/dist/ui/button.js +10 -24
- package/dist/ui/card.js +5 -25
- package/dist/ui/category-bars.js +14 -59
- package/dist/ui/chart-frame.d.ts +2 -2
- package/dist/ui/chart-frame.js +6 -85
- package/dist/ui/chat-composer.js +4 -16
- package/dist/ui/chat-dock-timing.js +7 -3
- package/dist/ui/chat-input.js +20 -100
- package/dist/ui/chat-panel.d.ts +1 -1
- package/dist/ui/chat-panel.js +7 -41
- package/dist/ui/chat-thread.js +13 -31
- package/dist/ui/checkbox.js +10 -52
- package/dist/ui/command-palette.js +85 -246
- package/dist/ui/compare-body.d.ts +1 -1
- package/dist/ui/compare-body.js +28 -98
- package/dist/ui/confirm-button.js +8 -15
- package/dist/ui/corbits-mark.js +7 -16
- package/dist/ui/csv-table.d.ts +1 -1
- package/dist/ui/csv-table.js +8 -50
- package/dist/ui/dialog.d.ts +1 -1
- package/dist/ui/dialog.js +32 -62
- package/dist/ui/dictation-status-line.js +7 -25
- package/dist/ui/dither-canvas.d.ts +1 -1
- package/dist/ui/dither-canvas.js +31 -56
- package/dist/ui/embed-body.d.ts +1 -1
- package/dist/ui/embed-body.js +7 -56
- package/dist/ui/empty-state.d.ts +1 -1
- package/dist/ui/empty-state.js +3 -26
- package/dist/ui/file-input.js +17 -37
- package/dist/ui/filter-bar.d.ts +1 -1
- package/dist/ui/filter-bar.js +11 -71
- package/dist/ui/filter-chip.d.ts +1 -1
- package/dist/ui/filter-chip.js +7 -15
- package/dist/ui/gate-block.d.ts +2 -1
- package/dist/ui/gate-block.js +4 -29
- package/dist/ui/horizontal-stepper.d.ts +1 -1
- package/dist/ui/horizontal-stepper.js +18 -45
- package/dist/ui/input.js +1 -6
- package/dist/ui/inspector-shell.d.ts +5 -5
- package/dist/ui/inspector-shell.js +14 -81
- package/dist/ui/intake-form.d.ts +1 -1
- package/dist/ui/intake-form.js +21 -97
- package/dist/ui/kind-card-grid.js +9 -31
- package/dist/ui/library-page-header.js +4 -32
- package/dist/ui/list-detail.js +5 -35
- package/dist/ui/live-run-banner.d.ts +1 -1
- package/dist/ui/live-run-banner.js +5 -15
- package/dist/ui/live-status-line.js +6 -24
- package/dist/ui/menu.js +7 -24
- package/dist/ui/message-bubble.d.ts +3 -3
- package/dist/ui/message-bubble.js +5 -44
- package/dist/ui/mic-button.d.ts +1 -1
- package/dist/ui/mic-button.js +15 -21
- package/dist/ui/mic-permission-dialog.js +5 -41
- package/dist/ui/notifications-bell.js +9 -43
- package/dist/ui/now-cards.d.ts +2 -2
- package/dist/ui/now-cards.js +16 -94
- package/dist/ui/page-panel.js +11 -11
- package/dist/ui/page-shell.js +4 -11
- package/dist/ui/parts-renderer.js +51 -149
- package/dist/ui/profile-card.js +7 -111
- package/dist/ui/progress-checklist.js +11 -49
- package/dist/ui/provider-mark.d.ts +2 -2
- package/dist/ui/provider-mark.js +9 -16
- package/dist/ui/quick-reply-chips.js +6 -14
- package/dist/ui/quote-card.js +12 -32
- package/dist/ui/reasoning-block.d.ts +1 -1
- package/dist/ui/reasoning-block.js +14 -33
- package/dist/ui/research-body.js +32 -213
- package/dist/ui/rich-empty-state.js +6 -48
- package/dist/ui/run-now-button.js +14 -15
- package/dist/ui/section.d.ts +1 -1
- package/dist/ui/section.js +4 -40
- package/dist/ui/select.js +1 -15
- package/dist/ui/selection-checkbox.d.ts +1 -1
- package/dist/ui/selection-checkbox.js +11 -29
- package/dist/ui/settings-panel.js +7 -50
- package/dist/ui/shimmer-text.js +3 -6
- package/dist/ui/sidebar-item-row.d.ts +1 -1
- package/dist/ui/sidebar-item-row.js +9 -39
- package/dist/ui/sidebar-panel.js +15 -40
- package/dist/ui/sidebar.js +15 -76
- package/dist/ui/skeleton.js +3 -7
- package/dist/ui/sortable-table.js +29 -72
- package/dist/ui/sparkline.js +14 -38
- package/dist/ui/stat-grid.js +17 -52
- package/dist/ui/status-dot.d.ts +1 -1
- package/dist/ui/status-dot.js +8 -20
- package/dist/ui/step-list.d.ts +1 -0
- package/dist/ui/step-list.js +11 -50
- package/dist/ui/switch.js +4 -16
- package/dist/ui/table.js +7 -32
- package/dist/ui/tabs.js +39 -54
- package/dist/ui/textarea.js +10 -19
- package/dist/ui/theme-provider.js +54 -78
- package/dist/ui/theme-toggle.js +13 -16
- package/dist/ui/thinking-indicator.js +3 -15
- package/dist/ui/thinking-label.js +16 -35
- package/dist/ui/thinking-mark.d.ts +1 -1
- package/dist/ui/thinking-mark.js +4 -145
- package/dist/ui/time-series-chart.js +43 -174
- package/dist/ui/toast.js +12 -20
- package/dist/ui/token-mosaic.js +5 -43
- package/dist/ui/tool-block.js +18 -70
- package/dist/ui/tool-narrative.d.ts +1 -1
- package/dist/ui/tool-narrative.js +10 -57
- package/dist/ui/tool-picker.js +57 -145
- package/dist/ui/tooltip.d.ts +1 -1
- package/dist/ui/tooltip.js +4 -33
- package/dist/ui/top-bar.js +14 -72
- package/dist/ui/trace-waterfall.d.ts +1 -1
- package/dist/ui/trace-waterfall.js +21 -93
- package/dist/ui/typing-indicator.d.ts +1 -1
- package/dist/ui/typing-indicator.js +4 -18
- package/dist/ui/view-toggle.js +10 -29
- package/dist/ui/voice-waveform.js +3 -11
- package/package.json +96 -535
package/dist/ui/research-body.js
CHANGED
|
@@ -9,34 +9,38 @@ import { cn } from "../lib/utils.js";
|
|
|
9
9
|
* Everything below the header is already guarded by a presence check at its own
|
|
10
10
|
* render site, so validating it here would only reject briefs this component
|
|
11
11
|
* draws correctly.
|
|
12
|
-
*/
|
|
12
|
+
*/
|
|
13
|
+
export function parseResearchArtifact(json) {
|
|
13
14
|
let raw;
|
|
14
15
|
try {
|
|
15
16
|
raw = JSON.parse(json);
|
|
16
|
-
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
17
19
|
return null;
|
|
18
20
|
}
|
|
19
|
-
if (typeof raw !== "object" || raw === null)
|
|
21
|
+
if (typeof raw !== "object" || raw === null)
|
|
22
|
+
return null;
|
|
20
23
|
const candidate = raw;
|
|
21
|
-
if (typeof candidate.topic !== "string" ||
|
|
24
|
+
if (typeof candidate.topic !== "string" ||
|
|
25
|
+
typeof candidate.sourceCount !== "number" ||
|
|
26
|
+
typeof candidate.itemCount !== "number") {
|
|
22
27
|
return null;
|
|
23
28
|
}
|
|
24
29
|
const body = typeof candidate.body === "string" ? candidate.body : undefined;
|
|
25
|
-
return body === undefined
|
|
26
|
-
brief: candidate
|
|
27
|
-
|
|
28
|
-
brief: candidate,
|
|
29
|
-
body
|
|
30
|
-
};
|
|
30
|
+
return body === undefined
|
|
31
|
+
? { brief: candidate }
|
|
32
|
+
: { brief: candidate, body };
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* A citation list can name the same URL twice — one item that clustered under
|
|
34
36
|
* two sources. Both the numbering the reader sees and any export they take must
|
|
35
37
|
* agree on the count, so dedupe once, here, at the point of render.
|
|
36
|
-
*/
|
|
38
|
+
*/
|
|
39
|
+
function dedupeByUrl(sources) {
|
|
37
40
|
const seen = new Set();
|
|
38
|
-
return sources.filter((source)=>{
|
|
39
|
-
if (seen.has(source.url))
|
|
41
|
+
return sources.filter((source) => {
|
|
42
|
+
if (seen.has(source.url))
|
|
43
|
+
return false;
|
|
40
44
|
seen.add(source.url);
|
|
41
45
|
return true;
|
|
42
46
|
});
|
|
@@ -60,211 +64,26 @@ import { cn } from "../lib/utils.js";
|
|
|
60
64
|
* ships no Markdown dependency, and adding ~40kB of parser that a consumer then
|
|
61
65
|
* owns is not something a component should decide on their behalf. Wrap the
|
|
62
66
|
* `body` slot in your own renderer if you have one.
|
|
63
|
-
*/
|
|
67
|
+
*/
|
|
68
|
+
export function ResearchBody({ brief, body, className, }) {
|
|
64
69
|
const report = body?.trim() ?? "";
|
|
65
70
|
const citations = dedupeByUrl(brief.citations ?? []);
|
|
66
|
-
const data =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
children: [
|
|
73
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
74
|
-
className: "text-sm font-semibold",
|
|
75
|
-
children: "Themes"
|
|
76
|
-
}),
|
|
77
|
-
brief.clusters.map((cluster)=>/*#__PURE__*/ _jsxs("article", {
|
|
78
|
-
className: "flex flex-col gap-2 rounded-lg border border-border p-4",
|
|
79
|
-
children: [
|
|
80
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
81
|
-
className: "flex items-start justify-between gap-3",
|
|
82
|
-
children: [
|
|
83
|
-
/*#__PURE__*/ _jsx("h4", {
|
|
84
|
-
className: "text-sm leading-snug font-semibold",
|
|
85
|
-
children: cluster.title
|
|
86
|
-
}),
|
|
87
|
-
cluster.sources === undefined || cluster.sources.length === 0 ? null : /*#__PURE__*/ _jsx("span", {
|
|
88
|
-
className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 text-xs text-muted-foreground",
|
|
89
|
-
children: cluster.sources.join(", ")
|
|
90
|
-
})
|
|
91
|
-
]
|
|
92
|
-
}),
|
|
93
|
-
cluster.summary === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
94
|
-
className: "text-xs leading-relaxed text-muted-foreground",
|
|
95
|
-
children: cluster.summary
|
|
96
|
-
}),
|
|
97
|
-
cluster.items === undefined || cluster.items.length === 0 ? null : /*#__PURE__*/ _jsx("ul", {
|
|
98
|
-
className: "flex flex-col gap-1.5",
|
|
99
|
-
children: cluster.items.map((item)=>{
|
|
100
|
-
const href = toSafeHref(item.url);
|
|
101
|
-
return /*#__PURE__*/ _jsx("li", {
|
|
102
|
-
className: "min-w-0",
|
|
103
|
-
children: href === undefined ? /*#__PURE__*/ _jsx("span", {
|
|
104
|
-
className: "text-xs leading-snug text-muted-foreground",
|
|
105
|
-
children: item.title ?? item.url
|
|
106
|
-
}) : /*#__PURE__*/ _jsx("a", {
|
|
107
|
-
href: href,
|
|
108
|
-
target: "_blank",
|
|
109
|
-
rel: "noopener noreferrer",
|
|
110
|
-
className: "text-xs leading-snug text-primary-emphasis underline-offset-2 hover:underline",
|
|
111
|
-
children: item.title ?? item.url
|
|
112
|
-
})
|
|
113
|
-
}, item.url);
|
|
114
|
-
})
|
|
115
|
-
})
|
|
116
|
-
]
|
|
117
|
-
}, cluster.id))
|
|
118
|
-
]
|
|
119
|
-
}),
|
|
120
|
-
brief.quotes === undefined || brief.quotes.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
|
|
121
|
-
className: "flex flex-col gap-3",
|
|
122
|
-
"aria-label": "Quotes",
|
|
123
|
-
children: [
|
|
124
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
125
|
-
className: "text-sm font-semibold",
|
|
126
|
-
children: "Quotes"
|
|
127
|
-
}),
|
|
128
|
-
brief.quotes.map((quote)=>{
|
|
71
|
+
const data = (_jsxs("div", { className: "flex flex-col gap-6", children: [brief.clusters === undefined || brief.clusters.length === 0 ? null : (_jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Themes", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Themes" }), brief.clusters.map((cluster) => (_jsxs("article", { className: "flex flex-col gap-2 rounded-lg border border-border p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("h4", { className: "text-sm leading-snug font-semibold", children: cluster.title }), cluster.sources === undefined ||
|
|
72
|
+
cluster.sources.length === 0 ? null : (_jsx("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 text-xs text-muted-foreground", children: cluster.sources.join(", ") }))] }), cluster.summary === undefined ? null : (_jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: cluster.summary })), cluster.items === undefined ||
|
|
73
|
+
cluster.items.length === 0 ? null : (_jsx("ul", { className: "flex flex-col gap-1.5", children: cluster.items.map((item) => {
|
|
74
|
+
const href = toSafeHref(item.url);
|
|
75
|
+
return (_jsx("li", { className: "min-w-0", children: href === undefined ? (_jsx("span", { className: "text-xs leading-snug text-muted-foreground", children: item.title ?? item.url })) : (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "text-xs leading-snug text-primary-emphasis underline-offset-2 hover:underline", children: item.title ?? item.url })) }, item.url));
|
|
76
|
+
}) }))] }, cluster.id)))] })), brief.quotes === undefined || brief.quotes.length === 0 ? null : (_jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Quotes", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Quotes" }), brief.quotes.map((quote) => {
|
|
129
77
|
const href = toSafeHref(quote.url);
|
|
130
|
-
return(
|
|
78
|
+
return (
|
|
79
|
+
// <figure>/<figcaption> rather than a div and a span: a quotation
|
|
131
80
|
// with an attribution is exactly what the element pair is for, and
|
|
132
81
|
// it is what makes the attribution reachable from the quote for a
|
|
133
82
|
// screen reader instead of being a loose line of text after it.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
children: [
|
|
137
|
-
/*#__PURE__*/ _jsx("blockquote", {
|
|
138
|
-
className: "text-sm leading-relaxed italic",
|
|
139
|
-
children: quote.quote
|
|
140
|
-
}),
|
|
141
|
-
/*#__PURE__*/ _jsxs("figcaption", {
|
|
142
|
-
className: "mt-1 text-xs text-muted-foreground",
|
|
143
|
-
children: [
|
|
144
|
-
quote.author === undefined ? "" : `${quote.author} · `,
|
|
145
|
-
quote.source,
|
|
146
|
-
href === undefined ? null : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
147
|
-
children: [
|
|
148
|
-
" · ",
|
|
149
|
-
/*#__PURE__*/ _jsx("a", {
|
|
150
|
-
href: href,
|
|
151
|
-
target: "_blank",
|
|
152
|
-
rel: "noopener noreferrer",
|
|
153
|
-
className: "text-primary-emphasis underline-offset-2 hover:underline",
|
|
154
|
-
children: "source"
|
|
155
|
-
})
|
|
156
|
-
]
|
|
157
|
-
})
|
|
158
|
-
]
|
|
159
|
-
})
|
|
160
|
-
]
|
|
161
|
-
}, `${quote.source}-${quote.quote.slice(0, 32)}`));
|
|
162
|
-
})
|
|
163
|
-
]
|
|
164
|
-
}),
|
|
165
|
-
citations.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
|
|
166
|
-
className: "flex flex-col gap-3",
|
|
167
|
-
"aria-label": "Citations",
|
|
168
|
-
children: [
|
|
169
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
170
|
-
className: "text-sm font-semibold",
|
|
171
|
-
children: "Citations"
|
|
172
|
-
}),
|
|
173
|
-
/*#__PURE__*/ _jsx("ol", {
|
|
174
|
-
className: "flex flex-col gap-2",
|
|
175
|
-
children: citations.map((citation, index)=>{
|
|
83
|
+
_jsxs("figure", { className: "border-l-2 border-border pl-4", children: [_jsx("blockquote", { className: "text-sm leading-relaxed italic", children: quote.quote }), _jsxs("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: [quote.author === undefined ? "" : `${quote.author} · `, quote.source, href === undefined ? null : (_jsxs(_Fragment, { children: [" · ", _jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "text-primary-emphasis underline-offset-2 hover:underline", children: "source" })] }))] })] }, `${quote.source}-${quote.quote.slice(0, 32)}`));
|
|
84
|
+
})] })), citations.length === 0 ? null : (_jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Citations", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Citations" }), _jsx("ol", { className: "flex flex-col gap-2", children: citations.map((citation, index) => {
|
|
176
85
|
const href = toSafeHref(citation.url);
|
|
177
|
-
return
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
/*#__PURE__*/ _jsx("span", {
|
|
181
|
-
className: "w-6 shrink-0 text-right font-mono text-muted-foreground tabular-nums",
|
|
182
|
-
children: index + 1
|
|
183
|
-
}),
|
|
184
|
-
/*#__PURE__*/ _jsxs("span", {
|
|
185
|
-
className: "min-w-0 leading-relaxed",
|
|
186
|
-
children: [
|
|
187
|
-
href === undefined ? /*#__PURE__*/ _jsx("span", {
|
|
188
|
-
className: "break-words",
|
|
189
|
-
children: citation.title ?? citation.url
|
|
190
|
-
}) : /*#__PURE__*/ _jsx("a", {
|
|
191
|
-
href: href,
|
|
192
|
-
target: "_blank",
|
|
193
|
-
rel: "noopener noreferrer",
|
|
194
|
-
className: "break-words text-primary-emphasis underline-offset-2 hover:underline",
|
|
195
|
-
children: citation.title ?? citation.url
|
|
196
|
-
}),
|
|
197
|
-
/*#__PURE__*/ _jsxs("span", {
|
|
198
|
-
className: "text-muted-foreground",
|
|
199
|
-
children: [
|
|
200
|
-
" · ",
|
|
201
|
-
citation.source
|
|
202
|
-
]
|
|
203
|
-
})
|
|
204
|
-
]
|
|
205
|
-
})
|
|
206
|
-
]
|
|
207
|
-
}, citation.url);
|
|
208
|
-
})
|
|
209
|
-
})
|
|
210
|
-
]
|
|
211
|
-
})
|
|
212
|
-
]
|
|
213
|
-
});
|
|
214
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
215
|
-
className: cn("flex flex-col gap-6", className),
|
|
216
|
-
children: [
|
|
217
|
-
/*#__PURE__*/ _jsxs("header", {
|
|
218
|
-
className: "flex flex-col gap-1.5",
|
|
219
|
-
children: [
|
|
220
|
-
/*#__PURE__*/ _jsx("h2", {
|
|
221
|
-
className: "text-base leading-snug font-semibold",
|
|
222
|
-
children: brief.topic
|
|
223
|
-
}),
|
|
224
|
-
/*#__PURE__*/ _jsxs("p", {
|
|
225
|
-
className: "font-mono text-xs text-muted-foreground tabular-nums",
|
|
226
|
-
children: [
|
|
227
|
-
brief.sourceCount.toLocaleString(),
|
|
228
|
-
" sources · ",
|
|
229
|
-
brief.itemCount.toLocaleString(),
|
|
230
|
-
" items",
|
|
231
|
-
brief.range === undefined ? "" : ` · ${brief.range}`
|
|
232
|
-
]
|
|
233
|
-
}),
|
|
234
|
-
brief.leadInsight === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
235
|
-
className: "max-w-prose text-sm leading-relaxed",
|
|
236
|
-
children: brief.leadInsight
|
|
237
|
-
})
|
|
238
|
-
]
|
|
239
|
-
}),
|
|
240
|
-
report === "" ? data : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
241
|
-
children: [
|
|
242
|
-
/*#__PURE__*/ _jsx("div", {
|
|
243
|
-
className: "max-w-prose text-sm leading-relaxed whitespace-pre-wrap",
|
|
244
|
-
children: report
|
|
245
|
-
}),
|
|
246
|
-
/*#__PURE__*/ _jsxs("details", {
|
|
247
|
-
className: "group border-t border-border pt-4",
|
|
248
|
-
children: [
|
|
249
|
-
/*#__PURE__*/ _jsxs("summary", {
|
|
250
|
-
className: "cursor-pointer text-sm font-semibold text-muted-foreground hover:text-foreground",
|
|
251
|
-
children: [
|
|
252
|
-
"Sources & data (",
|
|
253
|
-
brief.sourceCount.toLocaleString(),
|
|
254
|
-
" sources ·",
|
|
255
|
-
" ",
|
|
256
|
-
brief.itemCount.toLocaleString(),
|
|
257
|
-
" items)"
|
|
258
|
-
]
|
|
259
|
-
}),
|
|
260
|
-
/*#__PURE__*/ _jsx("div", {
|
|
261
|
-
className: "mt-5",
|
|
262
|
-
children: data
|
|
263
|
-
})
|
|
264
|
-
]
|
|
265
|
-
})
|
|
266
|
-
]
|
|
267
|
-
})
|
|
268
|
-
]
|
|
269
|
-
});
|
|
86
|
+
return (_jsxs("li", { className: "flex items-baseline gap-3 text-xs", children: [_jsx("span", { className: "w-6 shrink-0 text-right font-mono text-muted-foreground tabular-nums", children: index + 1 }), _jsxs("span", { className: "min-w-0 leading-relaxed", children: [href === undefined ? (_jsx("span", { className: "break-words", children: citation.title ?? citation.url })) : (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "break-words text-primary-emphasis underline-offset-2 hover:underline", children: citation.title ?? citation.url })), _jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", citation.source] })] })] }, citation.url));
|
|
87
|
+
}) })] }))] }));
|
|
88
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-6", className), children: [_jsxs("header", { className: "flex flex-col gap-1.5", children: [_jsx("h2", { className: "text-base leading-snug font-semibold", children: brief.topic }), _jsxs("p", { className: "font-mono text-xs text-muted-foreground tabular-nums", children: [brief.sourceCount.toLocaleString(), " sources \u00B7", " ", brief.itemCount.toLocaleString(), " items", brief.range === undefined ? "" : ` · ${brief.range}`] }), brief.leadInsight === undefined ? null : (_jsx("p", { className: "max-w-prose text-sm leading-relaxed", children: brief.leadInsight }))] }), report === "" ? (data) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "max-w-prose text-sm leading-relaxed whitespace-pre-wrap", children: report }), _jsxs("details", { className: "group border-t border-border pt-4", children: [_jsxs("summary", { className: "cursor-pointer text-sm font-semibold text-muted-foreground hover:text-foreground", children: ["Sources & data (", brief.sourceCount.toLocaleString(), " sources \u00B7", " ", brief.itemCount.toLocaleString(), " items)"] }), _jsx("div", { className: "mt-5", children: data })] })] }))] }));
|
|
270
89
|
}
|
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { Button } from "./button.js";
|
|
4
|
-
function ActionControl({ action, size }) {
|
|
4
|
+
function ActionControl({ action, size, }) {
|
|
5
5
|
const variant = action.variant === "primary" ? "primary" : "secondary";
|
|
6
6
|
if (action.href !== undefined) {
|
|
7
|
-
return
|
|
8
|
-
asChild: true,
|
|
9
|
-
variant: variant,
|
|
10
|
-
size: size,
|
|
11
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
12
|
-
href: action.href,
|
|
13
|
-
children: action.label
|
|
14
|
-
})
|
|
15
|
-
});
|
|
7
|
+
return (_jsx(Button, { asChild: true, variant: variant, size: size, children: _jsx("a", { href: action.href, children: action.label }) }));
|
|
16
8
|
}
|
|
17
|
-
return
|
|
18
|
-
type: "button",
|
|
19
|
-
variant: variant,
|
|
20
|
-
size: size,
|
|
21
|
-
onClick: action.onClick,
|
|
22
|
-
children: action.label
|
|
23
|
-
});
|
|
9
|
+
return (_jsx(Button, { type: "button", variant: variant, size: size, onClick: action.onClick, children: action.label }));
|
|
24
10
|
}
|
|
25
11
|
/**
|
|
26
12
|
* The onboarding-grade empty state: icon, message, and suggested actions on a
|
|
@@ -31,35 +17,7 @@ function ActionControl({ action, size }) {
|
|
|
31
17
|
* yet, where the empty state *is* the page and has to carry the way in. The
|
|
32
18
|
* icon stays decorative and hidden from assistive tech; the title carries the
|
|
33
19
|
* meaning.
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
className: cn("flex flex-col items-center justify-center rounded-[12px] border border-border bg-card px-6 py-10 text-center", className),
|
|
38
|
-
children: [
|
|
39
|
-
icon === undefined ? null : /*#__PURE__*/ _jsx("div", {
|
|
40
|
-
"aria-hidden": true,
|
|
41
|
-
className: "mb-4 grid size-12 place-items-center rounded-[12px] bg-muted text-muted-foreground",
|
|
42
|
-
children: icon
|
|
43
|
-
}),
|
|
44
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
45
|
-
className: "text-[15px] font-bold tracking-[-0.01em]",
|
|
46
|
-
children: title
|
|
47
|
-
}),
|
|
48
|
-
/*#__PURE__*/ _jsx("p", {
|
|
49
|
-
className: "mt-2 max-w-md text-[13px] leading-relaxed text-muted-foreground",
|
|
50
|
-
children: description
|
|
51
|
-
}),
|
|
52
|
-
actions !== undefined && actions.length > 0 ? /*#__PURE__*/ _jsx("div", {
|
|
53
|
-
className: "mt-5 flex flex-wrap items-center justify-center gap-2",
|
|
54
|
-
children: actions.map((action)=>/*#__PURE__*/ _jsx(ActionControl, {
|
|
55
|
-
action: action,
|
|
56
|
-
size: actionSize
|
|
57
|
-
}, action.label))
|
|
58
|
-
}) : null,
|
|
59
|
-
footer === undefined ? null : /*#__PURE__*/ _jsx("div", {
|
|
60
|
-
className: "mt-4",
|
|
61
|
-
children: footer
|
|
62
|
-
})
|
|
63
|
-
]
|
|
64
|
-
});
|
|
20
|
+
*/
|
|
21
|
+
export function RichEmptyState({ icon, title, description, actions, footer, actionSize = "md", className, }) {
|
|
22
|
+
return (_jsxs("div", { "data-slot": "rich-empty-state", className: cn("flex flex-col items-center justify-center rounded-[12px] border border-border bg-card px-6 py-10 text-center", className), children: [icon === undefined ? null : (_jsx("div", { "aria-hidden": true, className: "mb-4 grid size-12 place-items-center rounded-[12px] bg-muted text-muted-foreground", children: icon })), _jsx("h3", { className: "text-[15px] font-bold tracking-[-0.01em]", children: title }), _jsx("p", { className: "mt-2 max-w-md text-[13px] leading-relaxed text-muted-foreground", children: description }), actions !== undefined && actions.length > 0 ? (_jsx("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: actions.map((action) => (_jsx(ActionControl, { action: action, size: actionSize }, action.label))) })) : null, footer === undefined ? null : _jsx("div", { className: "mt-4", children: footer })] }));
|
|
65
23
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { useEffect, useRef, useState } from "react";
|
|
3
4
|
import { Button } from "./button.js";
|
|
@@ -12,31 +13,29 @@ import { Button } from "./button.js";
|
|
|
12
13
|
*
|
|
13
14
|
* A rejected promise returns the button to rest rather than claiming success.
|
|
14
15
|
* The caller surfaces the reason; this only refuses to lie about it.
|
|
15
|
-
*/
|
|
16
|
+
*/
|
|
17
|
+
export function RunNowButton({ onRun, label = "Run now", pendingLabel = "Starting…", doneLabel = "Started", confirmMs = 2000, disabled, ...props }) {
|
|
16
18
|
const [state, setState] = useState("idle");
|
|
17
19
|
const timerRef = useRef(undefined);
|
|
18
20
|
// Clear on unmount: a timer that fires into an unmounted component is a
|
|
19
21
|
// React warning today and a leak in every version.
|
|
20
|
-
useEffect(()=>()=>window.clearTimeout(timerRef.current), []);
|
|
21
|
-
const run = async ()=>{
|
|
22
|
+
useEffect(() => () => window.clearTimeout(timerRef.current), []);
|
|
23
|
+
const run = async () => {
|
|
22
24
|
setState("pending");
|
|
23
25
|
try {
|
|
24
26
|
await onRun();
|
|
25
27
|
setState("done");
|
|
26
|
-
timerRef.current = window.setTimeout(()=>setState("idle"), confirmMs);
|
|
27
|
-
}
|
|
28
|
+
timerRef.current = window.setTimeout(() => setState("idle"), confirmMs);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
28
31
|
setState("idle");
|
|
29
32
|
}
|
|
30
33
|
};
|
|
31
|
-
return
|
|
32
|
-
...props,
|
|
33
|
-
disabled: disabled || state !== "idle",
|
|
34
|
-
onClick: ()=>{
|
|
34
|
+
return (_jsx(Button, { ...props, disabled: disabled || state !== "idle", onClick: () => {
|
|
35
35
|
void run();
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
36
|
+
}, children: _jsx("span", { "aria-live": "polite", children: state === "pending"
|
|
37
|
+
? pendingLabel
|
|
38
|
+
: state === "done"
|
|
39
|
+
? doneLabel
|
|
40
|
+
: label }) }));
|
|
42
41
|
}
|
package/dist/ui/section.d.ts
CHANGED
|
@@ -17,4 +17,4 @@ export type SectionProps = {
|
|
|
17
17
|
readonly className?: string;
|
|
18
18
|
};
|
|
19
19
|
/** A titled block: heading, optional description and controls, then content. */
|
|
20
|
-
export declare function Section({ title, description, action, count, as, children, className }: SectionProps): import("react").JSX.Element;
|
|
20
|
+
export declare function Section({ title, description, action, count, as, children, className, }: SectionProps): import("react").JSX.Element;
|
package/dist/ui/section.js
CHANGED
|
@@ -3,46 +3,10 @@ import { cn } from "../lib/utils.js";
|
|
|
3
3
|
const TITLE_CLASS = {
|
|
4
4
|
h1: "text-xl font-semibold tracking-tight",
|
|
5
5
|
h2: "text-sm font-semibold",
|
|
6
|
-
h3: "text-xs font-semibold"
|
|
6
|
+
h3: "text-xs font-semibold",
|
|
7
7
|
};
|
|
8
|
-
/** A titled block: heading, optional description and controls, then content. */
|
|
8
|
+
/** A titled block: heading, optional description and controls, then content. */
|
|
9
|
+
export function Section({ title, description, action, count, as = "h2", children, className, }) {
|
|
9
10
|
const Heading = as;
|
|
10
|
-
return
|
|
11
|
-
"data-slot": "section",
|
|
12
|
-
className: cn("flex flex-col gap-3", className),
|
|
13
|
-
children: [
|
|
14
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
15
|
-
className: "flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4",
|
|
16
|
-
children: [
|
|
17
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
18
|
-
className: "flex min-w-0 flex-col gap-1",
|
|
19
|
-
children: [
|
|
20
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
21
|
-
className: "flex items-center gap-2",
|
|
22
|
-
children: [
|
|
23
|
-
/*#__PURE__*/ _jsx(Heading, {
|
|
24
|
-
className: TITLE_CLASS[as],
|
|
25
|
-
children: title
|
|
26
|
-
}),
|
|
27
|
-
count === undefined ? null : /*#__PURE__*/ _jsx("span", {
|
|
28
|
-
className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-xs text-muted-foreground",
|
|
29
|
-
children: count
|
|
30
|
-
})
|
|
31
|
-
]
|
|
32
|
-
}),
|
|
33
|
-
description === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
34
|
-
className: "text-sm leading-snug text-muted-foreground",
|
|
35
|
-
children: description
|
|
36
|
-
})
|
|
37
|
-
]
|
|
38
|
-
}),
|
|
39
|
-
action === undefined ? null : /*#__PURE__*/ _jsx("div", {
|
|
40
|
-
className: "flex shrink-0 items-center gap-2",
|
|
41
|
-
children: action
|
|
42
|
-
})
|
|
43
|
-
]
|
|
44
|
-
}),
|
|
45
|
-
children
|
|
46
|
-
]
|
|
47
|
-
});
|
|
11
|
+
return (_jsxs("section", { "data-slot": "section", className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Heading, { className: TITLE_CLASS[as], children: title }), count === undefined ? null : (_jsx("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-xs text-muted-foreground", children: count }))] }), description === undefined ? null : (_jsx("p", { className: "text-sm leading-snug text-muted-foreground", children: description }))] }), action === undefined ? null : (_jsx("div", { className: "flex shrink-0 items-center gap-2", children: action }))] }), children] }));
|
|
48
12
|
}
|
package/dist/ui/select.js
CHANGED
|
@@ -4,19 +4,5 @@ import { cn } from "../lib/utils.js";
|
|
|
4
4
|
// Same border/focus-ring/disabled treatment as `Input`; the chevron is a
|
|
5
5
|
// decorative overlay since a native `<select>` cannot be given its own icon.
|
|
6
6
|
export function Select({ className, children, ...props }) {
|
|
7
|
-
return
|
|
8
|
-
className: "group relative",
|
|
9
|
-
children: [
|
|
10
|
-
/*#__PURE__*/ _jsx("select", {
|
|
11
|
-
"data-slot": "select",
|
|
12
|
-
className: cn("flex h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors", "selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
13
|
-
...props,
|
|
14
|
-
children: children
|
|
15
|
-
}),
|
|
16
|
-
/*#__PURE__*/ _jsx(ChevronDown, {
|
|
17
|
-
"aria-hidden": true,
|
|
18
|
-
className: "pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground group-has-[:disabled]:opacity-50"
|
|
19
|
-
})
|
|
20
|
-
]
|
|
21
|
-
});
|
|
7
|
+
return (_jsxs("div", { className: "group relative", children: [_jsx("select", { "data-slot": "select", className: cn("flex h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors", "selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className), ...props, children: children }), _jsx(ChevronDown, { "aria-hidden": true, className: "pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground group-has-[:disabled]:opacity-50" })] }));
|
|
22
8
|
}
|
|
@@ -39,4 +39,4 @@ export type SelectionCheckboxProps = {
|
|
|
39
39
|
* reveal below, where the control needs to participate in hover/focus state
|
|
40
40
|
* that belongs to an ancestor it does not render.
|
|
41
41
|
*/
|
|
42
|
-
export declare function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className }: SelectionCheckboxProps): import("react").JSX.Element;
|
|
42
|
+
export declare function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className, }: SelectionCheckboxProps): import("react").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Check, Minus } from "lucide-react";
|
|
3
4
|
import { useId } from "react";
|
|
@@ -14,41 +15,22 @@ import { cn } from "../lib/utils.js";
|
|
|
14
15
|
* a native input's own focus ring and box model fight the opacity-driven
|
|
15
16
|
* reveal below, where the control needs to participate in hover/focus state
|
|
16
17
|
* that belongs to an ancestor it does not render.
|
|
17
|
-
*/
|
|
18
|
+
*/
|
|
19
|
+
export function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className, }) {
|
|
18
20
|
const generatedId = useId();
|
|
19
21
|
const checkboxId = id ?? generatedId;
|
|
20
22
|
const isOn = checked !== false;
|
|
21
23
|
const isIndeterminate = checked === "indeterminate";
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
role: "checkbox",
|
|
26
|
-
"aria-checked": isIndeterminate ? "mixed" : checked,
|
|
27
|
-
"aria-label": ariaLabel ?? `Select ${rowLabel}`,
|
|
28
|
-
onClick: (event)=>onToggle({
|
|
29
|
-
shiftKey: event.shiftKey
|
|
30
|
-
}),
|
|
31
|
-
onKeyDown: (event)=>{
|
|
32
|
-
if (event.key !== " " && event.key !== "Enter") return;
|
|
24
|
+
return (_jsxs("button", { type: "button", id: checkboxId, role: "checkbox", "aria-checked": isIndeterminate ? "mixed" : checked, "aria-label": ariaLabel ?? `Select ${rowLabel}`, onClick: (event) => onToggle({ shiftKey: event.shiftKey }), onKeyDown: (event) => {
|
|
25
|
+
if (event.key !== " " && event.key !== "Enter")
|
|
26
|
+
return;
|
|
33
27
|
event.preventDefault();
|
|
34
|
-
onToggle({
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
className: cn("relative flex size-4 shrink-0 items-center justify-center rounded-sm border border-input bg-card shadow-xs transition-colors", "opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 [@media(hover:none)]:opacity-100", isOn && "border-primary bg-primary opacity-100", // The pop plays only on landing fully checked — an indeterminate
|
|
28
|
+
onToggle({ shiftKey: event.shiftKey });
|
|
29
|
+
}, className: cn("relative flex size-4 shrink-0 items-center justify-center rounded-sm border border-input bg-card shadow-xs transition-colors", "opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 [@media(hover:none)]:opacity-100", isOn && "border-primary bg-primary opacity-100",
|
|
30
|
+
// The pop plays only on landing fully checked — an indeterminate
|
|
39
31
|
// header checkbox flips between indeterminate and checked/unchecked
|
|
40
32
|
// as rows come and go, and it should not re-pop on every one of
|
|
41
33
|
// those incidental transitions.
|
|
42
|
-
checked === true &&
|
|
43
|
-
|
|
44
|
-
/*#__PURE__*/ _jsx(Check, {
|
|
45
|
-
"aria-hidden": true,
|
|
46
|
-
className: cn("size-3 text-primary-foreground transition-transform", checked === true ? "scale-100" : "scale-0")
|
|
47
|
-
}),
|
|
48
|
-
/*#__PURE__*/ _jsx(Minus, {
|
|
49
|
-
"aria-hidden": true,
|
|
50
|
-
className: cn("absolute size-3 text-primary-foreground transition-transform", isIndeterminate ? "scale-100" : "scale-0")
|
|
51
|
-
})
|
|
52
|
-
]
|
|
53
|
-
});
|
|
34
|
+
checked === true &&
|
|
35
|
+
"motion-safe:[animation:corbits-selection-pop_220ms_var(--ease-out)_both]", className), children: [_jsx(Check, { "aria-hidden": true, className: cn("size-3 text-primary-foreground transition-transform", checked === true ? "scale-100" : "scale-0") }), _jsx(Minus, { "aria-hidden": true, className: cn("absolute size-3 text-primary-foreground transition-transform", isIndeterminate ? "scale-100" : "scale-0") })] }));
|
|
54
36
|
}
|
|
@@ -15,54 +15,11 @@ import { Button } from "./button.js";
|
|
|
15
15
|
* The save control is disabled when there is nothing to save, and the panel
|
|
16
16
|
* says why in text as well: a greyed-out button with no explanation is the most
|
|
17
17
|
* common dead end in a settings screen.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
className: "flex flex-col gap-1",
|
|
26
|
-
children: [
|
|
27
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
28
|
-
className: "text-sm font-semibold",
|
|
29
|
-
children: title
|
|
30
|
-
}),
|
|
31
|
-
description === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
32
|
-
className: "text-sm leading-snug text-muted-foreground",
|
|
33
|
-
children: description
|
|
34
|
-
})
|
|
35
|
-
]
|
|
36
|
-
}),
|
|
37
|
-
children,
|
|
38
|
-
error === null ? null : /*#__PURE__*/ _jsx("p", {
|
|
39
|
-
role: "alert",
|
|
40
|
-
className: "text-sm text-destructive",
|
|
41
|
-
children: error
|
|
42
|
-
}),
|
|
43
|
-
onSave === undefined ? null : /*#__PURE__*/ _jsxs("div", {
|
|
44
|
-
className: "flex flex-wrap items-center gap-2 border-t border-border pt-4",
|
|
45
|
-
children: [
|
|
46
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
47
|
-
size: "sm",
|
|
48
|
-
onClick: onSave,
|
|
49
|
-
disabled: !dirty || saving,
|
|
50
|
-
children: saving ? "Saving…" : "Save changes"
|
|
51
|
-
}),
|
|
52
|
-
onReset === undefined || !dirty ? null : /*#__PURE__*/ _jsx(Button, {
|
|
53
|
-
variant: "ghost",
|
|
54
|
-
size: "sm",
|
|
55
|
-
onClick: onReset,
|
|
56
|
-
disabled: saving,
|
|
57
|
-
children: "Discard"
|
|
58
|
-
}),
|
|
59
|
-
/*#__PURE__*/ _jsx("p", {
|
|
60
|
-
"aria-live": "polite",
|
|
61
|
-
className: "ml-auto text-xs text-muted-foreground",
|
|
62
|
-
children: dirty ? "Unsaved changes" : savedAt === null ? "" : `Saved ${savedAt}`
|
|
63
|
-
})
|
|
64
|
-
]
|
|
65
|
-
})
|
|
66
|
-
]
|
|
67
|
-
});
|
|
18
|
+
*/
|
|
19
|
+
export function SettingsPanel({ title, description, onSave, dirty = false, saving = false, error = null, savedAt = null, onReset, children, className, }) {
|
|
20
|
+
return (_jsxs("section", { "data-slot": "settings-panel", "aria-label": title, className: cn("flex flex-col gap-4 rounded-lg border border-border bg-card p-5 text-card-foreground", className), children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h3", { className: "text-sm font-semibold", children: title }), description === undefined ? null : (_jsx("p", { className: "text-sm leading-snug text-muted-foreground", children: description }))] }), children, error === null ? null : (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })), onSave === undefined ? null : (_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t border-border pt-4", children: [_jsx(Button, { size: "sm", onClick: onSave, disabled: !dirty || saving, children: saving ? "Saving…" : "Save changes" }), onReset === undefined || !dirty ? null : (_jsx(Button, { variant: "ghost", size: "sm", onClick: onReset, disabled: saving, children: "Discard" })), _jsx("p", { "aria-live": "polite", className: "ml-auto text-xs text-muted-foreground", children: dirty
|
|
21
|
+
? "Unsaved changes"
|
|
22
|
+
: savedAt === null
|
|
23
|
+
? ""
|
|
24
|
+
: `Saved ${savedAt}` })] }))] }));
|
|
68
25
|
}
|
package/dist/ui/shimmer-text.js
CHANGED
|
@@ -11,10 +11,7 @@ import { cn } from "../lib/utils.js";
|
|
|
11
11
|
* that would still leave `text-transparent` + `background-clip: text` with
|
|
12
12
|
* nothing to paint. Under `prefers-reduced-motion` and `forced-colors` this
|
|
13
13
|
* falls back to solid `text-muted-foreground` with no clip.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
className: cn("bg-clip-text text-transparent", "[background-image:linear-gradient(90deg,var(--muted-foreground)_40%,var(--foreground)_50%,var(--muted-foreground)_60%)]", "[background-size:200%_100%]", "[animation:corbits-shimmer-sweep_1.8s_linear_infinite]", "motion-reduce:animate-none motion-reduce:bg-none motion-reduce:bg-clip-border motion-reduce:text-muted-foreground", "forced-colors:animate-none forced-colors:bg-none forced-colors:bg-clip-border forced-colors:text-muted-foreground", className),
|
|
18
|
-
children: children
|
|
19
|
-
});
|
|
14
|
+
*/
|
|
15
|
+
export function ShimmerText({ children, role, className }) {
|
|
16
|
+
return (_jsx("span", { role: role, className: cn("bg-clip-text text-transparent", "[background-image:linear-gradient(90deg,var(--muted-foreground)_40%,var(--foreground)_50%,var(--muted-foreground)_60%)]", "[background-size:200%_100%]", "[animation:corbits-shimmer-sweep_1.8s_linear_infinite]", "motion-reduce:animate-none motion-reduce:bg-none motion-reduce:bg-clip-border motion-reduce:text-muted-foreground", "forced-colors:animate-none forced-colors:bg-none forced-colors:bg-clip-border forced-colors:text-muted-foreground", className), children: children }));
|
|
20
17
|
}
|