@tulipfarm/sprout 0.0.0-stage → 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 +21 -0
- package/README.md +183 -2
- package/dist/components/accordion.d.ts +28 -0
- package/dist/components/accordion.js +26 -0
- package/dist/components/alert-dialog.d.ts +60 -0
- package/dist/components/alert-dialog.js +69 -0
- package/dist/components/alert.d.ts +30 -0
- package/dist/components/alert.js +35 -0
- package/dist/components/aspect-ratio.d.ts +7 -0
- package/dist/components/aspect-ratio.js +6 -0
- package/dist/components/attachment.d.ts +118 -0
- package/dist/components/attachment.js +285 -0
- package/dist/components/avatar.d.ts +32 -0
- package/dist/components/avatar.js +73 -0
- package/dist/components/badge.d.ts +21 -0
- package/dist/components/badge.js +27 -0
- package/dist/components/brand-icon.d.ts +28 -0
- package/dist/components/brand-icon.js +78 -0
- package/dist/components/breadcrumb.d.ts +29 -0
- package/dist/components/breadcrumb.js +44 -0
- package/dist/components/button-group.d.ts +28 -0
- package/dist/components/button-group.js +36 -0
- package/dist/components/button.d.ts +79 -0
- package/dist/components/button.js +93 -0
- package/dist/components/calendar.d.ts +27 -0
- package/dist/components/calendar.js +107 -0
- package/dist/components/canvas.d.ts +78 -0
- package/dist/components/canvas.js +180 -0
- package/dist/components/card.d.ts +43 -0
- package/dist/components/card.js +42 -0
- package/dist/components/carousel.d.ts +57 -0
- package/dist/components/carousel.js +422 -0
- package/dist/components/chart.d.ts +112 -0
- package/dist/components/chart.js +172 -0
- package/dist/components/chat-confirmation.d.ts +42 -0
- package/dist/components/chat-confirmation.js +62 -0
- package/dist/components/chat-plan.d.ts +21 -0
- package/dist/components/chat-plan.js +35 -0
- package/dist/components/chat-sources.d.ts +64 -0
- package/dist/components/chat-sources.js +66 -0
- package/dist/components/checkbox.d.ts +16 -0
- package/dist/components/checkbox.js +17 -0
- package/dist/components/choice-list.d.ts +53 -0
- package/dist/components/choice-list.js +36 -0
- package/dist/components/code-block.d.ts +35 -0
- package/dist/components/code-block.js +95 -0
- package/dist/components/collapsible.d.ts +24 -0
- package/dist/components/collapsible.js +27 -0
- package/dist/components/combobox.d.ts +95 -0
- package/dist/components/combobox.js +106 -0
- package/dist/components/command.d.ts +94 -0
- package/dist/components/command.js +414 -0
- package/dist/components/context-menu.d.ts +57 -0
- package/dist/components/context-menu.js +60 -0
- package/dist/components/dialog.d.ts +60 -0
- package/dist/components/dialog.js +79 -0
- package/dist/components/direction.d.ts +6 -0
- package/dist/components/direction.js +6 -0
- package/dist/components/dropdown-menu.d.ts +62 -0
- package/dist/components/dropdown-menu.js +59 -0
- package/dist/components/field.d.ts +117 -0
- package/dist/components/field.js +150 -0
- package/dist/components/file-viewer.d.ts +120 -0
- package/dist/components/file-viewer.js +373 -0
- package/dist/components/form.d.ts +87 -0
- package/dist/components/form.js +55 -0
- package/dist/components/glass-banner.d.ts +27 -0
- package/dist/components/glass-banner.js +116 -0
- package/dist/components/gradient-banner.d.ts +37 -0
- package/dist/components/gradient-banner.js +43 -0
- package/dist/components/hover-card.d.ts +27 -0
- package/dist/components/hover-card.js +26 -0
- package/dist/components/input-group.d.ts +68 -0
- package/dist/components/input-group.js +88 -0
- package/dist/components/input-otp.d.ts +38 -0
- package/dist/components/input-otp.js +43 -0
- package/dist/components/input.d.ts +16 -0
- package/dist/components/input.js +18 -0
- package/dist/components/kbd.d.ts +12 -0
- package/dist/components/kbd.js +11 -0
- package/dist/components/label.d.ts +8 -0
- package/dist/components/label.js +10 -0
- package/dist/components/mention-list.d.ts +54 -0
- package/dist/components/mention-list.js +31 -0
- package/dist/components/menubar.d.ts +64 -0
- package/dist/components/menubar.js +68 -0
- package/dist/components/message-actions.d.ts +34 -0
- package/dist/components/message-actions.js +68 -0
- package/dist/components/message-response.d.ts +20 -0
- package/dist/components/message-response.js +46 -0
- package/dist/components/message-scroller.d.ts +71 -0
- package/dist/components/message-scroller.js +217 -0
- package/dist/components/message.d.ts +49 -0
- package/dist/components/message.js +59 -0
- package/dist/components/navigation-menu.d.ts +45 -0
- package/dist/components/navigation-menu.js +54 -0
- package/dist/components/notice-banner.d.ts +14 -0
- package/dist/components/notice-banner.js +11 -0
- package/dist/components/page-content.d.ts +7 -0
- package/dist/components/page-content.js +9 -0
- package/dist/components/page-header.d.ts +37 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/pagination.d.ts +29 -0
- package/dist/components/pagination.js +29 -0
- package/dist/components/plan-outline.d.ts +60 -0
- package/dist/components/plan-outline.js +108 -0
- package/dist/components/popover.d.ts +34 -0
- package/dist/components/popover.js +35 -0
- package/dist/components/progress.d.ts +27 -0
- package/dist/components/progress.js +35 -0
- package/dist/components/prompt-list.d.ts +23 -0
- package/dist/components/prompt-list.js +11 -0
- package/dist/components/questionnaire.d.ts +148 -0
- package/dist/components/questionnaire.js +1001 -0
- package/dist/components/radio-group.d.ts +16 -0
- package/dist/components/radio-group.js +21 -0
- package/dist/components/reasoning-effort.d.ts +24 -0
- package/dist/components/reasoning-effort.js +269 -0
- package/dist/components/resizable.d.ts +67 -0
- package/dist/components/resizable.js +536 -0
- package/dist/components/result-card.d.ts +58 -0
- package/dist/components/result-card.js +88 -0
- package/dist/components/review-card.d.ts +62 -0
- package/dist/components/review-card.js +49 -0
- package/dist/components/rich-text-editor.d.ts +51 -0
- package/dist/components/rich-text-editor.js +248 -0
- package/dist/components/schema-diagram.d.ts +52 -0
- package/dist/components/schema-diagram.js +226 -0
- package/dist/components/scroll-area.d.ts +22 -0
- package/dist/components/scroll-area.js +19 -0
- package/dist/components/search-dialog.d.ts +103 -0
- package/dist/components/search-dialog.js +64 -0
- package/dist/components/search-field.d.ts +17 -0
- package/dist/components/search-field.js +27 -0
- package/dist/components/select.d.ts +57 -0
- package/dist/components/select.js +57 -0
- package/dist/components/selection-slot.d.ts +18 -0
- package/dist/components/selection-slot.js +53 -0
- package/dist/components/separator.d.ts +48 -0
- package/dist/components/separator.js +54 -0
- package/dist/components/settings-list.d.ts +26 -0
- package/dist/components/settings-list.js +17 -0
- package/dist/components/sheet.d.ts +70 -0
- package/dist/components/sheet.js +77 -0
- package/dist/components/sidebar.d.ts +70 -0
- package/dist/components/sidebar.js +187 -0
- package/dist/components/skeleton.d.ts +31 -0
- package/dist/components/skeleton.js +32 -0
- package/dist/components/slider.d.ts +17 -0
- package/dist/components/slider.js +24 -0
- package/dist/components/spinner.d.ts +37 -0
- package/dist/components/spinner.js +43 -0
- package/dist/components/state-panel-samples.d.ts +25 -0
- package/dist/components/state-panel-samples.js +423 -0
- package/dist/components/state-panel.d.ts +35 -0
- package/dist/components/state-panel.js +29 -0
- package/dist/components/state-sample.d.ts +63 -0
- package/dist/components/state-sample.js +42 -0
- package/dist/components/switch.d.ts +12 -0
- package/dist/components/switch.js +14 -0
- package/dist/components/table-nuqs.d.ts +16 -0
- package/dist/components/table-nuqs.js +59 -0
- package/dist/components/table.d.ts +120 -0
- package/dist/components/table.js +663 -0
- package/dist/components/tabs.d.ts +37 -0
- package/dist/components/tabs.js +36 -0
- package/dist/components/textarea.d.ts +18 -0
- package/dist/components/textarea.js +19 -0
- package/dist/components/toast.d.ts +45 -0
- package/dist/components/toast.js +83 -0
- package/dist/components/toggle-group.d.ts +30 -0
- package/dist/components/toggle-group.js +39 -0
- package/dist/components/toggle.d.ts +27 -0
- package/dist/components/toggle.js +21 -0
- package/dist/components/tool-activity.d.ts +79 -0
- package/dist/components/tool-activity.js +226 -0
- package/dist/components/toolbar-status.d.ts +21 -0
- package/dist/components/toolbar-status.js +14 -0
- package/dist/components/tooltip.d.ts +31 -0
- package/dist/components/tooltip.js +99 -0
- package/dist/components/typed-text.d.ts +17 -0
- package/dist/components/typed-text.js +56 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +88 -0
- package/dist/internal/brand-glyphs.d.ts +342 -0
- package/dist/internal/brand-glyphs.js +238 -0
- package/dist/internal/classes.d.ts +3 -0
- package/dist/internal/classes.js +20 -0
- package/dist/internal/fieldset.d.ts +6 -0
- package/dist/internal/fieldset.js +38 -0
- package/dist/internal/file-kind.d.ts +4 -0
- package/dist/internal/file-kind.js +62 -0
- package/dist/internal/languages.d.ts +7 -0
- package/dist/internal/languages.js +1010 -0
- package/dist/internal/message-scroller.d.ts +102 -0
- package/dist/internal/message-scroller.js +637 -0
- package/dist/internal/pressable.d.ts +2 -0
- package/dist/internal/pressable.js +29 -0
- package/dist/internal/syntax.d.ts +14 -0
- package/dist/internal/syntax.js +137 -0
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/alert-dialog.css +198 -0
- package/dist/styles/alert.css +149 -0
- package/dist/styles/aspect-ratio.css +8 -0
- package/dist/styles/attachment.css +720 -0
- package/dist/styles/avatar.css +186 -0
- package/dist/styles/badge.css +178 -0
- package/dist/styles/brand-icon.css +104 -0
- package/dist/styles/breadcrumb.css +121 -0
- package/dist/styles/button-group.css +113 -0
- package/dist/styles/button.css +380 -0
- package/dist/styles/calendar.css +295 -0
- package/dist/styles/canvas.css +327 -0
- package/dist/styles/card.css +146 -0
- package/dist/styles/carousel.css +107 -0
- package/dist/styles/chart.css +227 -0
- package/dist/styles/chat-confirmation.css +29 -0
- package/dist/styles/chat-plan.css +109 -0
- package/dist/styles/chat-sources.css +191 -0
- package/dist/styles/checkbox.css +123 -0
- package/dist/styles/choice-list.css +343 -0
- package/dist/styles/code-block.css +211 -0
- package/dist/styles/collapsible.css +72 -0
- package/dist/styles/combobox.css +347 -0
- package/dist/styles/command.css +233 -0
- package/dist/styles/context-menu.css +151 -0
- package/dist/styles/dialog.css +237 -0
- package/dist/styles/dropdown-menu.css +150 -0
- package/dist/styles/field.css +341 -0
- package/dist/styles/file-viewer.css +278 -0
- package/dist/styles/form.css +155 -0
- package/dist/styles/glass-banner.css +308 -0
- package/dist/styles/gradient-banner.css +256 -0
- package/dist/styles/hover-card.css +95 -0
- package/dist/styles/index.css +262 -0
- package/dist/styles/input-group.css +290 -0
- package/dist/styles/input-otp.css +138 -0
- package/dist/styles/input.css +99 -0
- package/dist/styles/kbd.css +59 -0
- package/dist/styles/label.css +89 -0
- package/dist/styles/mention-list.css +166 -0
- package/dist/styles/menubar.css +226 -0
- package/dist/styles/message-actions.css +83 -0
- package/dist/styles/message-response.css +176 -0
- package/dist/styles/message-scroller.css +136 -0
- package/dist/styles/message.css +310 -0
- package/dist/styles/navigation-menu.css +291 -0
- package/dist/styles/notice-banner.css +68 -0
- package/dist/styles/page-content.css +21 -0
- package/dist/styles/page-header.css +156 -0
- package/dist/styles/pagination.css +72 -0
- package/dist/styles/plan-outline.css +393 -0
- package/dist/styles/popover.css +140 -0
- package/dist/styles/progress.css +93 -0
- package/dist/styles/prompt-list.css +165 -0
- package/dist/styles/questionnaire.css +323 -0
- package/dist/styles/radio-group.css +120 -0
- package/dist/styles/reasoning-effort.css +157 -0
- package/dist/styles/resizable.css +108 -0
- package/dist/styles/result-card.css +215 -0
- package/dist/styles/review-card.css +235 -0
- package/dist/styles/rich-text-editor.css +169 -0
- package/dist/styles/schema-diagram.css +419 -0
- package/dist/styles/scroll-area.css +77 -0
- package/dist/styles/search-dialog.css +363 -0
- package/dist/styles/search-field.css +17 -0
- package/dist/styles/select.css +285 -0
- package/dist/styles/selection-slot.css +31 -0
- package/dist/styles/separator.css +206 -0
- package/dist/styles/settings-list.css +81 -0
- package/dist/styles/sheet.css +361 -0
- package/dist/styles/sidebar.css +363 -0
- package/dist/styles/skeleton.css +80 -0
- package/dist/styles/slider.css +142 -0
- package/dist/styles/spinner.css +39 -0
- package/dist/styles/state-panel-samples.css +41 -0
- package/dist/styles/state-panel.css +182 -0
- package/dist/styles/state-sample.css +285 -0
- package/dist/styles/switch.css +128 -0
- package/dist/styles/table.css +604 -0
- package/dist/styles/tabs.css +223 -0
- package/dist/styles/textarea.css +78 -0
- package/dist/styles/themes/chatgpt.css +125 -0
- package/dist/styles/themes/claude.css +125 -0
- package/dist/styles/themes/cloudflare.css +125 -0
- package/dist/styles/themes/discord.css +125 -0
- package/dist/styles/themes/github.css +125 -0
- package/dist/styles/themes/grok.css +125 -0
- package/dist/styles/themes/linear.css +126 -0
- package/dist/styles/themes/neutral.css +125 -0
- package/dist/styles/themes/notion.css +125 -0
- package/dist/styles/themes/perplexity.css +125 -0
- package/dist/styles/themes/raycast.css +125 -0
- package/dist/styles/themes/spotify.css +125 -0
- package/dist/styles/themes/stripe.css +125 -0
- package/dist/styles/themes/supabase.css +125 -0
- package/dist/styles/themes/tulipfarm.css +125 -0
- package/dist/styles/themes/vercel.css +126 -0
- package/dist/styles/toast.css +197 -0
- package/dist/styles/toggle-group.css +161 -0
- package/dist/styles/toggle.css +119 -0
- package/dist/styles/tool-activity.css +396 -0
- package/dist/styles/toolbar-status.css +52 -0
- package/dist/styles/tooltip.css +122 -0
- package/dist/styles/typed-text.css +17 -0
- package/package.json +443 -4
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useId, useMemo, } from "react";
|
|
4
|
+
import { Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
|
|
5
|
+
import { cn } from "../internal/classes.js";
|
|
6
|
+
/** Theme names a `ChartConfig` entry can color, and the selector each one uses. */
|
|
7
|
+
const THEMES = { light: "", dark: ".dark" };
|
|
8
|
+
const INITIAL_DIMENSION = { width: 320, height: 200 };
|
|
9
|
+
export const chartIndicatorNames = ["dot", "line", "dashed"];
|
|
10
|
+
const cssKey = (key) => key.replace(/[^\w-]/g, "_");
|
|
11
|
+
/**
|
|
12
|
+
* The CSS variable a `ChartContainer` sets for one config key. Pass it as a
|
|
13
|
+
* series `fill` or `stroke`, for example `fill={chartColor("tasks")}`.
|
|
14
|
+
*/
|
|
15
|
+
export function chartColor(key) {
|
|
16
|
+
return `var(--sprout-chart-color-${cssKey(key)})`;
|
|
17
|
+
}
|
|
18
|
+
const ChartContext = createContext(null);
|
|
19
|
+
function useChart() {
|
|
20
|
+
const context = useContext(ChartContext);
|
|
21
|
+
if (!context)
|
|
22
|
+
throw new Error("useChart must be used within a <ChartContainer />");
|
|
23
|
+
return context;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Sizes a Recharts chart to its box and turns `config` into series colors
|
|
27
|
+
* and labels for the tooltip and legend. The box is 16:9 unless `className`
|
|
28
|
+
* or `style` gives it a height.
|
|
29
|
+
*/
|
|
30
|
+
export function ChartContainer({ id, className, children, config, initialDimension = INITIAL_DIMENSION, ...props }) {
|
|
31
|
+
const uniqueId = useId();
|
|
32
|
+
const chartId = `chart-${cssKey(id ?? uniqueId)}`;
|
|
33
|
+
const value = useMemo(() => ({ config }), [config]);
|
|
34
|
+
return (_jsx(ChartContext.Provider, { value: value, children: _jsxs("div", { "data-slot": "chart", "data-chart": chartId, className: cn("sprout-chart", className), ...props, children: [_jsx(ChartStyle, { id: chartId, config: config }), _jsx(ResponsiveContainer, { initialDimension: initialDimension, children: children })] }) }));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Writes `--sprout-chart-color-<key>` for every colored config entry, with a
|
|
38
|
+
* dark value for entries that pass `theme`. `ChartContainer` renders it.
|
|
39
|
+
*/
|
|
40
|
+
export function ChartStyle({ id, config, }) {
|
|
41
|
+
const colorConfig = Object.entries(config).filter(([, item]) => item.theme ?? item.color);
|
|
42
|
+
if (!colorConfig.length)
|
|
43
|
+
return null;
|
|
44
|
+
const rules = (theme) => colorConfig
|
|
45
|
+
.map(([key, item]) => {
|
|
46
|
+
const color = item.theme?.[theme] ?? item.color;
|
|
47
|
+
return color
|
|
48
|
+
? ` --sprout-chart-color-${cssKey(key)}: ${color};`
|
|
49
|
+
: null;
|
|
50
|
+
})
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.join("\n");
|
|
53
|
+
const scope = `[data-chart="${cssKey(id)}"]`;
|
|
54
|
+
const themed = colorConfig.some(([, item]) => item.theme);
|
|
55
|
+
const css = [
|
|
56
|
+
`${scope} {\n${rules("light")}\n}`,
|
|
57
|
+
themed ? `${THEMES.dark} ${scope} {\n${rules("dark")}\n}` : "",
|
|
58
|
+
themed
|
|
59
|
+
? `@media (prefers-color-scheme: dark) {\n:root:not(.light):not(.dark) ${scope} {\n${rules("dark")}\n}\n}`
|
|
60
|
+
: "",
|
|
61
|
+
]
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.join("\n");
|
|
64
|
+
return _jsx("style", { dangerouslySetInnerHTML: { __html: css } });
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Recharts' `Tooltip`, moving in 160ms. Pass `content={<ChartTooltipContent />}`.
|
|
68
|
+
* It stays still when reduced motion is on.
|
|
69
|
+
*/
|
|
70
|
+
export function ChartTooltip(props) {
|
|
71
|
+
return _jsx(Tooltip, { animationDuration: 160, ...props });
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The tooltip card: a label, then one row per series with its indicator,
|
|
75
|
+
* config label, and value. Recharts passes `active`, `payload`, and `label`.
|
|
76
|
+
*/
|
|
77
|
+
export function ChartTooltipContent({ active, payload, className, indicator = "dot", hideLabel = false, hideIndicator = false, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey, }) {
|
|
78
|
+
const { config } = useChart();
|
|
79
|
+
const tooltipLabel = useMemo(() => {
|
|
80
|
+
if (hideLabel || !payload?.length)
|
|
81
|
+
return null;
|
|
82
|
+
const [item] = payload;
|
|
83
|
+
const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`;
|
|
84
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
85
|
+
const value = !labelKey && typeof label === "string"
|
|
86
|
+
? (config[label]?.label ?? label)
|
|
87
|
+
: itemConfig?.label;
|
|
88
|
+
if (labelFormatter)
|
|
89
|
+
return (_jsx("div", { className: cn("sprout-chart-tooltip-label", labelClassName), children: labelFormatter(value, payload) }));
|
|
90
|
+
if (!value)
|
|
91
|
+
return null;
|
|
92
|
+
return (_jsx("div", { className: cn("sprout-chart-tooltip-label", labelClassName), children: value }));
|
|
93
|
+
}, [
|
|
94
|
+
label,
|
|
95
|
+
labelFormatter,
|
|
96
|
+
payload,
|
|
97
|
+
hideLabel,
|
|
98
|
+
labelClassName,
|
|
99
|
+
config,
|
|
100
|
+
labelKey,
|
|
101
|
+
]);
|
|
102
|
+
if (!active || !payload?.length)
|
|
103
|
+
return null;
|
|
104
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
105
|
+
return (_jsxs("div", { "data-slot": "chart-tooltip", "data-indicator": indicator, className: cn("sprout-chart-tooltip", className), children: [!nestLabel ? tooltipLabel : null, _jsx("div", { className: "sprout-chart-tooltip-items", children: payload
|
|
106
|
+
.filter((item) => item.type !== "none")
|
|
107
|
+
.map((item, index) => {
|
|
108
|
+
const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`;
|
|
109
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
110
|
+
const indicatorColor = color ??
|
|
111
|
+
item.payload?.fill ??
|
|
112
|
+
item.color;
|
|
113
|
+
return (_jsx("div", { "data-slot": "chart-tooltip-item", className: "sprout-chart-tooltip-item", children: formatter && item?.value !== undefined && item.name ? (formatter(item.value, item.name, item, index, item.payload)) : (_jsxs(_Fragment, { children: [itemConfig?.icon ? (_jsx(itemConfig.icon, {})) : (!hideIndicator && (_jsx("span", { "aria-hidden": "true", "data-slot": "chart-tooltip-indicator", className: "sprout-chart-tooltip-indicator", style: {
|
|
114
|
+
"--sprout-chart-indicator": indicatorColor,
|
|
115
|
+
} }))), _jsxs("div", { className: "sprout-chart-tooltip-row", "data-nested": nestLabel || undefined, children: [_jsxs("div", { className: "sprout-chart-tooltip-name", children: [nestLabel ? tooltipLabel : null, _jsx("span", { children: itemConfig?.label ?? item.name })] }), item.value != null && (_jsx("span", { className: "sprout-chart-tooltip-value", children: typeof item.value === "number"
|
|
116
|
+
? item.value.toLocaleString()
|
|
117
|
+
: String(item.value) }))] })] })) }, index));
|
|
118
|
+
}) })] }));
|
|
119
|
+
}
|
|
120
|
+
/** Recharts' `Legend`. Pass `content={<ChartLegendContent />}`. */
|
|
121
|
+
export const ChartLegend = Legend;
|
|
122
|
+
/** A centered row of swatches and config labels, one per series. */
|
|
123
|
+
export function ChartLegendContent({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey, }) {
|
|
124
|
+
const { config } = useChart();
|
|
125
|
+
if (!payload?.length)
|
|
126
|
+
return null;
|
|
127
|
+
return (_jsx("div", { "data-slot": "chart-legend", "data-align": verticalAlign === "top" ? "top" : "bottom", className: cn("sprout-chart-legend", className), children: payload
|
|
128
|
+
.filter((item) => item.type !== "none")
|
|
129
|
+
.map((item, index) => {
|
|
130
|
+
const key = `${nameKey ?? item.dataKey ?? "value"}`;
|
|
131
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
132
|
+
return (_jsxs("div", { "data-slot": "chart-legend-item", className: "sprout-chart-legend-item", children: [itemConfig?.icon && !hideIcon ? (_jsx(itemConfig.icon, {})) : (_jsx("span", { "aria-hidden": "true", className: "sprout-chart-legend-swatch", style: { backgroundColor: item.color } })), itemConfig?.label] }, index));
|
|
133
|
+
}) }));
|
|
134
|
+
}
|
|
135
|
+
function getPayloadConfigFromPayload(config, payload, key) {
|
|
136
|
+
if (typeof payload !== "object" || payload === null)
|
|
137
|
+
return undefined;
|
|
138
|
+
const inner = "payload" in payload &&
|
|
139
|
+
typeof payload.payload === "object" &&
|
|
140
|
+
payload.payload !== null
|
|
141
|
+
? payload.payload
|
|
142
|
+
: undefined;
|
|
143
|
+
const outer = payload;
|
|
144
|
+
let configLabelKey = key;
|
|
145
|
+
if (typeof outer[key] === "string")
|
|
146
|
+
configLabelKey = outer[key];
|
|
147
|
+
else if (inner && typeof inner[key] === "string")
|
|
148
|
+
configLabelKey = inner[key];
|
|
149
|
+
return configLabelKey in config ? config[configLabelKey] : config[key];
|
|
150
|
+
}
|
|
151
|
+
const seriesConfig = (series) => Object.fromEntries(series.map((item) => [item.key, { label: item.label, color: item.color }]));
|
|
152
|
+
const tooltipCursor = { fill: "var(--sprout-hover)" };
|
|
153
|
+
/**
|
|
154
|
+
* Stacked daily bars, 256px tall. Days are rows keyed by series; a missing or
|
|
155
|
+
* null value keeps that series out of the day's tooltip. The top series gets
|
|
156
|
+
* rounded corners.
|
|
157
|
+
*/
|
|
158
|
+
export function UsageChart({ days, series, title = "Usage by day", locale, className, }) {
|
|
159
|
+
const config = useMemo(() => seriesConfig(series), [series]);
|
|
160
|
+
const usageDay = (date) => new Date(`${String(date)}T00:00:00Z`).toLocaleDateString(locale, {
|
|
161
|
+
month: "short",
|
|
162
|
+
day: "numeric",
|
|
163
|
+
timeZone: "UTC",
|
|
164
|
+
});
|
|
165
|
+
return (_jsx(ChartContainer, { "data-variant": "usage", className: cn("sprout-chart-usage", className), config: config, children: _jsxs(BarChart, { data: days, accessibilityLayer: true, title: title, barCategoryGap: "20%", children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: "date", tickFormatter: usageDay, tickLine: false, axisLine: false, minTickGap: 24 }), _jsx(YAxis, { width: 40, tickLine: false, axisLine: false, allowDecimals: false }), _jsx(ChartTooltip, { cursor: tooltipCursor, content: _jsx(ChartTooltipContent, { labelFormatter: usageDay }) }), series.map((item, index) => (_jsx(Bar, { dataKey: item.key, stackId: "usage", fill: chartColor(item.key), radius: index === series.length - 1 ? [3, 3, 0, 0] : 0, isAnimationActive: false }, item.key)))] }) }));
|
|
166
|
+
}
|
|
167
|
+
/** Side-by-side bars per category with a legend in series order, 288px tall. */
|
|
168
|
+
export function CategoryChart({ rows, category, series, title, className, }) {
|
|
169
|
+
const config = useMemo(() => seriesConfig(series), [series]);
|
|
170
|
+
const name = title ?? `${series.map((item) => item.label).join(", ")} by ${category}`;
|
|
171
|
+
return (_jsx(ChartContainer, { "data-variant": "category", className: cn("sprout-chart-category", className), config: config, children: _jsxs(BarChart, { data: rows, accessibilityLayer: true, title: name, barCategoryGap: "24%", children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: category, tickLine: false, axisLine: false, minTickGap: 16 }), _jsx(YAxis, { width: 40, tickLine: false, axisLine: false, allowDecimals: false }), _jsx(ChartTooltip, { cursor: tooltipCursor, content: _jsx(ChartTooltipContent, {}) }), _jsx(ChartLegend, { itemSorter: null, content: _jsx(ChartLegendContent, {}) }), series.map((item) => (_jsx(Bar, { dataKey: item.key, fill: chartColor(item.key), radius: [3, 3, 0, 0], isAnimationActive: false }, item.key)))] }) }));
|
|
172
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
export declare const chatConfirmationStateNames: readonly ["pending", "submitting", "approved", "rejected", "expired", "cancelled", "error"];
|
|
3
|
+
export type ChatConfirmationDecision = "approve" | "reject";
|
|
4
|
+
export type ConfirmationState = {
|
|
5
|
+
kind: "pending";
|
|
6
|
+
} | {
|
|
7
|
+
kind: "submitting";
|
|
8
|
+
decision: ChatConfirmationDecision;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "approved";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "rejected";
|
|
13
|
+
} | {
|
|
14
|
+
kind: "expired";
|
|
15
|
+
} | {
|
|
16
|
+
kind: "cancelled";
|
|
17
|
+
} | {
|
|
18
|
+
kind: "error";
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
export type ChatConfirmationDetail = {
|
|
22
|
+
label: string;
|
|
23
|
+
value: string;
|
|
24
|
+
};
|
|
25
|
+
export interface ChatConfirmationProps extends Omit<ComponentProps<"div">, "title" | "children"> {
|
|
26
|
+
/** What the agent wants to do, such as “Remove the Notes field”. */
|
|
27
|
+
title: string;
|
|
28
|
+
/** One line above the facts, such as “Review these changes before approving.” */
|
|
29
|
+
description: string;
|
|
30
|
+
/** Labeled facts about the action, in order. */
|
|
31
|
+
details: readonly ChatConfirmationDetail[];
|
|
32
|
+
state: ConfirmationState;
|
|
33
|
+
onDecision: (decision: ChatConfirmationDecision) => void;
|
|
34
|
+
/** Locks both buttons, such as while the request is refreshing or cannot be verified. */
|
|
35
|
+
disabled?: boolean | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Asks someone in chat to approve or decline an action an agent proposed.
|
|
39
|
+
* Built on `ReviewCard`: it folds once decided, and when the decision was
|
|
40
|
+
* made from inside the card, focus moves to the outcome.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ChatConfirmation({ title, description, details, state, onDecision, disabled, className, ...props }: ChatConfirmationProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { ShieldUser } from "reicon-react/icons/ShieldUser";
|
|
5
|
+
import { cn } from "../internal/classes.js";
|
|
6
|
+
import { Button } from "./button.js";
|
|
7
|
+
import { ReviewCard, ReviewRow, ReviewRows, } from "./review-card.js";
|
|
8
|
+
import { Spinner } from "./spinner.js";
|
|
9
|
+
export const chatConfirmationStateNames = [
|
|
10
|
+
"pending",
|
|
11
|
+
"submitting",
|
|
12
|
+
"approved",
|
|
13
|
+
"rejected",
|
|
14
|
+
"expired",
|
|
15
|
+
"cancelled",
|
|
16
|
+
"error",
|
|
17
|
+
];
|
|
18
|
+
const settledKinds = new Set([
|
|
19
|
+
"approved",
|
|
20
|
+
"rejected",
|
|
21
|
+
"expired",
|
|
22
|
+
"cancelled",
|
|
23
|
+
]);
|
|
24
|
+
const badges = {
|
|
25
|
+
pending: { label: "Approval required", variant: "warning" },
|
|
26
|
+
submitting: { label: "Approval required", variant: "warning" },
|
|
27
|
+
error: { label: "Approval required", variant: "warning" },
|
|
28
|
+
approved: { label: "Approved", variant: "info" },
|
|
29
|
+
rejected: { label: "Declined", variant: "secondary" },
|
|
30
|
+
expired: { label: "Expired", variant: "secondary" },
|
|
31
|
+
cancelled: { label: "Cancelled", variant: "secondary" },
|
|
32
|
+
};
|
|
33
|
+
const outcomes = {
|
|
34
|
+
approved: "Approval recorded. This does not confirm the action has completed.",
|
|
35
|
+
rejected: "The proposed action was declined.",
|
|
36
|
+
expired: "This approval expired. No approval was granted.",
|
|
37
|
+
cancelled: "This approval was cancelled. No approval was granted.",
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Asks someone in chat to approve or decline an action an agent proposed.
|
|
41
|
+
* Built on `ReviewCard`: it folds once decided, and when the decision was
|
|
42
|
+
* made from inside the card, focus moves to the outcome.
|
|
43
|
+
*/
|
|
44
|
+
export function ChatConfirmation({ title, description, details, state, onDecision, disabled = false, className, ...props }) {
|
|
45
|
+
const done = settledKinds.has(state.kind);
|
|
46
|
+
const section = useRef(null);
|
|
47
|
+
const result = useRef(null);
|
|
48
|
+
const submittedHere = useRef(false);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (state.kind === "submitting")
|
|
51
|
+
submittedHere.current =
|
|
52
|
+
section.current?.contains(document.activeElement) ?? false;
|
|
53
|
+
if (done && submittedHere.current) {
|
|
54
|
+
result.current?.focus();
|
|
55
|
+
submittedHere.current = false;
|
|
56
|
+
}
|
|
57
|
+
}, [done, state.kind]);
|
|
58
|
+
const submitting = state.kind === "submitting";
|
|
59
|
+
const busy = (decision) => state.kind === "submitting" && state.decision === decision;
|
|
60
|
+
const outcome = outcomes[state.kind];
|
|
61
|
+
return (_jsx("div", { ref: section, "data-slot": "chat-confirmation", "data-state": state.kind, ...props, className: cn("sprout-chat-confirmation", className), children: _jsxs(ReviewCard, { icon: _jsx(ShieldUser, { size: 16 }), title: title, badge: badges[state.kind], settled: done, status: state.kind === "error" || outcome ? (_jsxs(_Fragment, { children: [state.kind === "error" && (_jsx("p", { role: "alert", className: "sprout-chat-confirmation-error", children: state.message })), outcome && (_jsx("p", { ref: result, tabIndex: -1, role: "status", className: "sprout-chat-confirmation-result", children: outcome }))] })) : undefined, actions: done ? undefined : (_jsxs(_Fragment, { children: [_jsxs(Button, { disabled: disabled || submitting, "aria-busy": busy("approve"), onClick: () => onDecision("approve"), children: [busy("approve") && _jsx(Spinner, { "aria-hidden": "true" }), "Approve action"] }), _jsxs(Button, { variant: "ghost", disabled: disabled || submitting, "aria-busy": busy("reject"), onClick: () => onDecision("reject"), children: [busy("reject") && _jsx(Spinner, { "aria-hidden": "true" }), "Decline"] })] })), children: [_jsx("p", { className: "sprout-chat-confirmation-description", children: description }), _jsx(ReviewRows, { children: details.map((detail) => (_jsx(ReviewRow, { label: detail.label, children: detail.value }, detail.label))) })] }) }));
|
|
62
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
export declare const chatPlanStepStateNames: readonly ["pending", "running", "complete", "error"];
|
|
3
|
+
export type ChatPlanStepState = (typeof chatPlanStepStateNames)[number];
|
|
4
|
+
export type ChatPlanStep = {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
/** One line under the title, such as why a step failed. */
|
|
8
|
+
detail?: string | undefined;
|
|
9
|
+
state: ChatPlanStepState;
|
|
10
|
+
};
|
|
11
|
+
export interface ChatPlanProps extends Omit<ComponentProps<"section">, "title" | "children"> {
|
|
12
|
+
/** The plan name. It names the section and shows above the steps. */
|
|
13
|
+
title: string;
|
|
14
|
+
/** Steps in order. With none, the plan says no steps were proposed yet. */
|
|
15
|
+
steps: readonly ChatPlanStep[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A plan an agent proposed in chat, with each step’s progress: not started,
|
|
19
|
+
* in progress, complete, or failed. The header counts completed steps.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ChatPlan({ title, steps, className, ...props }: ChatPlanProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check } from "reicon-react/icons/Check";
|
|
3
|
+
import { Clock } from "reicon-react/icons/Clock";
|
|
4
|
+
import { Warning } from "reicon-react/icons/Warning";
|
|
5
|
+
import { cn } from "../internal/classes.js";
|
|
6
|
+
import { Spinner } from "./spinner.js";
|
|
7
|
+
export const chatPlanStepStateNames = [
|
|
8
|
+
"pending",
|
|
9
|
+
"running",
|
|
10
|
+
"complete",
|
|
11
|
+
"error",
|
|
12
|
+
];
|
|
13
|
+
const stateLabels = {
|
|
14
|
+
pending: "Not started",
|
|
15
|
+
running: "In progress",
|
|
16
|
+
complete: "Complete",
|
|
17
|
+
error: "Failed",
|
|
18
|
+
};
|
|
19
|
+
function StepIcon({ state }) {
|
|
20
|
+
if (state === "running")
|
|
21
|
+
return _jsx(Spinner, { "aria-hidden": "true" });
|
|
22
|
+
if (state === "complete")
|
|
23
|
+
return _jsx(Check, { size: 16, "aria-hidden": "true" });
|
|
24
|
+
if (state === "error")
|
|
25
|
+
return _jsx(Warning, { size: 16, "aria-hidden": "true" });
|
|
26
|
+
return _jsx(Clock, { size: 16, "aria-hidden": "true" });
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A plan an agent proposed in chat, with each step’s progress: not started,
|
|
30
|
+
* in progress, complete, or failed. The header counts completed steps.
|
|
31
|
+
*/
|
|
32
|
+
export function ChatPlan({ title, steps, className, ...props }) {
|
|
33
|
+
const complete = steps.filter((step) => step.state === "complete").length;
|
|
34
|
+
return (_jsxs("section", { "data-slot": "chat-plan", "aria-label": title, ...props, className: cn("sprout-chat-plan", className), children: [_jsxs("div", { className: "sprout-chat-plan-header", children: [_jsx("h4", { className: "sprout-chat-plan-title", children: title }), _jsxs("span", { className: "sprout-chat-plan-count", children: [complete, " of ", steps.length, " complete"] })] }), steps.length === 0 ? (_jsx("p", { className: "sprout-chat-plan-empty", children: "No steps have been proposed yet." })) : (_jsx("ol", { className: "sprout-chat-plan-steps", children: steps.map((step) => (_jsxs("li", { "data-slot": "chat-plan-step", "data-state": step.state, className: "sprout-chat-plan-step", children: [_jsx("span", { className: "sprout-chat-plan-icon", children: _jsx(StepIcon, { state: step.state }) }, step.state), _jsxs("div", { className: "sprout-chat-plan-text", children: [_jsxs("p", { className: "sprout-chat-plan-step-title", children: [step.title, _jsxs("span", { className: "sprout-chat-plan-sr-only", children: [" · ", stateLabels[step.state]] })] }), step.detail && (_jsx("p", { className: "sprout-chat-plan-detail", children: step.detail }))] })] }, step.id))) }))] }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
export type ChatSource = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
/** Where the passage lives, such as a collection or a heading. */
|
|
6
|
+
location: string;
|
|
7
|
+
excerpt: string;
|
|
8
|
+
/** Opens the source in a new tab. */
|
|
9
|
+
href: string;
|
|
10
|
+
};
|
|
11
|
+
export interface InlineCitationProps {
|
|
12
|
+
source: ChatSource;
|
|
13
|
+
/** The citation number shown in the reply and in the sources list. */
|
|
14
|
+
number: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A numbered citation inside a reply. Hovering, clicking, tapping, or pressing
|
|
18
|
+
* Enter shows the cited passage and where it comes from.
|
|
19
|
+
*/
|
|
20
|
+
export declare function InlineCitation({ source, number }: InlineCitationProps): import("react").JSX.Element;
|
|
21
|
+
export interface ChatSourcesProps extends Omit<ComponentProps<"div">, "children"> {
|
|
22
|
+
/** Sources in citation order. With none, it says no sources were used. */
|
|
23
|
+
sources: readonly ChatSource[];
|
|
24
|
+
/** Start with the list open. */
|
|
25
|
+
defaultOpen?: boolean | undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The sources a reply used. A count opens the numbered list; numbers match
|
|
29
|
+
* each `InlineCitation`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function ChatSources({ sources, defaultOpen, className, ...props }: ChatSourcesProps): import("react").JSX.Element;
|
|
32
|
+
export declare const citationStateKindNames: readonly ["idle", "loading", "ready", "unavailable", "error"];
|
|
33
|
+
export type CitationStateKind = (typeof citationStateKindNames)[number];
|
|
34
|
+
export type CitationState = {
|
|
35
|
+
kind: "idle";
|
|
36
|
+
} | {
|
|
37
|
+
kind: "loading";
|
|
38
|
+
} | {
|
|
39
|
+
kind: "ready";
|
|
40
|
+
title: string;
|
|
41
|
+
location: string;
|
|
42
|
+
excerpt: string;
|
|
43
|
+
} | {
|
|
44
|
+
kind: "unavailable";
|
|
45
|
+
message: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "error";
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
export interface CheckedCitationProps {
|
|
51
|
+
/** The linked words in the reply. */
|
|
52
|
+
label: string;
|
|
53
|
+
state: CitationState;
|
|
54
|
+
/** Called each time the passage opens, to check access again. */
|
|
55
|
+
onOpen: () => void;
|
|
56
|
+
/** Called by “Load source again” after an error. */
|
|
57
|
+
onRetry: () => void;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A citation link whose passage is checked each time it opens, on hover or
|
|
61
|
+
* click. While checking it shows a spinner; an unavailable source never shows
|
|
62
|
+
* an old passage.
|
|
63
|
+
*/
|
|
64
|
+
export declare function CheckedCitation({ label, state, onOpen, onRetry, }: CheckedCitationProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { ArrowUpRight } from "reicon-react/icons/ArrowUpRight";
|
|
5
|
+
import { ChevronDown } from "reicon-react/icons/ChevronDown";
|
|
6
|
+
import { FileText } from "reicon-react/icons/FileText";
|
|
7
|
+
import { cn } from "../internal/classes.js";
|
|
8
|
+
import { Button } from "./button.js";
|
|
9
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "./collapsible.js";
|
|
10
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
11
|
+
import { Spinner } from "./spinner.js";
|
|
12
|
+
const hoverDelay = 300;
|
|
13
|
+
const hoverCloseDelay = 150;
|
|
14
|
+
function hostOf(href) {
|
|
15
|
+
try {
|
|
16
|
+
return new URL(href).hostname.replace(/^www\./, "");
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function SourceContent({ source, number, }) {
|
|
23
|
+
const host = number === undefined ? null : hostOf(source.href);
|
|
24
|
+
return (_jsxs("div", { "data-slot": "chat-source", className: "sprout-chat-sources-source", children: [number !== undefined && (_jsxs("p", { className: "sprout-chat-sources-preview-head", children: [_jsx("span", { className: "sprout-chat-sources-badge", "aria-hidden": "true", children: number }), _jsx("span", { children: host ?? `Source ${number}` })] })), _jsx("p", { className: "sprout-chat-sources-title", children: source.title }), _jsx("p", { className: "sprout-chat-sources-location", children: source.location }), _jsx("p", { className: "sprout-chat-sources-excerpt", children: source.excerpt }), _jsxs("a", { href: source.href, target: "_blank", rel: "noopener noreferrer", className: "sprout-chat-sources-link", children: ["Open source", _jsx(ArrowUpRight, { size: 14, "aria-hidden": "true" }), _jsx("span", { className: "sprout-chat-sources-sr-only", children: " in a new tab" })] })] }));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A numbered citation inside a reply. Hovering, clicking, tapping, or pressing
|
|
28
|
+
* Enter shows the cited passage and where it comes from.
|
|
29
|
+
*/
|
|
30
|
+
export function InlineCitation({ source, number }) {
|
|
31
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { openOnHover: true, delay: hoverDelay, closeDelay: hoverCloseDelay, "data-slot": "inline-citation", className: "sprout-chat-sources-citation", render: _jsx(Button, { variant: "secondary", size: "icon-xs", "aria-label": `Source ${number}: ${source.title}` }), children: number }), _jsx(PopoverContent, { className: "sprout-chat-sources-preview", children: _jsx(SourceContent, { source: source, number: number }) })] }));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The sources a reply used. A count opens the numbered list; numbers match
|
|
35
|
+
* each `InlineCitation`.
|
|
36
|
+
*/
|
|
37
|
+
export function ChatSources({ sources, defaultOpen, className, ...props }) {
|
|
38
|
+
if (sources.length === 0)
|
|
39
|
+
return (_jsx("div", { "data-slot": "chat-sources", "data-empty": "", ...props, className: cn("sprout-chat-sources", className), children: _jsx("p", { className: "sprout-chat-sources-empty", children: "No sources were used for this response." }) }));
|
|
40
|
+
return (_jsxs(Collapsible, { "data-slot": "chat-sources", defaultOpen: defaultOpen, ...props, className: cn("sprout-chat-sources", className), children: [_jsxs(CollapsibleTrigger, { className: "sprout-chat-sources-trigger", children: [_jsx(FileText, { size: 16, "aria-hidden": "true" }), _jsxs("span", { children: [sources.length, " ", sources.length === 1 ? "source" : "sources"] }), _jsx(ChevronDown, { size: 14, "aria-hidden": "true", "data-slot": "collapsible-indicator" })] }), _jsx(CollapsibleContent, { variant: "sidebar", children: _jsx("ol", { className: "sprout-chat-sources-list", children: sources.map((source, index) => (_jsxs("li", { className: "sprout-chat-sources-item", children: [_jsx("span", { className: "sprout-chat-sources-number", children: index + 1 }), _jsx(SourceContent, { source: source })] }, source.id))) }) })] }));
|
|
41
|
+
}
|
|
42
|
+
export const citationStateKindNames = [
|
|
43
|
+
"idle",
|
|
44
|
+
"loading",
|
|
45
|
+
"ready",
|
|
46
|
+
"unavailable",
|
|
47
|
+
"error",
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* A citation link whose passage is checked each time it opens, on hover or
|
|
51
|
+
* click. While checking it shows a spinner; an unavailable source never shows
|
|
52
|
+
* an old passage.
|
|
53
|
+
*/
|
|
54
|
+
export function CheckedCitation({ label, state, onOpen, onRetry, }) {
|
|
55
|
+
const popup = useRef(null);
|
|
56
|
+
return (_jsxs(Popover, { onOpenChange: (open) => {
|
|
57
|
+
if (open)
|
|
58
|
+
onOpen();
|
|
59
|
+
}, children: [_jsx(PopoverTrigger, { openOnHover: true, delay: hoverDelay, closeDelay: hoverCloseDelay, "data-slot": "checked-citation", className: "sprout-chat-sources-checked", render: _jsx(Button, { variant: "link", size: "task-link", "aria-label": `Source: ${label}` }), children: label }), _jsx(PopoverContent, { ref: popup, "data-citation-state": state.kind, className: "sprout-chat-sources-preview", children: state.kind === "idle" || state.kind === "loading" ? (_jsxs("p", { role: "status", className: "sprout-chat-sources-checking", children: [_jsx(Spinner, { "aria-hidden": "true" }), "Checking access to this source\u2026"] })) : state.kind === "ready" ? (_jsxs("div", { "data-slot": "chat-source", className: "sprout-chat-sources-source", children: [_jsx("p", { className: "sprout-chat-sources-title", children: state.title }), _jsx("p", { className: "sprout-chat-sources-location", children: state.location }), _jsx("p", { className: "sprout-chat-sources-excerpt", "data-preserve-lines": "", children: state.excerpt })] })) : (_jsxs("div", { className: "sprout-chat-sources-source", children: [_jsx("p", { className: "sprout-chat-sources-title", children: state.kind === "unavailable"
|
|
60
|
+
? "Source unavailable"
|
|
61
|
+
: "Couldn’t load this source" }), _jsx("p", { role: "alert", className: "sprout-chat-sources-excerpt", children: state.message }), state.kind === "error" && (_jsx(Button, { variant: "outline", size: "sm", className: "sprout-chat-sources-retry", onClick: () => {
|
|
62
|
+
// The button leaves as loading starts; keep focus in the passage.
|
|
63
|
+
popup.current?.focus();
|
|
64
|
+
onRetry();
|
|
65
|
+
}, children: "Load source again" }))] })) })] }));
|
|
66
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
|
2
|
+
export declare const checkboxVariantNames: readonly ["default", "neutral"];
|
|
3
|
+
export type CheckboxVariant = (typeof checkboxVariantNames)[number];
|
|
4
|
+
export interface CheckboxProps extends BaseCheckbox.Root.Props {
|
|
5
|
+
/**
|
|
6
|
+
* `default` fills with the primary color. `neutral` fills with the text
|
|
7
|
+
* color, for filter lists that should not look branded.
|
|
8
|
+
*/
|
|
9
|
+
variant?: CheckboxVariant | undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A 16px checkbox with a larger hit area. Name it with a wrapping `Label`,
|
|
13
|
+
* a `Label htmlFor` pointing at its `id`, or `aria-label`. Pass
|
|
14
|
+
* `indeterminate` to show a mixed state, such as a "select all" box.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Checkbox({ variant, indeterminate, className, disabled, ref, ...props }: CheckboxProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
|
4
|
+
import { Check } from "reicon-react/icons/Check";
|
|
5
|
+
import { Minus } from "reicon-react/icons/Minus";
|
|
6
|
+
import { cn, stateClassName } from "../internal/classes.js";
|
|
7
|
+
import { useFieldsetDisabled } from "../internal/fieldset.js";
|
|
8
|
+
export const checkboxVariantNames = ["default", "neutral"];
|
|
9
|
+
/**
|
|
10
|
+
* A 16px checkbox with a larger hit area. Name it with a wrapping `Label`,
|
|
11
|
+
* a `Label htmlFor` pointing at its `id`, or `aria-label`. Pass
|
|
12
|
+
* `indeterminate` to show a mixed state, such as a "select all" box.
|
|
13
|
+
*/
|
|
14
|
+
export function Checkbox({ variant = "default", indeterminate = false, className, disabled, ref, ...props }) {
|
|
15
|
+
const [fieldsetDisabled, fieldsetRef] = useFieldsetDisabled(ref);
|
|
16
|
+
return (_jsx(BaseCheckbox.Root, { "data-slot": "checkbox", "data-variant": variant, indeterminate: indeterminate, ...props, ref: fieldsetRef, disabled: disabled || fieldsetDisabled, className: stateClassName(cn("sprout-checkbox", `sprout-checkbox-variant-${variant}`), className), children: _jsx(BaseCheckbox.Indicator, { "data-slot": "checkbox-indicator", className: "sprout-checkbox-indicator", children: indeterminate ? (_jsx(Minus, { "aria-hidden": "true" })) : (_jsx(Check, { "aria-hidden": "true" })) }) }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export declare const choiceListLayoutNames: readonly ["stacked", "compact"];
|
|
3
|
+
export type ChoiceListLayout = (typeof choiceListLayoutNames)[number];
|
|
4
|
+
export interface ChoiceListOption {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
/** Shows a "Recommended" badge. A label ending in "(Recommended)" does the same. */
|
|
8
|
+
recommended?: boolean | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface ChoiceListTextOption {
|
|
11
|
+
value: string;
|
|
12
|
+
/** True while the typed answer is the chosen one. */
|
|
13
|
+
active: boolean;
|
|
14
|
+
onChange: (value: string) => void;
|
|
15
|
+
onFocus: () => void;
|
|
16
|
+
/** Id of the answer box, for an "Other answer" toggle's `aria-controls`. */
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
error: string | null;
|
|
19
|
+
label?: string | undefined;
|
|
20
|
+
placeholder?: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
export interface ChoiceListProps {
|
|
23
|
+
legend: string;
|
|
24
|
+
options: readonly ChoiceListOption[];
|
|
25
|
+
value: readonly string[];
|
|
26
|
+
onChange: (value: string[]) => void;
|
|
27
|
+
/** Checkboxes instead of radios. */
|
|
28
|
+
multiple?: boolean | undefined;
|
|
29
|
+
disabled?: boolean | undefined;
|
|
30
|
+
error?: string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* `stacked` draws bordered rows with radio or checkbox marks. `compact`
|
|
33
|
+
* draws numbered rows in a scrolling list, for questions inside a chat.
|
|
34
|
+
*/
|
|
35
|
+
layout?: ChoiceListLayout | undefined;
|
|
36
|
+
/** A free-text answer after the options. Compact lists show it only while active. */
|
|
37
|
+
textOption?: ChoiceListTextOption | undefined;
|
|
38
|
+
className?: string | undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A fieldset of radio or checkbox rows for answering a question, with an
|
|
42
|
+
* optional typed answer. Value is always an array of option ids.
|
|
43
|
+
*/
|
|
44
|
+
export declare function ChoiceList({ legend, options, value, onChange, multiple, disabled, error, layout, textOption, className, }: ChoiceListProps): import("react").JSX.Element;
|
|
45
|
+
export interface ChoiceSummaryProps {
|
|
46
|
+
prompt: string;
|
|
47
|
+
/** The recorded answer. Omit it when the question expired or was cancelled. */
|
|
48
|
+
answer?: string | undefined;
|
|
49
|
+
className?: string | undefined;
|
|
50
|
+
children?: ReactNode;
|
|
51
|
+
}
|
|
52
|
+
/** One line that shows a question and its recorded answer. */
|
|
53
|
+
export declare function ChoiceSummary({ prompt, answer, className, children, }: ChoiceSummaryProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import { Check } from "reicon-react/icons/Check";
|
|
5
|
+
import { HelpCircle } from "reicon-react/icons/HelpCircle";
|
|
6
|
+
import { cn } from "../internal/classes.js";
|
|
7
|
+
import { Badge } from "./badge.js";
|
|
8
|
+
export const choiceListLayoutNames = ["stacked", "compact"];
|
|
9
|
+
const recommendedSuffix = /\s*\(recommended\)\s*$/i;
|
|
10
|
+
/**
|
|
11
|
+
* A fieldset of radio or checkbox rows for answering a question, with an
|
|
12
|
+
* optional typed answer. Value is always an array of option ids.
|
|
13
|
+
*/
|
|
14
|
+
export function ChoiceList({ legend, options, value, onChange, multiple = false, disabled = false, error, layout = "stacked", textOption, className, }) {
|
|
15
|
+
const name = useId();
|
|
16
|
+
const compact = layout === "compact";
|
|
17
|
+
const errorId = `${name}-choices-error`;
|
|
18
|
+
const textId = `${name}-text`;
|
|
19
|
+
const textErrorId = `${name}-text-error`;
|
|
20
|
+
return (_jsxs("fieldset", { "data-slot": "choice-list", "data-layout": layout, "data-multiple": multiple || undefined, disabled: disabled, className: cn("sprout-choice-list", className), children: [_jsx("legend", { className: compact && !multiple
|
|
21
|
+
? "sprout-choice-list-sr-only"
|
|
22
|
+
: "sprout-choice-list-legend", children: legend }), _jsx("div", { "data-slot": "choice-list-options", className: "sprout-choice-list-options", children: options.map((option, index) => {
|
|
23
|
+
const checked = value.includes(option.id);
|
|
24
|
+
const recommended = option.recommended === true || recommendedSuffix.test(option.label);
|
|
25
|
+
const label = option.label.replace(recommendedSuffix, "");
|
|
26
|
+
return (_jsxs("label", { "data-slot": "choice-list-option", "data-checked": checked || undefined, "data-disabled": disabled || undefined, "data-recommended": recommended || undefined, className: "sprout-choice-list-option", children: [_jsx("input", { type: multiple ? "checkbox" : "radio", name: name, value: option.id, checked: checked, "aria-invalid": error ? true : undefined, "aria-describedby": error ? errorId : undefined, onChange: (event) => onChange(multiple
|
|
27
|
+
? event.target.checked
|
|
28
|
+
? [...value, option.id]
|
|
29
|
+
: value.filter((id) => id !== option.id)
|
|
30
|
+
: [option.id]), className: "sprout-choice-list-input" }), _jsx("span", { "aria-hidden": "true", className: "sprout-choice-list-mark", children: checked ? _jsx(Check, {}) : compact ? index + 1 : null }), _jsxs("span", { className: "sprout-choice-list-label", children: [label, recommended && (_jsx(Badge, { variant: "info", className: "sprout-choice-list-badge", children: "Recommended" }))] })] }, option.id));
|
|
31
|
+
}) }), error && (_jsx("p", { id: errorId, className: "sprout-choice-list-error", children: error })), textOption && (!compact || textOption.active) && (_jsxs("div", { id: textOption.id ?? `${name}-custom`, "data-slot": "choice-list-text", "data-checked": textOption.active || undefined, "data-invalid": textOption.error ? true : undefined, className: "sprout-choice-list-text", children: [_jsx("span", { "aria-hidden": "true", className: "sprout-choice-list-mark", children: textOption.active && _jsx(Check, {}) }), _jsxs("div", { className: "sprout-choice-list-text-body", children: [_jsx("label", { htmlFor: textId, className: "sprout-choice-list-text-label", children: textOption.label ?? "Your answer" }), _jsx("textarea", { id: textId, autoFocus: compact, rows: 1, value: textOption.value, disabled: disabled, placeholder: textOption.placeholder ?? "Type a different answer…", "aria-describedby": textOption.error ? textErrorId : undefined, "aria-invalid": textOption.error ? true : undefined, onFocus: textOption.onFocus, onChange: (event) => textOption.onChange(event.target.value), className: "sprout-choice-list-textarea" }), textOption.error && (_jsx("p", { id: textErrorId, role: "alert", className: "sprout-choice-list-error", children: textOption.error }))] })] }))] }));
|
|
32
|
+
}
|
|
33
|
+
/** One line that shows a question and its recorded answer. */
|
|
34
|
+
export function ChoiceSummary({ prompt, answer, className, children, }) {
|
|
35
|
+
return (_jsxs("div", { "data-slot": "choice-summary", className: cn("sprout-choice-summary", className), children: [_jsx(HelpCircle, { "aria-hidden": "true", className: "sprout-choice-summary-icon" }), _jsx("span", { className: "sprout-choice-summary-prompt", children: prompt }), answer && (_jsxs("span", { className: "sprout-choice-summary-answer", children: [_jsx(Check, { "aria-hidden": "true", className: "sprout-choice-summary-check" }), _jsx("span", { className: "sprout-choice-summary-answer-text", children: answer })] })), children] }));
|
|
36
|
+
}
|