@timbal-ai/timbal-react 0.8.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +100 -6
  3. package/dist/app.cjs +2538 -1262
  4. package/dist/app.d.cts +11 -6
  5. package/dist/app.d.ts +11 -6
  6. package/dist/app.esm.js +43 -6
  7. package/dist/{button-ClSgD6OF.d.cts → button-BoyX5pM_.d.cts} +1 -1
  8. package/dist/{button-ClSgD6OF.d.ts → button-BoyX5pM_.d.ts} +1 -1
  9. package/dist/{chart-artifact-Bl67kre7.d.ts → chart-artifact-BZp7nmaf.d.ts} +430 -14
  10. package/dist/{chart-artifact-BzcvblDe.d.cts → chart-artifact-CX-rh9nq.d.cts} +430 -14
  11. package/dist/{chat-Bed4FQSl.d.cts → chat-DCms8pJ_.d.cts} +31 -4
  12. package/dist/{chat-Bed4FQSl.d.ts → chat-DCms8pJ_.d.ts} +31 -4
  13. package/dist/chat.cjs +1111 -776
  14. package/dist/chat.d.cts +1 -1
  15. package/dist/chat.d.ts +1 -1
  16. package/dist/chat.esm.js +3 -3
  17. package/dist/{chunk-QVAUCVQA.esm.js → chunk-4AKJ6FKE.esm.js} +277 -4
  18. package/dist/chunk-6HWMJNZT.esm.js +3439 -0
  19. package/dist/{chunk-VWHHKAHN.esm.js → chunk-FRZOEYBO.esm.js} +4 -4
  20. package/dist/chunk-JEAUF54A.esm.js +52 -0
  21. package/dist/{chunk-OISVICYF.esm.js → chunk-P3KDAYX6.esm.js} +1 -1
  22. package/dist/{chunk-6YVKCVEP.esm.js → chunk-TK2AGIME.esm.js} +1106 -298
  23. package/dist/{chunk-CFU3YDTV.esm.js → chunk-XCM3V6RK.esm.js} +5 -5
  24. package/dist/{chunk-5ZKLPWVN.esm.js → chunk-YXZ22OJN.esm.js} +849 -667
  25. package/dist/index.cjs +6070 -1605
  26. package/dist/index.d.cts +8 -6
  27. package/dist/index.d.ts +8 -6
  28. package/dist/index.esm.js +427 -11
  29. package/dist/pill-segmented-tabs-Ba5q0feL.d.cts +500 -0
  30. package/dist/pill-segmented-tabs-Ba5q0feL.d.ts +500 -0
  31. package/dist/studio.cjs +1333 -998
  32. package/dist/studio.d.cts +2 -2
  33. package/dist/studio.d.ts +2 -2
  34. package/dist/studio.esm.js +5 -5
  35. package/dist/styles.css +220 -0
  36. package/dist/ui.cjs +3592 -89
  37. package/dist/ui.d.cts +72 -96
  38. package/dist/ui.d.ts +72 -96
  39. package/dist/ui.esm.js +400 -6
  40. package/dist/{welcome-COOb05a5.d.cts → welcome-CRqOPKMp.d.cts} +1 -1
  41. package/dist/{welcome-DE08m9ca.d.ts → welcome-DlHUa3OL.d.ts} +1 -1
  42. package/package.json +9 -3
  43. package/dist/chunk-P4SN7M67.esm.js +0 -435
@@ -13,14 +13,16 @@ import {
13
13
  monotoneAreaPath,
14
14
  monotoneLinePath,
15
15
  studioIntegrationCardClass,
16
- studioSearchChromeClass,
17
- studioSecondaryChromeClass,
18
16
  studioTopbarPillHeightClass,
19
17
  toNum
20
- } from "./chunk-5ZKLPWVN.esm.js";
18
+ } from "./chunk-YXZ22OJN.esm.js";
19
+ import {
20
+ Checkbox,
21
+ Skeleton
22
+ } from "./chunk-JEAUF54A.esm.js";
21
23
  import {
22
24
  PillSegmentedTabs
23
- } from "./chunk-OISVICYF.esm.js";
25
+ } from "./chunk-P3KDAYX6.esm.js";
24
26
  import {
25
27
  Button,
26
28
  Dialog,
@@ -31,10 +33,195 @@ import {
31
33
  TIMBAL_V2_SWITCH_THUMB,
32
34
  TIMBAL_V2_SWITCH_TRACK_OFF,
33
35
  TimbalV2Button,
34
- cn
35
- } from "./chunk-QVAUCVQA.esm.js";
36
+ cn,
37
+ controlClass
38
+ } from "./chunk-4AKJ6FKE.esm.js";
39
+
40
+ // src/design/ui-vocabulary.ts
41
+ var SEMANTIC_COLOR_TOKENS = [
42
+ // shadcn-style base tokens
43
+ "background",
44
+ "foreground",
45
+ "card",
46
+ "card-foreground",
47
+ "popover",
48
+ "popover-foreground",
49
+ "primary",
50
+ "primary-foreground",
51
+ "secondary",
52
+ "secondary-foreground",
53
+ "muted",
54
+ "muted-foreground",
55
+ "accent",
56
+ "accent-foreground",
57
+ "destructive",
58
+ "destructive-foreground",
59
+ "border",
60
+ "input",
61
+ "ring",
62
+ // sidebar scope
63
+ "sidebar",
64
+ "sidebar-foreground",
65
+ "sidebar-primary",
66
+ "sidebar-primary-foreground",
67
+ "sidebar-accent",
68
+ "sidebar-accent-foreground",
69
+ "sidebar-border",
70
+ "sidebar-ring",
71
+ // timbal chrome extensions
72
+ "elevated-from",
73
+ "elevated-to",
74
+ "modal-from",
75
+ "modal-to",
76
+ "playground-from",
77
+ "playground-via",
78
+ "playground-to",
79
+ "composer-bg",
80
+ "composer-border",
81
+ "composer-border-focus",
82
+ "bubble-user",
83
+ "bubble-user-foreground",
84
+ "code-block-bg",
85
+ "code-header-bg"
86
+ ];
87
+ var RESERVED_GRADIENT_TOKENS = [
88
+ "primary-fill-from",
89
+ "primary-fill-to",
90
+ "primary-fill-hover-from",
91
+ "primary-fill-hover-to",
92
+ "primary-fill-active-from",
93
+ "primary-fill-active-to",
94
+ "secondary-fill-hover-from",
95
+ "secondary-fill-hover-to",
96
+ "secondary-fill-active-from",
97
+ "secondary-fill-active-to",
98
+ "destructive-fill-hover-from",
99
+ "destructive-fill-hover-to",
100
+ "destructive-fill-active-from",
101
+ "destructive-fill-active-to",
102
+ "ghost-fill-hover",
103
+ "ghost-fill-active",
104
+ "elevated-from",
105
+ "elevated-to",
106
+ "modal-from",
107
+ "modal-to",
108
+ "playground-from",
109
+ "playground-via",
110
+ "playground-to"
111
+ ];
112
+ var TAILWIND_PALETTE_COLORS = [
113
+ "slate",
114
+ "gray",
115
+ "zinc",
116
+ "neutral",
117
+ "stone",
118
+ "red",
119
+ "orange",
120
+ "amber",
121
+ "yellow",
122
+ "lime",
123
+ "green",
124
+ "emerald",
125
+ "teal",
126
+ "cyan",
127
+ "sky",
128
+ "blue",
129
+ "indigo",
130
+ "violet",
131
+ "purple",
132
+ "fuchsia",
133
+ "pink",
134
+ "rose"
135
+ ];
136
+ var COLOR_UTILITY_PREFIXES = [
137
+ "bg",
138
+ "text",
139
+ "border",
140
+ "ring",
141
+ "from",
142
+ "via",
143
+ "to",
144
+ "fill",
145
+ "stroke",
146
+ "decoration",
147
+ "outline",
148
+ "shadow",
149
+ "divide",
150
+ "accent",
151
+ "caret"
152
+ ];
153
+ var SLOP_BUDGETS = {
154
+ /** Max decorative/standalone icons rendered in a single generated file. */
155
+ maxIconsPerView: 6,
156
+ /** Max consecutive list rows separated by an explicit border/divider before
157
+ * it reads as a "ruled table" — prefer spacing or zebra instead. */
158
+ maxRowDividers: 2
159
+ };
160
+ var HOUSE_RULES = [
161
+ {
162
+ id: "semantic-color",
163
+ rule: "Color only through semantic tokens \u2014 never a raw palette color, hex, or oklch literal.",
164
+ why: "The theme generator owns every color; hardcoding breaks dark mode and rebranding.",
165
+ slop: `<span className="text-blue-600 bg-green-50">`,
166
+ good: `<span className="text-primary bg-muted">`
167
+ },
168
+ {
169
+ id: "no-decorative-icons",
170
+ rule: "Icons must earn their place (action, nav, or status). Never add an icon beside a label that already says the thing.",
171
+ why: "An icon on every tile/card is the #1 tell of generated slop.",
172
+ slop: `<StatTile label={<><BarChart2 /> Revenue</>} value="$95k" />`,
173
+ good: `<StatTile label="Revenue" value="$95k" />`
174
+ },
175
+ {
176
+ id: "neutral-trend",
177
+ rule: "Don't put a colored trend pill on every metric. Use a trend only when the delta is the point, and keep it muted.",
178
+ why: "Loud green/red pills everywhere are noise, not signal.",
179
+ slop: `<MetricTile trend="+8%" className="text-green-500" />`,
180
+ good: `<MetricTile label="Win rate" value="50%" />`
181
+ },
182
+ {
183
+ id: "values-normal-weight",
184
+ rule: "Metric values use normal font weight, not bold.",
185
+ why: "Giant bold numbers read as a template; normal weight reads as a product.",
186
+ slop: `<span className="text-3xl font-bold tabular-nums">$322k</span>`,
187
+ good: `<span className="text-2xl font-normal tabular-nums">$322k</span>`
188
+ },
189
+ {
190
+ id: "no-card-in-card",
191
+ rule: "Don't nest a bordered card inside another bordered card. Group with spacing or a Section instead.",
192
+ why: "Card-in-card doubles borders and shadows for no information gain."
193
+ },
194
+ {
195
+ id: "no-row-dividers",
196
+ rule: "Don't put a divider between every list row. Use spacing or zebra striping.",
197
+ why: "A rule under every row turns a clean list into a dense ledger."
198
+ },
199
+ {
200
+ id: "no-data-gradient",
201
+ rule: "Gradients are reserved for chrome (composer, elevated surface, playground). Never on a data card, tile, or table.",
202
+ why: "Gradient stat cards are the canonical 'AI dashboard' look."
203
+ },
204
+ {
205
+ id: "compose-from-blocks",
206
+ rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
207
+ why: "Slop appears the moment generation falls below the curated block layer."
208
+ },
209
+ {
210
+ id: "use-kit-controls",
211
+ rule: "Use the kit's controls (SearchInput, Select, DropdownMenu, FieldInput, FieldSelect) \u2014 never hand-roll an input/trigger surface (`border-input rounded-* bg-\u2026`).",
212
+ why: "Hand-rolled controls drift from the shared control-surface skin and look foreign next to kit controls.",
213
+ slop: `<button className="rounded-lg border border-input bg-transparent px-3 h-9">`,
214
+ good: `<SelectTrigger><SelectValue /></SelectTrigger>`
215
+ }
216
+ ];
36
217
 
37
218
  // src/app/agent-instructions.ts
219
+ var ANTI_SLOP_CHECKLIST = HOUSE_RULES.map((r) => {
220
+ const pair = r.slop && r.good ? `
221
+ - slop: \`${r.slop}\`
222
+ - good: \`${r.good}\`` : "";
223
+ return `- **${r.id}** \u2014 ${r.rule} (${r.why})${pair}`;
224
+ }).join("\n");
38
225
  var APP_KIT_AGENT_INSTRUCTIONS = `
39
226
  ## App kit (@timbal-ai/timbal-react/app)
40
227
 
@@ -58,12 +245,12 @@ Presentational groups \u2014 import from the package root, not from these paths:
58
245
 
59
246
  | Folder | Components |
60
247
  |--------|------------|
61
- | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`ChartPanel\` |
248
+ | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
62
249
  | \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
63
250
  | \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
64
251
  | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
65
252
  | \`layout/\` | \`AppShell\`, \`Page\`, \`Section\` |
66
- | \`charts\` (re-exported) | \`LineAreaChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
253
+ | \`charts\` (re-exported) | \`LineAreaChart\`, \`PieChart\`, \`RadialChart\`, \`RadarChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
67
254
 
68
255
  Also re-exported: \`Button\`, \`TimbalChat\`, \`ChartArtifactView\`, \`APP_KIT_AGENT_INSTRUCTIONS\`.
69
256
 
@@ -78,11 +265,19 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
78
265
  | **Context** | Do not show raw JSON context in the panel header; keep context in \`AppCopilotProvider\` only. |
79
266
  | **Theming** | Use semantic Tailwind tokens (\`bg-background\`, \`text-foreground\`, \`border-border\`, \`bg-elevated-from\`, etc.) from the host app's \`styles.css\`. To rebrand, **never hand-author OKLCH** \u2014 call \`createTimbalTheme({ brand })\` + \`themeToCss\`/\`applyTimbalTheme\`, or apply a catalog preset (\`TIMBAL_THEME_PRESETS\` / \`applyThemePreset\`). To offer styles, render \`ThemePresetGallery\`. See \`THEME_AGENT_INSTRUCTIONS\`. |
80
267
  | **Layout chrome** | \`Page\` \u2192 \`Section\` for main content hierarchy. \`AppShellTopbar\` for global actions (auth, theme). |
81
- | **Data** | Prefer \`DataTable\` with typed \`columns\` / \`rows\` / \`getRowKey\`; use \`ChartPanel\` with \`ChartArtifact\` for charts. |
268
+ | **Data** | Prefer \`DataTable\` with typed \`columns\` / \`rows\` / \`getRowKey\`; use \`ChartPanel\` with a \`ChartArtifact\` for charts (set \`chartType\` + options \u2014 see the chart catalog). Chart colors come from the theme \`--chart-1..6\` tokens; pass \`series[].color\` / \`colors\` only to override, never raw hex on every series. |
82
269
  | **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
83
270
  | **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
84
271
  | **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
85
- | **Anti-slop** | No loud green/red trend pills on every tile; no \`bg-card\` flat grids when platform chrome exists; avoid recycling demo names ("Operations", mock workforce lists). |
272
+ | **Anti-slop** | Follow the **anti-slop checklist** below. No loud green/red trend pills on every tile; no \`bg-card\` flat grids when platform chrome exists; avoid recycling demo names ("Operations", mock workforce lists). |
273
+
274
+ ### Anti-slop checklist (required \u2014 output is linted against this)
275
+
276
+ Generated UIs are checked by \`lintGeneratedUi\` and rejected on any error. Self-review against these before returning code (icon budget: ${SLOP_BUDGETS.maxIconsPerView} per view; at most ${SLOP_BUDGETS.maxRowDividers} ruled rows before it reads as a ledger):
277
+
278
+ ${ANTI_SLOP_CHECKLIST}
279
+
280
+ The cause of slop is dropping **below** the curated block layer into raw primitives + free Tailwind. Stay on the blocks; reach for primitives only when no block fits, and even then keep colors on semantic tokens.
86
281
 
87
282
  ### Accessibility (required)
88
283
 
@@ -108,15 +303,17 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
108
303
  | \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
109
304
  | \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, children. |
110
305
  | \`Section\` | Titled block inside a page. |
111
- | \`SubNav\` | In-page tabs: \`items\`, \`activeId\`, \`onChange\`. |
306
+ | \`SubNav\` | **Section switcher** (Overview / Reports pill bar): \`items\`, \`activeId\`, \`onChange\`. Never use Radix/shadcn \`Tabs\` \u2014 it is not in this package. Switch panels with state or the router. |
307
+ | **Menus** | **Select** = short list, no search. **Combobox** = searchable (same trigger as Select). **Command** only inside \`PopoverContent variant="list"\` or Combobox \u2014 never padded default Popover. See \`examples/app-kit/src/recipes/primitives-catalog.ts\`. |
112
308
  | \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
113
309
  | \`Button\` | Actions \u2014 \`variant="secondary"\` for catalog/secondary CTAs; \`variant="default"\` for primary. |
114
310
  | \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
115
- | \`StatusBadge\` | Status pill: \`tone\` (\`success\`, \`warn\`, \u2026), children. |
116
- | \`FilterBar\` | Horizontal filter row (wraps \`SearchInput\`, buttons, etc.). |
311
+ | \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
312
+ | \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
313
+ | \`FilterField\` | Optional label wrapper for a filter control inside \`FilterBar\` (severity, status, \u2026). Omit \`label\` for search-only fields. |
117
314
  | \`SearchInput\` | Filter field with consistent app styling. |
118
- | \`DataTable\` | Sortable table: \`columns\`, \`rows\`, \`getRowKey\`, optional \`sort\` / \`onSortChange\`, \`emptyTitle\`, \`showRowCount\`, \`caption\` for screen readers. |
119
- | \`ChartPanel\` | Same shell as \`MetricChartCard\`: title row (\`px-4 pt-4\`), flush plot (\`pt-2\` only). Pass \`title\` + \`artifact\` (omit \`artifact.title\` to avoid duplicates) or \`children\`. |
315
+ | \`DataTable\` | Sortable table: \`columns\`, \`rows\`, \`getRowKey\`, optional \`sort\` / \`onSortChange\`, \`emptyTitle\`, \`showRowCount\`, \`caption\`. **Scales:** \`pageSize\` (built-in client pager), \`selectable\` + \`onSelectionChange\` (checkbox column for bulk actions), \`loading\` (skeleton rows). \`onRowClick\` for row \u2192 detail (open a \`Sheet\`). |
316
+ | \`ChartPanel\` | Same shell as \`MetricChartCard\`: title row (\`px-4 pt-4\`), flush plot (\`pt-2\` only). Pass \`title\` + \`artifact\` (omit \`artifact.title\` to avoid duplicates) or \`children\`. \`loading\` renders a plot-height skeleton. |
120
317
  | \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. |
121
318
  | \`FormSection\` | Grouped form block. |
122
319
  | \`AppConfirmDialog\` | Confirm/cancel modal: \`open\`, \`onOpenChange\`, \`title\`, \`description\`, \`onConfirm\`. |
@@ -125,13 +322,18 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
125
322
 
126
323
  #### Charts & metrics
127
324
 
325
+ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipContent\` chrome (see \`src/ui/chart.tsx\`). Series colors default to \`--chart-1..6\`; override those CSS tokens to rebrand every chart.
326
+
128
327
  | Component | Use for |
129
328
  |-----------|---------|
130
- | \`LineAreaChart\` | Chart engine. Props: \`data\`, \`xKey\`, \`series\`, \`variant\` (\`"area"\`), \`layout\` (\`"flush"\`), \`height\`, \`ariaLabel\`, \`formatX\`, \`formatValue\`. |
329
+ | \`LineAreaChart\` | Cartesian engine (shadcn-style chrome). Props: \`data\`, \`xKey\`, \`series: [{ dataKey, label?, color? }]\`, \`variant\` (\`area\`\\|\`line\`\\|\`bar\`), \`orientation\` (\`horizontal\` for horizontal bars), \`stacked\`, \`curve\` (\`monotone\`\\|\`linear\`\\|\`step\`), \`dots\`, \`gridLines\`, \`tooltipIndicator\` (\`dot\`\\|\`line\`\\|\`dashed\`), \`layout\` (\`flush\`), \`height\`, \`showLegend\`, \`formatX\`, \`formatValue\`, \`ariaLabel\`. |
330
+ | \`PieChart\` | Pie / donut: \`data\`, \`nameKey\`, \`dataKey\`, \`innerRadius\` (>0 = donut), \`centerValue\`/\`centerLabel\` (donut hole KPI), \`showLabels\`, \`colors\`. |
331
+ | \`RadialChart\` | Concentric progress rings: \`data\`, \`nameKey\`, \`dataKey\`, \`maxValue\`, \`centerValue\`/\`centerLabel\`. Good for gauges / share-of-target. |
332
+ | \`RadarChart\` | Spider chart (\u22653 axes): \`data\`, \`nameKey\`, \`series\`, \`maxValue\`. Compare a few metrics across entities. |
131
333
  | \`Sparkline\` | Tiny inline trend (table cells): \`data\`, \`color\`, \`area\`. |
132
334
  | \`MetricTile\` | Low-level KPI cell \u2014 prefer \`MetricRow\` / \`MetricChartCard\` instead of hand-wiring tiles. |
133
- | \`MetricRow\` | KPI strip in one elevated card (no chart). Props: \`metrics: [{ id, label, value, unit?, trend? }]\`, optional \`onMetricChange\`, \`metricsAriaLabel\`. |
134
- | \`MetricChartCard\` | KPI strip + flush chart; tile click swaps series. Same metrics shape + \`data\` per metric. Default chart height 300. |
335
+ | \`MetricRow\` | KPI strip in one elevated card (no chart). Props: \`metrics: [{ id, label, value, unit?, trend?, trendTone? }]\`, optional \`onMetricChange\`, \`metricsAriaLabel\`, \`loading\` (skeleton tiles). |
336
+ | \`MetricChartCard\` | KPI strip + flush chart; tile click swaps series. Same metrics shape + \`data\` per metric. Default chart height 300. \`loading\` renders skeleton tiles + chart. |
135
337
 
136
338
  #### Settings
137
339
 
@@ -164,19 +366,37 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
164
366
 
165
367
  Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
166
368
 
167
- ### Recipe index (\`examples/app-kit/recipes/\`)
369
+ ### Block recipes \u2014 compose these (don't clone wholesale)
370
+
371
+ Ready-made **section patterns** assembled from the components above. Each is a composition to rebuild in your own domain with your data \u2014 **not** an importable component. Reach for a block before dropping to raw primitives.
168
372
 
169
- | Recipe file | Components to study |
170
- |-------------|---------------------|
171
- | \`metrics-row.tsx\` | \`Page\`, \`MetricRow\` |
172
- | \`analytics-card.tsx\` | \`MetricChartCard\`, \`Button\` |
173
- | \`integrations-grid.tsx\` | \`IntegrationCard\`, \`ConnectionRowList\`, \`PlanBadge\` |
174
- | \`table-with-filters.tsx\` | \`FilterBar\`, \`DataTable\` |
175
- | \`settings-page.tsx\` | \`SettingsSection\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
176
- | \`resource-gallery.tsx\` | \`ResourceCard\`, \`StatusDot\`, \`Sparkline\` |
177
- | \`charts-panel.tsx\` | \`ChartPanel\`, \`ChartArtifact\` |
178
- | \`copilot-overlay.tsx\` | \`AppShell\`, \`AppChatPanel\` |
179
- | \`theme-presets.tsx\` | \`ThemePresetGallery\`, \`applyTimbalTheme\` |
373
+ **Settings**
374
+ - **Project settings** \u2014 General / Usage / Danger sections; the floating save bar appears on first edit. Compose \`SettingsSection\` + \`FieldInput\`/\`FieldSwitch\` + \`FieldRow\` + \`InfoCard\` + \`DangerZone\` + \`FloatingUnsavedChangesBar\`.
375
+ - **Settings form** \u2014 compact stacked form for one concern (profile, billing). Compose \`FormSection\` + \`FieldInput\`/\`FieldSelect\`/\`FieldTextarea\`.
376
+
377
+ **Data & metrics**
378
+ - **Metrics row** \u2014 KPI strip in one elevated card. Compose \`MetricRow\` + \`MetricTile\`.
379
+ - **Analytics card** \u2014 selectable KPI tiles driving a shared chart. Compose \`MetricChartCard\` + \`LineAreaChart\`.
380
+ - **Charts panel** \u2014 embedded chart artifact. Compose \`ChartPanel\` + \`ChartArtifactView\`.
381
+ - **Chart catalog** \u2014 every chart kind (stacked area, multi-line, step, bar, stacked + horizontal bar, donut, radial, radar) in \`ChartPanel\` cards. Pick a \`chartType\` + options on a \`ChartArtifact\`; theme via \`--chart-N\`.
382
+ - **Table + filters** \u2014 \`FilterBar\` above a sortable \`DataTable\` (+ \`StatusBadge\` in cells).
383
+
384
+ **Collections**
385
+ - **Integrations grid** \u2014 connector catalog + connected list. Compose \`IntegrationCard\` + \`PlanBadge\` + \`ConnectionRowList\` (\`IntegrationsEmptyState\` when empty).
386
+ - **Resource gallery** \u2014 project / agent / dataset cards. Compose \`ResourceCard\` + \`StatusDot\` + \`Sparkline\`.
387
+
388
+ **Overlays & flows** (animate automatically)
389
+ - **Confirm & destructive** \u2014 confirm/cancel modal or destructive alert. Compose \`AppConfirmDialog\` (or \`AlertDialog\`) + \`Button\`; never hand-roll a \`Dialog\` for confirms.
390
+ - **Detail sheet** \u2014 slide-over edit panel without leaving the list. Compose \`Sheet\` + \`Field*\` + \`Button\` + \`Separator\`.
391
+
392
+ **States & auth**
393
+ - **Empty states** \u2014 no-data / no-results / first-run. Compose \`EmptyState\` + \`Card\` + \`Button\`.
394
+ - **Sign-in card** \u2014 centered auth entry. Compose \`Card\` + \`Input\` + \`Label\` + \`Button\`.
395
+
396
+ **Shells & theming**
397
+ - **Minimal shell** \u2014 \`AppShell\` + \`Page\` (no sidebar/chat).
398
+ - **Copilot overlay** \u2014 \`AppShell\` + floating \`AppChatPanel\`.
399
+ - **Theme presets** \u2014 \`ThemePresetGallery\` + \`applyTimbalTheme\` (never hand-author OKLCH).
180
400
 
181
401
  ### Typical compositions
182
402
 
@@ -187,6 +407,7 @@ Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/
187
407
  - **Integrations** \u2014 grid of \`IntegrationCard\`; \`ConnectionRowList\` for connected providers; \`IntegrationsEmptyState\` when empty.
188
408
  - **Resource gallery** \u2014 grid of \`ResourceCard\`.
189
409
  - **Copilot-assisted app** \u2014 \`AppCopilotProvider\` + \`AppShell\` with \`chat={<AppChatPanel workforceId="\u2026" />}\`.
410
+ - **Motion is automatic** \u2014 Dialog, AlertDialog, Sheet, Popover, DropdownMenu, Select, Tooltip, Toast, and Accordion/Collapsible animate out of the box (fade/zoom/slide/height) via the engine inlined in \`styles.css\`. Do not add a separate animation library or hand-write \`@keyframes\`.
190
411
 
191
412
  ### Example imports
192
413
 
@@ -205,6 +426,7 @@ import {
205
426
  Button,
206
427
  DataTable,
207
428
  FilterBar,
429
+ FilterField,
208
430
  } from "@timbal-ai/timbal-react/app";
209
431
  \`\`\`
210
432
 
@@ -222,6 +444,211 @@ import {
222
444
  - For rich in-chat widgets, use **artifacts** (\`ARTIFACT_AGENT_INSTRUCTIONS\`) \u2014 app kit is for the **host application shell**.
223
445
  `.trim();
224
446
 
447
+ // src/design/ui-lint.ts
448
+ var PALETTE_GROUP = TAILWIND_PALETTE_COLORS.join("|");
449
+ var PREFIX_GROUP = COLOR_UTILITY_PREFIXES.join("|");
450
+ var RAW_COLOR_RE = new RegExp(
451
+ `(?:^|[\\s"'\`:])(?:[a-z-]+:)*(?:${PREFIX_GROUP})-(?:${PALETTE_GROUP})-\\d{2,3}(?:/\\d{1,3})?`,
452
+ "g"
453
+ );
454
+ var COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|\b(?:oklch|rgba?|hsla?)\s*\(/g;
455
+ var INLINE_STYLE_COLOR_RE = /style=\{\{[^}]*\b(?:color|background|backgroundColor|borderColor|fill|stroke)\b/;
456
+ var BOLD_VALUE_RE = /text-(?:xl|2xl|3xl|4xl|5xl|6xl)[^"'`]*\bfont-(?:bold|extrabold|black|semibold)|font-(?:bold|extrabold|black|semibold)[^"'`]*text-(?:xl|2xl|3xl|4xl|5xl|6xl)/;
457
+ var GRADIENT_RE = /\bbg-(?:gradient|linear|radial|conic)-/;
458
+ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
459
+ "t",
460
+ "tr",
461
+ "r",
462
+ "br",
463
+ "b",
464
+ "bl",
465
+ "l",
466
+ "tl"
467
+ ]);
468
+ var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
469
+ var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
470
+ var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
471
+ function stripVariants(util) {
472
+ return util.replace(/^(?:[a-z-]+:)*/, "");
473
+ }
474
+ function isCommentOrImport(line) {
475
+ const t = line.trim();
476
+ return t.startsWith("//") || t.startsWith("*") || t.startsWith("/*") || t.startsWith("import ") || t.startsWith("export ");
477
+ }
478
+ function lintGeneratedUi(source, options = {}) {
479
+ const maxIcons = options.maxIconsPerView ?? SLOP_BUDGETS.maxIconsPerView;
480
+ const maxRowDividers = options.maxRowDividers ?? SLOP_BUDGETS.maxRowDividers;
481
+ const findings = [];
482
+ const lines = source.split("\n");
483
+ let usesLucide = false;
484
+ let iconUsageCount = 0;
485
+ let dividerRunCount = 0;
486
+ const lucideNames = /* @__PURE__ */ new Set();
487
+ for (let i = 0; i < lines.length; i++) {
488
+ const line = lines[i];
489
+ const lineNo = i + 1;
490
+ if (ICON_IMPORT_RE.test(line)) {
491
+ usesLucide = true;
492
+ const named = line.match(/\{([^}]*)\}/);
493
+ if (named) {
494
+ for (const raw of named[1].split(",")) {
495
+ const name = raw.trim().split(/\s+as\s+/)[0].trim();
496
+ if (name) lucideNames.add(name);
497
+ }
498
+ }
499
+ continue;
500
+ }
501
+ if (isCommentOrImport(line)) continue;
502
+ const rawColors = line.match(RAW_COLOR_RE);
503
+ if (rawColors) {
504
+ for (const m of rawColors) {
505
+ findings.push({
506
+ rule: "raw-color",
507
+ severity: "error",
508
+ line: lineNo,
509
+ message: "Hardcoded palette color. Use a semantic token (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) so dark mode and rebranding work.",
510
+ snippet: m.trim().replace(/^["'`:\s]+/, "")
511
+ });
512
+ }
513
+ }
514
+ const literals = line.match(COLOR_LITERAL_RE);
515
+ if (literals) {
516
+ findings.push({
517
+ rule: "color-literal",
518
+ severity: "error",
519
+ line: lineNo,
520
+ message: "Hardcoded color literal. Colors must come from the theme generator (createTimbalTheme) and semantic tokens \u2014 never inline hex/oklch/rgb.",
521
+ snippet: line.trim().slice(0, 120)
522
+ });
523
+ }
524
+ if (INLINE_STYLE_COLOR_RE.test(line)) {
525
+ findings.push({
526
+ rule: "inline-style-color",
527
+ severity: "error",
528
+ line: lineNo,
529
+ message: "Inline style color. Move color to a semantic Tailwind token on className.",
530
+ snippet: line.trim().slice(0, 120)
531
+ });
532
+ }
533
+ if (RAW_CONTROL_SURFACE_RE.test(line)) {
534
+ findings.push({
535
+ rule: "raw-control-surface",
536
+ severity: "warn",
537
+ line: lineNo,
538
+ message: "Hand-rolled control surface (border-input). Use a kit control \u2014 SearchInput, Select, DropdownMenu, FieldInput, FieldSelect \u2014 so it matches every other control.",
539
+ snippet: line.trim().slice(0, 120)
540
+ });
541
+ }
542
+ if (BOLD_VALUE_RE.test(line)) {
543
+ findings.push({
544
+ rule: "bold-metric",
545
+ severity: "warn",
546
+ line: lineNo,
547
+ message: "Bold large value. House style: metric values use font-normal, not bold \u2014 bold giant numbers read as a template.",
548
+ snippet: line.trim().slice(0, 120)
549
+ });
550
+ }
551
+ if (GRADIENT_RE.test(line)) {
552
+ const fromTo = line.match(
553
+ new RegExp(`(?:from|via|to)-([a-z-]+)`, "g")
554
+ );
555
+ const colorStops = (fromTo ?? []).map((u) => stripVariants(u).replace(/^(?:from|via|to)-/, "")).filter((token) => !GRADIENT_DIRECTIONS.has(token));
556
+ const allReserved = colorStops.length > 0 && colorStops.every((token) => RESERVED_GRADIENT_SET.has(token));
557
+ if (!allReserved) {
558
+ findings.push({
559
+ rule: "data-gradient",
560
+ severity: "warn",
561
+ line: lineNo,
562
+ message: "Gradient outside chrome. Gradients are reserved for buttons / elevated / modal / playground \u2014 never a data card, tile, or table.",
563
+ snippet: line.trim().slice(0, 120)
564
+ });
565
+ }
566
+ }
567
+ if (/\b(?:border-t|border-b|divide-y)\b/.test(line)) {
568
+ dividerRunCount++;
569
+ if (dividerRunCount === maxRowDividers + 1) {
570
+ findings.push({
571
+ rule: "row-divider",
572
+ severity: "warn",
573
+ line: lineNo,
574
+ message: "Divider on every row. Prefer spacing (gap-*) or zebra striping over a rule under each list item.",
575
+ snippet: line.trim().slice(0, 120)
576
+ });
577
+ }
578
+ } else if (line.trim() !== "" && !line.includes("className")) {
579
+ if (!/^\s*[)>}/]/.test(line)) dividerRunCount = 0;
580
+ }
581
+ if (usesLucide && lucideNames.size > 0) {
582
+ for (const name of lucideNames) {
583
+ const usage = new RegExp(`<${name}\\b`, "g");
584
+ const hits = line.match(usage);
585
+ if (hits) iconUsageCount += hits.length;
586
+ }
587
+ }
588
+ }
589
+ if (usesLucide && iconUsageCount > maxIcons) {
590
+ findings.push({
591
+ rule: "icon-spam",
592
+ severity: "warn",
593
+ line: 1,
594
+ message: `Too many icons (${iconUsageCount} > ${maxIcons}). Icons should mark actions/nav/status \u2014 not decorate every label, tile, and card.`,
595
+ snippet: `${iconUsageCount} lucide-react icon usages`
596
+ });
597
+ }
598
+ const effectiveErrors = findings.filter(
599
+ (f) => f.severity === "error" || options.strict && f.severity === "warn"
600
+ ).length;
601
+ return {
602
+ findings,
603
+ errorCount: findings.filter((f) => f.severity === "error").length,
604
+ warnCount: findings.filter((f) => f.severity === "warn").length,
605
+ ok: effectiveErrors === 0
606
+ };
607
+ }
608
+ function formatLintReport(findings) {
609
+ if (findings.length === 0) return "";
610
+ const lines = findings.slice().sort((a, b) => a.line - b.line).map((f) => {
611
+ const tag = f.severity === "error" ? "ERROR" : "warn ";
612
+ return ` ${tag} L${f.line} [${f.rule}] ${f.message}
613
+ \u2192 ${f.snippet}`;
614
+ });
615
+ const errs = findings.filter((f) => f.severity === "error").length;
616
+ const warns = findings.filter((f) => f.severity === "warn").length;
617
+ return `Anti-slop review: ${errs} error(s), ${warns} warning(s)
618
+ ${lines.join("\n")}`;
619
+ }
620
+
621
+ // src/design/ui-review.ts
622
+ function reviewGeneratedUi(source, options = {}) {
623
+ const lint = lintGeneratedUi(source, options);
624
+ const report = formatLintReport(lint.findings);
625
+ if (lint.ok) {
626
+ return { lint, passed: true, report, revisionPrompt: null };
627
+ }
628
+ const revisionPrompt = [
629
+ "The generated UI failed the Timbal anti-slop review. Fix every issue below, then return the corrected code only.",
630
+ "",
631
+ report,
632
+ "",
633
+ "Rules: colors come only from semantic tokens (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) \u2014 never palette colors, hex, or oklch. Icons mark actions/nav/status, not decoration. Metric values use font-normal. No gradients on data surfaces. No divider under every row. Do not change anything that already passed."
634
+ ].join("\n");
635
+ return { lint, passed: false, report, revisionPrompt };
636
+ }
637
+ var UI_REVIEW_AGENT_INSTRUCTIONS = `
638
+ ## Self-review before returning UI (anti-slop)
639
+
640
+ Before you output any generated UI code, silently re-read it and fix anything that matches the slop checklist \u2014 this is the same rubric an automated linter applies, so output that fails it will be rejected and sent back:
641
+
642
+ - **No hardcoded colors.** Every color is a semantic token (\`text-primary\`, \`bg-muted\`, \`border-border\`, \`text-muted-foreground\`, \`bg-destructive\`, \u2026). No \`text-blue-600\`, no \`#hex\`, no \`oklch(...)\`, no \`style={{ color }}\`.
643
+ - **No decorative icons.** An icon must mark an action, nav target, or status. Remove icons that sit beside a label that already says the thing. Aim for very few icons per view.
644
+ - **Muted, sparse trends.** No colored up/down pill on every metric. Show a trend only when the change is the point.
645
+ - **Normal-weight values.** Metric numbers use \`font-normal\`, never \`font-bold\` at large sizes.
646
+ - **No card-in-card, no per-row dividers, no gradients on data surfaces.** Group with spacing/Sections; reserve gradients for chrome.
647
+ - **Compose from blocks.** Prefer \`MetricRow\` / \`MetricChartCard\` / \`DataTable\` / \`IntegrationCard\` over hand-assembled primitives.
648
+
649
+ If a check fails, fix it and re-read once more. Only return code that would pass clean.
650
+ `.trim();
651
+
225
652
  // src/design/oklch.ts
226
653
  var clamp = (n, min, max) => Math.min(max, Math.max(min, n));
227
654
  var round = (n, digits) => {
@@ -1109,22 +1536,14 @@ var appStatTileClass = cn(
1109
1536
  );
1110
1537
  var appStatValueClass = "text-2xl font-normal tracking-tight text-foreground tabular-nums";
1111
1538
  var appStatLabelClass = "text-xs font-normal text-muted-foreground";
1112
- var appFilterBarClass = cn(
1113
- "flex flex-wrap items-center gap-2",
1114
- studioTopbarPillHeightClass
1115
- );
1116
- var appSearchInputClass = cn(studioSearchChromeClass, "text-sm");
1539
+ var appFilterBarClass = "flex flex-wrap items-end gap-2";
1540
+ var appSearchInputClass = controlClass({}, "inline-flex items-center gap-2");
1117
1541
  var appBreadcrumbsClass = "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground";
1118
1542
  var appBreadcrumbLinkClass = "transition-colors hover:text-foreground";
1119
1543
  var appFieldClass = "flex flex-col gap-1.5";
1120
1544
  var appFieldLabelClass = "text-sm font-medium text-foreground";
1121
1545
  var appFieldHintClass = "text-xs text-muted-foreground";
1122
- var appInputClass = cn(
1123
- studioSecondaryChromeClass,
1124
- "h-10 w-full rounded-lg px-3 text-sm text-foreground outline-none",
1125
- "placeholder:text-muted-foreground/70",
1126
- "focus-visible:ring-2 focus-visible:ring-foreground/10"
1127
- );
1546
+ var appInputClass = controlClass({}, "w-full");
1128
1547
  var appEmptyStateClass = cn(
1129
1548
  appSurfaceCardClass,
1130
1549
  "flex flex-col items-center justify-center gap-2 py-12 text-center"
@@ -1141,9 +1560,23 @@ function useAppShellChat() {
1141
1560
  return useContext(AppShellChatContext);
1142
1561
  }
1143
1562
 
1563
+ // src/app/layout/app-shell-nav-context.tsx
1564
+ import { createContext as createContext2, useContext as useContext2 } from "react";
1565
+ var AppShellNavContext = createContext2(null);
1566
+ var AppShellNavProvider = AppShellNavContext.Provider;
1567
+ function useAppShellNav() {
1568
+ return useContext2(AppShellNavContext) ?? {
1569
+ open: false,
1570
+ setOpen: () => {
1571
+ },
1572
+ toggle: () => {
1573
+ }
1574
+ };
1575
+ }
1576
+
1144
1577
  // src/app/layout/AppShell.tsx
1145
1578
  import { motion, useReducedMotion } from "motion/react";
1146
- import { useCallback, useState } from "react";
1579
+ import { useCallback, useMemo, useState } from "react";
1147
1580
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1148
1581
  var floatingTriggerClass = cn(
1149
1582
  "aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
@@ -1210,11 +1643,29 @@ var AppShell = ({
1210
1643
  chatCollapsible = true,
1211
1644
  chatTriggerLabel = "Assistant",
1212
1645
  hideChatTrigger = false,
1646
+ navOpen: navOpenProp,
1647
+ defaultNavOpen = false,
1648
+ onNavOpenChange,
1213
1649
  className,
1214
1650
  mainClassName
1215
1651
  }) => {
1216
1652
  const topbarContent = topbar ?? header;
1217
1653
  const hasChat = Boolean(chat);
1654
+ const [uncontrolledNavOpen, setUncontrolledNavOpen] = useState(defaultNavOpen);
1655
+ const isNavControlled = navOpenProp !== void 0;
1656
+ const navOpen = isNavControlled ? navOpenProp : uncontrolledNavOpen;
1657
+ const setNavOpen = useCallback(
1658
+ (open) => {
1659
+ if (!isNavControlled) setUncontrolledNavOpen(open);
1660
+ onNavOpenChange?.(open);
1661
+ },
1662
+ [isNavControlled, onNavOpenChange]
1663
+ );
1664
+ const toggleNav = useCallback(() => setNavOpen(!navOpen), [navOpen, setNavOpen]);
1665
+ const navControls = useMemo(
1666
+ () => ({ open: navOpen, setOpen: setNavOpen, toggle: toggleNav }),
1667
+ [navOpen, setNavOpen, toggleNav]
1668
+ );
1218
1669
  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultChatOpen);
1219
1670
  const isChatControlled = chatOpenProp !== void 0;
1220
1671
  const chatOpen = isChatControlled ? chatOpenProp : uncontrolledOpen;
@@ -1258,6 +1709,15 @@ var AppShell = ({
1258
1709
  style: studioChromeShellStyle,
1259
1710
  children: [
1260
1711
  sidebar,
1712
+ sidebar && navOpen ? /* @__PURE__ */ jsx5(
1713
+ "button",
1714
+ {
1715
+ type: "button",
1716
+ "aria-label": "Close navigation",
1717
+ onClick: () => setNavOpen(false),
1718
+ className: "fixed inset-0 z-40 bg-foreground/30 backdrop-blur-[2px] md:hidden"
1719
+ }
1720
+ ) : null,
1261
1721
  shellBody,
1262
1722
  hasChat && chatOpen ? /* @__PURE__ */ jsx5(
1263
1723
  "div",
@@ -1285,8 +1745,9 @@ var AppShell = ({
1285
1745
  ]
1286
1746
  }
1287
1747
  ) });
1748
+ const withNav = /* @__PURE__ */ jsx5(AppShellNavProvider, { value: navControls, children: tree });
1288
1749
  if (!hasChat) {
1289
- return tree;
1750
+ return withNav;
1290
1751
  }
1291
1752
  return /* @__PURE__ */ jsx5(
1292
1753
  AppShellChatProvider,
@@ -1297,7 +1758,7 @@ var AppShell = ({
1297
1758
  toggle: toggleChat,
1298
1759
  collapsible: chatCollapsible
1299
1760
  },
1300
- children: tree
1761
+ children: withNav
1301
1762
  }
1302
1763
  );
1303
1764
  };
@@ -1348,8 +1809,33 @@ var AppShellChatTrigger = ({
1348
1809
  );
1349
1810
  };
1350
1811
 
1812
+ // src/app/layout/AppShellSidebarTrigger.tsx
1813
+ import { MenuIcon } from "lucide-react";
1814
+ import { jsx as jsx8 } from "react/jsx-runtime";
1815
+ var AppShellSidebarTrigger = ({
1816
+ label = "Open navigation",
1817
+ className
1818
+ }) => {
1819
+ const nav = useAppShellNav();
1820
+ return /* @__PURE__ */ jsx8(
1821
+ "button",
1822
+ {
1823
+ type: "button",
1824
+ onClick: nav.toggle,
1825
+ "aria-label": label,
1826
+ "aria-expanded": nav.open,
1827
+ className: cn(
1828
+ "inline-flex size-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-foreground/[0.04] hover:text-foreground md:hidden",
1829
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10",
1830
+ className
1831
+ ),
1832
+ children: /* @__PURE__ */ jsx8(MenuIcon, { className: "size-5", "aria-hidden": true })
1833
+ }
1834
+ );
1835
+ };
1836
+
1351
1837
  // src/app/layout/PageHeader.tsx
1352
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1838
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1353
1839
  var PageHeader = ({
1354
1840
  title,
1355
1841
  description,
@@ -1358,15 +1844,15 @@ var PageHeader = ({
1358
1844
  }) => {
1359
1845
  return /* @__PURE__ */ jsxs7("header", { className: cn("aui-app-page-header", appPageHeaderClass, className), children: [
1360
1846
  /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
1361
- /* @__PURE__ */ jsx8("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
1362
- description ? /* @__PURE__ */ jsx8("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
1847
+ /* @__PURE__ */ jsx9("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
1848
+ description ? /* @__PURE__ */ jsx9("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
1363
1849
  ] }),
1364
- actions ? /* @__PURE__ */ jsx8("div", { className: "aui-app-page-header-actions flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
1850
+ actions ? /* @__PURE__ */ jsx9("div", { className: "aui-app-page-header-actions flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
1365
1851
  ] });
1366
1852
  };
1367
1853
 
1368
1854
  // src/app/layout/Page.tsx
1369
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1855
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1370
1856
  var Page = ({
1371
1857
  children,
1372
1858
  breadcrumbs,
@@ -1375,13 +1861,13 @@ var Page = ({
1375
1861
  }) => {
1376
1862
  return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-page", appPageColumnClass, className), children: [
1377
1863
  breadcrumbs,
1378
- /* @__PURE__ */ jsx9(PageHeader, { ...headerProps }),
1864
+ /* @__PURE__ */ jsx10(PageHeader, { ...headerProps }),
1379
1865
  children
1380
1866
  ] });
1381
1867
  };
1382
1868
 
1383
1869
  // src/app/layout/Section.tsx
1384
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1870
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1385
1871
  var Section = ({
1386
1872
  title,
1387
1873
  description,
@@ -1389,29 +1875,29 @@ var Section = ({
1389
1875
  className
1390
1876
  }) => {
1391
1877
  return /* @__PURE__ */ jsxs9("section", { className: cn("aui-app-section", appSectionClass, className), children: [
1392
- title ? /* @__PURE__ */ jsx10("h2", { className: appSectionTitleClass, children: title }) : null,
1393
- description ? /* @__PURE__ */ jsx10("p", { className: appSectionDescriptionClass, children: description }) : null,
1878
+ title ? /* @__PURE__ */ jsx11("h2", { className: appSectionTitleClass, children: title }) : null,
1879
+ description ? /* @__PURE__ */ jsx11("p", { className: appSectionDescriptionClass, children: description }) : null,
1394
1880
  children
1395
1881
  ] });
1396
1882
  };
1397
1883
 
1398
1884
  // src/app/copilot/app-copilot-context.tsx
1399
- import { createContext as createContext2, useContext as useContext2 } from "react";
1400
- import { jsx as jsx11 } from "react/jsx-runtime";
1401
- var AppCopilotContext = createContext2(null);
1885
+ import { createContext as createContext3, useContext as useContext3 } from "react";
1886
+ import { jsx as jsx12 } from "react/jsx-runtime";
1887
+ var AppCopilotContext = createContext3(null);
1402
1888
  var AppCopilotProvider = ({
1403
1889
  value,
1404
1890
  children
1405
1891
  }) => {
1406
- return /* @__PURE__ */ jsx11(AppCopilotContext.Provider, { value, children });
1892
+ return /* @__PURE__ */ jsx12(AppCopilotContext.Provider, { value, children });
1407
1893
  };
1408
1894
  function useAppCopilotContext() {
1409
- return useContext2(AppCopilotContext) ?? {};
1895
+ return useContext3(AppCopilotContext) ?? {};
1410
1896
  }
1411
1897
 
1412
1898
  // src/app/chat/AppChatPanel.tsx
1413
1899
  import { XIcon } from "lucide-react";
1414
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1900
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1415
1901
  var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
1416
1902
  var chromeClass = cn(
1417
1903
  "aui-app-chat-panel-chrome relative z-20 flex min-h-10 shrink-0 items-center justify-end px-2 pt-2"
@@ -1457,17 +1943,17 @@ var AppChatPanel = ({
1457
1943
  }) => {
1458
1944
  const shellChat = useAppShellChat();
1459
1945
  return /* @__PURE__ */ jsxs10("div", { className: cn(shellClass, className), children: [
1460
- shellChat?.collapsible ? /* @__PURE__ */ jsx12("div", { className: chromeClass, children: /* @__PURE__ */ jsx12(
1946
+ shellChat?.collapsible ? /* @__PURE__ */ jsx13("div", { className: chromeClass, children: /* @__PURE__ */ jsx13(
1461
1947
  "button",
1462
1948
  {
1463
1949
  type: "button",
1464
1950
  className: closeButtonClass,
1465
1951
  onClick: () => shellChat.setOpen(false),
1466
1952
  "aria-label": "Close assistant",
1467
- children: /* @__PURE__ */ jsx12(XIcon, { className: "size-4", "aria-hidden": true })
1953
+ children: /* @__PURE__ */ jsx13(XIcon, { className: "size-4", "aria-hidden": true })
1468
1954
  }
1469
1955
  ) }) : null,
1470
- /* @__PURE__ */ jsx12("div", { className: bodyClass, children: /* @__PURE__ */ jsx12(
1956
+ /* @__PURE__ */ jsx13("div", { className: bodyClass, children: /* @__PURE__ */ jsx13(
1471
1957
  TimbalRuntimeProvider,
1472
1958
  {
1473
1959
  workforceId,
@@ -1477,7 +1963,7 @@ var AppChatPanel = ({
1477
1963
  attachmentsUploadUrl,
1478
1964
  attachmentsAccept,
1479
1965
  debug,
1480
- children: /* @__PURE__ */ jsx12(
1966
+ children: /* @__PURE__ */ jsx13(
1481
1967
  Thread,
1482
1968
  {
1483
1969
  variant: "panel",
@@ -1498,23 +1984,23 @@ var AppChatPanel = ({
1498
1984
  };
1499
1985
 
1500
1986
  // src/app/surfaces/SurfaceCard.tsx
1501
- import { jsx as jsx13 } from "react/jsx-runtime";
1987
+ import { jsx as jsx14 } from "react/jsx-runtime";
1502
1988
  var SurfaceCard = ({ children, className }) => {
1503
- return /* @__PURE__ */ jsx13("div", { className: cn("aui-app-surface-card", appSurfaceCardClass, className), children });
1989
+ return /* @__PURE__ */ jsx14("div", { className: cn("aui-app-surface-card", appSurfaceCardClass, className), children });
1504
1990
  };
1505
1991
 
1506
1992
  // src/app/surfaces/StatTile.tsx
1507
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1993
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
1508
1994
  var StatTile = ({ label, value, hint, className }) => {
1509
1995
  return /* @__PURE__ */ jsxs11("div", { className: cn("aui-app-stat-tile", appStatTileClass, className), children: [
1510
- /* @__PURE__ */ jsx14("span", { className: appStatLabelClass, children: label }),
1511
- /* @__PURE__ */ jsx14("span", { className: appStatValueClass, children: value }),
1512
- hint ? /* @__PURE__ */ jsx14("span", { className: "text-xs text-muted-foreground", children: hint }) : null
1996
+ /* @__PURE__ */ jsx15("span", { className: appStatLabelClass, children: label }),
1997
+ /* @__PURE__ */ jsx15("span", { className: appStatValueClass, children: value }),
1998
+ hint ? /* @__PURE__ */ jsx15("span", { className: "text-xs text-muted-foreground", children: hint }) : null
1513
1999
  ] });
1514
2000
  };
1515
2001
 
1516
2002
  // src/app/surfaces/EmptyState.tsx
1517
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
2003
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
1518
2004
  var EmptyState = ({
1519
2005
  title,
1520
2006
  description,
@@ -1522,19 +2008,20 @@ var EmptyState = ({
1522
2008
  className
1523
2009
  }) => {
1524
2010
  return /* @__PURE__ */ jsxs12("div", { className: cn("aui-app-empty-state", appEmptyStateClass, className), children: [
1525
- /* @__PURE__ */ jsx15("p", { className: appEmptyStateTitleClass, children: title }),
1526
- description ? /* @__PURE__ */ jsx15("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
2011
+ /* @__PURE__ */ jsx16("p", { className: appEmptyStateTitleClass, children: title }),
2012
+ description ? /* @__PURE__ */ jsx16("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
1527
2013
  action
1528
2014
  ] });
1529
2015
  };
1530
2016
 
1531
2017
  // src/app/surfaces/StatusBadge.tsx
1532
- import { jsx as jsx16 } from "react/jsx-runtime";
2018
+ import { jsx as jsx17 } from "react/jsx-runtime";
1533
2019
  var statusBadgeToneClass = {
1534
2020
  default: "bg-muted text-foreground",
1535
2021
  primary: "bg-primary/10 text-primary",
1536
2022
  success: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
1537
2023
  warn: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
2024
+ danger: "bg-destructive/10 text-destructive",
1538
2025
  muted: "bg-muted/80 text-muted-foreground"
1539
2026
  };
1540
2027
  var StatusBadge = ({
@@ -1542,7 +2029,7 @@ var StatusBadge = ({
1542
2029
  tone = "default",
1543
2030
  className
1544
2031
  }) => {
1545
- return /* @__PURE__ */ jsx16(
2032
+ return /* @__PURE__ */ jsx17(
1546
2033
  "span",
1547
2034
  {
1548
2035
  className: cn(
@@ -1556,7 +2043,7 @@ var StatusBadge = ({
1556
2043
  };
1557
2044
 
1558
2045
  // src/app/surfaces/AppConfirmDialog.tsx
1559
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
2046
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1560
2047
  var bodyClass2 = "flex flex-col gap-4 p-6";
1561
2048
  var titleClass = "pr-8";
1562
2049
  var actionsClass = "flex flex-wrap justify-end gap-2";
@@ -1571,15 +2058,15 @@ var AppConfirmDialog = ({
1571
2058
  destructive = false,
1572
2059
  className
1573
2060
  }) => {
1574
- return /* @__PURE__ */ jsx17(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx17(
2061
+ return /* @__PURE__ */ jsx18(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx18(
1575
2062
  DialogContent,
1576
2063
  {
1577
2064
  className: cn("gap-0 p-0 sm:max-w-md", className),
1578
2065
  children: /* @__PURE__ */ jsxs13("div", { className: bodyClass2, children: [
1579
- /* @__PURE__ */ jsx17(DialogTitle, { className: titleClass, children: title }),
1580
- description ? /* @__PURE__ */ jsx17("p", { className: "text-sm text-muted-foreground", children: description }) : null,
2066
+ /* @__PURE__ */ jsx18(DialogTitle, { className: titleClass, children: title }),
2067
+ description ? /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: description }) : null,
1581
2068
  /* @__PURE__ */ jsxs13("div", { className: actionsClass, children: [
1582
- /* @__PURE__ */ jsx17(
2069
+ /* @__PURE__ */ jsx18(
1583
2070
  TimbalV2Button,
1584
2071
  {
1585
2072
  type: "button",
@@ -1589,7 +2076,7 @@ var AppConfirmDialog = ({
1589
2076
  children: cancelLabel
1590
2077
  }
1591
2078
  ),
1592
- /* @__PURE__ */ jsx17(
2079
+ /* @__PURE__ */ jsx18(
1593
2080
  TimbalV2Button,
1594
2081
  {
1595
2082
  type: "button",
@@ -1609,7 +2096,7 @@ var AppConfirmDialog = ({
1609
2096
  };
1610
2097
 
1611
2098
  // src/app/surfaces/InfoCard.tsx
1612
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
2099
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1613
2100
  var toneClass = {
1614
2101
  neutral: "border-border bg-muted/40",
1615
2102
  info: "border-primary/25 bg-primary/5",
@@ -1633,18 +2120,18 @@ var InfoCard = ({
1633
2120
  className
1634
2121
  ),
1635
2122
  children: [
1636
- icon ? /* @__PURE__ */ jsx18("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
2123
+ icon ? /* @__PURE__ */ jsx19("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
1637
2124
  /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
1638
- title ? /* @__PURE__ */ jsx18("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
1639
- children ? /* @__PURE__ */ jsx18("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
2125
+ title ? /* @__PURE__ */ jsx19("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
2126
+ children ? /* @__PURE__ */ jsx19("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
1640
2127
  ] }),
1641
- action ? /* @__PURE__ */ jsx18("div", { className: "shrink-0", children: action }) : null
2128
+ action ? /* @__PURE__ */ jsx19("div", { className: "shrink-0", children: action }) : null
1642
2129
  ]
1643
2130
  }
1644
2131
  );
1645
2132
 
1646
2133
  // src/app/surfaces/StatusDot.tsx
1647
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
2134
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1648
2135
  var dotClass = {
1649
2136
  online: "bg-emerald-500",
1650
2137
  busy: "bg-amber-500",
@@ -1659,7 +2146,7 @@ var StatusDot = ({
1659
2146
  className
1660
2147
  }) => /* @__PURE__ */ jsxs15("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
1661
2148
  /* @__PURE__ */ jsxs15("span", { className: "relative flex size-2", children: [
1662
- pulse ? /* @__PURE__ */ jsx19(
2149
+ pulse ? /* @__PURE__ */ jsx20(
1663
2150
  "span",
1664
2151
  {
1665
2152
  className: cn(
@@ -1668,18 +2155,18 @@ var StatusDot = ({
1668
2155
  )
1669
2156
  }
1670
2157
  ) : null,
1671
- /* @__PURE__ */ jsx19("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
2158
+ /* @__PURE__ */ jsx20("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
1672
2159
  ] }),
1673
- label ? /* @__PURE__ */ jsx19("span", { className: "text-xs text-muted-foreground", children: label }) : null
2160
+ label ? /* @__PURE__ */ jsx20("span", { className: "text-xs text-muted-foreground", children: label }) : null
1674
2161
  ] });
1675
2162
 
1676
2163
  // src/app/surfaces/DescriptionList.tsx
1677
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
2164
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1678
2165
  var DescriptionList = ({
1679
2166
  items,
1680
2167
  stacked = false,
1681
2168
  className
1682
- }) => /* @__PURE__ */ jsx20(
2169
+ }) => /* @__PURE__ */ jsx21(
1683
2170
  "dl",
1684
2171
  {
1685
2172
  className: cn(
@@ -1694,8 +2181,8 @@ var DescriptionList = ({
1694
2181
  stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4"
1695
2182
  ),
1696
2183
  children: [
1697
- /* @__PURE__ */ jsx20("dt", { className: "text-sm text-muted-foreground", children: item.label }),
1698
- /* @__PURE__ */ jsx20(
2184
+ /* @__PURE__ */ jsx21("dt", { className: "text-sm text-muted-foreground", children: item.label }),
2185
+ /* @__PURE__ */ jsx21(
1699
2186
  "dd",
1700
2187
  {
1701
2188
  className: cn(
@@ -1715,8 +2202,8 @@ var DescriptionList = ({
1715
2202
  // src/app/surfaces/ExpandableSection.tsx
1716
2203
  import { useId, useState as useState2 } from "react";
1717
2204
  import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
1718
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
1719
- var Chevron = ({ open }) => /* @__PURE__ */ jsx21(
2205
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
2206
+ var Chevron = ({ open }) => /* @__PURE__ */ jsx22(
1720
2207
  "svg",
1721
2208
  {
1722
2209
  viewBox: "0 0 24 24",
@@ -1730,7 +2217,7 @@ var Chevron = ({ open }) => /* @__PURE__ */ jsx21(
1730
2217
  strokeLinecap: "round",
1731
2218
  strokeLinejoin: "round",
1732
2219
  "aria-hidden": true,
1733
- children: /* @__PURE__ */ jsx21("path", { d: "m6 9 6 6 6-6" })
2220
+ children: /* @__PURE__ */ jsx22("path", { d: "m6 9 6 6 6-6" })
1734
2221
  }
1735
2222
  );
1736
2223
  var ExpandableSection = ({
@@ -1762,15 +2249,15 @@ var ExpandableSection = ({
1762
2249
  className: "flex w-full items-center justify-between gap-3 bg-transparent px-4 py-3 text-left hover:bg-transparent active:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10",
1763
2250
  children: [
1764
2251
  /* @__PURE__ */ jsxs17("span", { className: "flex min-w-0 items-center gap-3", children: [
1765
- icon ? /* @__PURE__ */ jsx21("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
1766
- /* @__PURE__ */ jsx21("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
1767
- count != null ? /* @__PURE__ */ jsx21("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
2252
+ icon ? /* @__PURE__ */ jsx22("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
2253
+ /* @__PURE__ */ jsx22("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
2254
+ count != null ? /* @__PURE__ */ jsx22("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
1768
2255
  ] }),
1769
- /* @__PURE__ */ jsx21(Chevron, { open })
2256
+ /* @__PURE__ */ jsx22(Chevron, { open })
1770
2257
  ]
1771
2258
  }
1772
2259
  ),
1773
- /* @__PURE__ */ jsx21(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx21(
2260
+ /* @__PURE__ */ jsx22(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx22(
1774
2261
  motion2.div,
1775
2262
  {
1776
2263
  id: panelId,
@@ -1779,7 +2266,7 @@ var ExpandableSection = ({
1779
2266
  exit: reduceMotion ? void 0 : { height: 0, opacity: 0 },
1780
2267
  transition: { duration: 0.2, ease: "easeOut" },
1781
2268
  className: "overflow-hidden",
1782
- children: /* @__PURE__ */ jsx21("div", { className: "bg-muted/20", children })
2269
+ children: /* @__PURE__ */ jsx22("div", { className: "bg-muted/20", children })
1783
2270
  },
1784
2271
  "body"
1785
2272
  ) : null })
@@ -1787,7 +2274,7 @@ var ExpandableSection = ({
1787
2274
  };
1788
2275
 
1789
2276
  // src/app/surfaces/ResourceCard.tsx
1790
- import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
2277
+ import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
1791
2278
  var resourceCardShellClass = cn(
1792
2279
  "flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
1793
2280
  TIMBAL_V2_ELEVATED_SURFACE
@@ -1814,33 +2301,33 @@ var ResourceCard = ({
1814
2301
  }) => {
1815
2302
  const body = /* @__PURE__ */ jsxs18(Fragment3, { children: [
1816
2303
  /* @__PURE__ */ jsxs18("div", { className: "flex items-start gap-3", children: [
1817
- media ? /* @__PURE__ */ jsx22("span", { className: mediaShellClass, children: media }) : null,
2304
+ media ? /* @__PURE__ */ jsx23("span", { className: mediaShellClass, children: media }) : null,
1818
2305
  /* @__PURE__ */ jsxs18("div", { className: "min-w-0 flex-1 pt-0.5", children: [
1819
- /* @__PURE__ */ jsx22("p", { className: "truncate text-sm font-normal leading-snug text-foreground", children: title }),
1820
- subtitle ? /* @__PURE__ */ jsx22("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground", children: subtitle }) : null
2306
+ /* @__PURE__ */ jsx23("p", { className: "truncate text-sm font-normal leading-snug text-foreground", children: title }),
2307
+ subtitle ? /* @__PURE__ */ jsx23("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground", children: subtitle }) : null
1821
2308
  ] }),
1822
- badge ? /* @__PURE__ */ jsx22("span", { className: "shrink-0 pt-0.5", children: badge }) : null
2309
+ badge ? /* @__PURE__ */ jsx23("span", { className: "shrink-0 pt-0.5", children: badge }) : null
1823
2310
  ] }),
1824
2311
  footer || action ? /* @__PURE__ */ jsxs18("div", { className: "mt-auto flex items-center justify-between gap-3 border-t border-border/40 pt-3 text-xs font-normal text-muted-foreground", children: [
1825
- /* @__PURE__ */ jsx22("span", { className: "min-w-0 truncate", children: footer }),
1826
- action ? /* @__PURE__ */ jsx22("span", { className: "shrink-0 opacity-80", children: action }) : null
2312
+ /* @__PURE__ */ jsx23("span", { className: "min-w-0 truncate", children: footer }),
2313
+ action ? /* @__PURE__ */ jsx23("span", { className: "shrink-0 opacity-80", children: action }) : null
1827
2314
  ] }) : null
1828
2315
  ] });
1829
2316
  if (onClick) {
1830
- return /* @__PURE__ */ jsx22("button", { type: "button", onClick, "aria-label": ariaLabel, className: cn(resourceCardInteractiveClass, className), children: body });
2317
+ return /* @__PURE__ */ jsx23("button", { type: "button", onClick, "aria-label": ariaLabel, className: cn(resourceCardInteractiveClass, className), children: body });
1831
2318
  }
1832
- return /* @__PURE__ */ jsx22("article", { className: cn(resourceCardShellClass, className), children: body });
2319
+ return /* @__PURE__ */ jsx23("article", { className: cn(resourceCardShellClass, className), children: body });
1833
2320
  };
1834
2321
 
1835
2322
  // src/app/settings/SettingsSection.tsx
1836
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
2323
+ import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
1837
2324
  var SettingsSectionHeader = ({
1838
2325
  title,
1839
2326
  description,
1840
2327
  className
1841
2328
  }) => /* @__PURE__ */ jsxs19("div", { className: cn("flex flex-col", className), children: [
1842
- /* @__PURE__ */ jsx23("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
1843
- description ? /* @__PURE__ */ jsx23("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
2329
+ /* @__PURE__ */ jsx24("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
2330
+ description ? /* @__PURE__ */ jsx24("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
1844
2331
  ] });
1845
2332
  var SettingsSection = ({
1846
2333
  title,
@@ -1859,17 +2346,17 @@ var SettingsSection = ({
1859
2346
  ),
1860
2347
  children: [
1861
2348
  /* @__PURE__ */ jsxs19("div", { className: "min-w-0", children: [
1862
- /* @__PURE__ */ jsx23("h2", { className: "text-sm font-medium text-foreground", children: title }),
1863
- description ? /* @__PURE__ */ jsx23("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
1864
- descriptionFooter ? /* @__PURE__ */ jsx23("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
2349
+ /* @__PURE__ */ jsx24("h2", { className: "text-sm font-medium text-foreground", children: title }),
2350
+ description ? /* @__PURE__ */ jsx24("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
2351
+ descriptionFooter ? /* @__PURE__ */ jsx24("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
1865
2352
  ] }),
1866
- /* @__PURE__ */ jsx23("div", { className: "min-w-0 space-y-3", children })
2353
+ /* @__PURE__ */ jsx24("div", { className: "min-w-0 space-y-3", children })
1867
2354
  ]
1868
2355
  }
1869
2356
  );
1870
2357
 
1871
2358
  // src/app/settings/FieldRow.tsx
1872
- import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
2359
+ import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
1873
2360
  var FieldRow = ({
1874
2361
  label,
1875
2362
  children,
@@ -1888,7 +2375,7 @@ var FieldRow = ({
1888
2375
  ),
1889
2376
  children: [
1890
2377
  /* @__PURE__ */ jsxs20("div", { className: "min-w-0", children: [
1891
- /* @__PURE__ */ jsx24(
2378
+ /* @__PURE__ */ jsx25(
1892
2379
  "label",
1893
2380
  {
1894
2381
  htmlFor,
@@ -1896,17 +2383,17 @@ var FieldRow = ({
1896
2383
  children: label
1897
2384
  }
1898
2385
  ),
1899
- description ? /* @__PURE__ */ jsx24("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
2386
+ description ? /* @__PURE__ */ jsx25("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
1900
2387
  ] }),
1901
- /* @__PURE__ */ jsx24("div", { className: "shrink-0", children })
2388
+ /* @__PURE__ */ jsx25("div", { className: "shrink-0", children })
1902
2389
  ]
1903
2390
  }
1904
2391
  );
1905
2392
  }
1906
2393
  return /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col gap-1.5", className), children: [
1907
- /* @__PURE__ */ jsx24("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
2394
+ /* @__PURE__ */ jsx25("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
1908
2395
  children,
1909
- description ? /* @__PURE__ */ jsx24("p", { className: "text-xs text-muted-foreground", children: description }) : null
2396
+ description ? /* @__PURE__ */ jsx25("p", { className: "text-xs text-muted-foreground", children: description }) : null
1910
2397
  ] });
1911
2398
  };
1912
2399
 
@@ -1914,7 +2401,7 @@ var FieldRow = ({
1914
2401
  import { useEffect, useState as useState3 } from "react";
1915
2402
  import { createPortal } from "react-dom";
1916
2403
  import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
1917
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
2404
+ import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
1918
2405
  var FloatingUnsavedChangesBar = ({
1919
2406
  visible,
1920
2407
  message = "Unsaved changes",
@@ -1932,7 +2419,7 @@ var FloatingUnsavedChangesBar = ({
1932
2419
  useEffect(() => setMounted(true), []);
1933
2420
  if (!mounted || typeof document === "undefined") return null;
1934
2421
  return createPortal(
1935
- /* @__PURE__ */ jsx25(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs21(
2422
+ /* @__PURE__ */ jsx26(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx26("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs21(
1936
2423
  motion3.div,
1937
2424
  {
1938
2425
  role: "region",
@@ -1946,10 +2433,10 @@ var FloatingUnsavedChangesBar = ({
1946
2433
  className
1947
2434
  ),
1948
2435
  children: [
1949
- /* @__PURE__ */ jsx25("span", { className: "text-sm text-muted-foreground", children: message }),
2436
+ /* @__PURE__ */ jsx26("span", { className: "text-sm text-muted-foreground", children: message }),
1950
2437
  /* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-1.5", children: [
1951
- /* @__PURE__ */ jsx25(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
1952
- /* @__PURE__ */ jsx25(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
2438
+ /* @__PURE__ */ jsx26(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
2439
+ /* @__PURE__ */ jsx26(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
1953
2440
  ] })
1954
2441
  ]
1955
2442
  }
@@ -1959,7 +2446,7 @@ var FloatingUnsavedChangesBar = ({
1959
2446
  };
1960
2447
 
1961
2448
  // src/app/settings/DangerZone.tsx
1962
- import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
2449
+ import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
1963
2450
  var DangerZoneAction = ({
1964
2451
  title,
1965
2452
  description,
@@ -1974,10 +2461,10 @@ var DangerZoneAction = ({
1974
2461
  ),
1975
2462
  children: [
1976
2463
  /* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
1977
- /* @__PURE__ */ jsx26("p", { className: "text-sm font-medium text-foreground", children: title }),
1978
- description ? /* @__PURE__ */ jsx26("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2464
+ /* @__PURE__ */ jsx27("p", { className: "text-sm font-medium text-foreground", children: title }),
2465
+ description ? /* @__PURE__ */ jsx27("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
1979
2466
  ] }),
1980
- /* @__PURE__ */ jsx26("div", { className: "shrink-0", children: action })
2467
+ /* @__PURE__ */ jsx27("div", { className: "shrink-0", children: action })
1981
2468
  ]
1982
2469
  }
1983
2470
  );
@@ -1995,17 +2482,17 @@ var DangerZone = ({
1995
2482
  ),
1996
2483
  children: [
1997
2484
  (title || description) && /* @__PURE__ */ jsxs22("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
1998
- title ? /* @__PURE__ */ jsx26("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
1999
- description ? /* @__PURE__ */ jsx26("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2485
+ title ? /* @__PURE__ */ jsx27("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
2486
+ description ? /* @__PURE__ */ jsx27("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2000
2487
  ] }),
2001
- /* @__PURE__ */ jsx26("div", { className: "divide-y divide-border bg-card", children })
2488
+ /* @__PURE__ */ jsx27("div", { className: "divide-y divide-border bg-card", children })
2002
2489
  ]
2003
2490
  }
2004
2491
  );
2005
2492
 
2006
2493
  // src/app/integrations/IntegrationCard.tsx
2007
2494
  import { useId as useId2 } from "react";
2008
- import { Fragment as Fragment4, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
2495
+ import { Fragment as Fragment4, jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
2009
2496
  var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
2010
2497
  var statusLabel = {
2011
2498
  available: null,
@@ -2048,10 +2535,10 @@ var IntegrationCard = ({
2048
2535
  const dimmed = status === "disabled" || locked;
2049
2536
  const body = /* @__PURE__ */ jsxs23("div", { className: "flex h-full min-h-0 flex-col", children: [
2050
2537
  /* @__PURE__ */ jsxs23("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
2051
- logo ? /* @__PURE__ */ jsx27("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
2052
- /* @__PURE__ */ jsx27("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs23("div", { className: "flex items-start justify-between gap-2", children: [
2538
+ logo ? /* @__PURE__ */ jsx28("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
2539
+ /* @__PURE__ */ jsx28("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs23("div", { className: "flex items-start justify-between gap-2", children: [
2053
2540
  /* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
2054
- /* @__PURE__ */ jsx27(
2541
+ /* @__PURE__ */ jsx28(
2055
2542
  "h4",
2056
2543
  {
2057
2544
  id: onClick && !action ? void 0 : titleId,
@@ -2059,12 +2546,12 @@ var IntegrationCard = ({
2059
2546
  children: name
2060
2547
  }
2061
2548
  ),
2062
- statusLabel[status] ? /* @__PURE__ */ jsx27("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
2549
+ statusLabel[status] ? /* @__PURE__ */ jsx28("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
2063
2550
  ] }),
2064
- badge ? /* @__PURE__ */ jsx27("span", { className: "shrink-0", children: badge }) : null
2551
+ badge ? /* @__PURE__ */ jsx28("span", { className: "shrink-0", children: badge }) : null
2065
2552
  ] }) })
2066
2553
  ] }),
2067
- description ? /* @__PURE__ */ jsx27(
2554
+ description ? /* @__PURE__ */ jsx28(
2068
2555
  "p",
2069
2556
  {
2070
2557
  className: cn(
@@ -2075,8 +2562,8 @@ var IntegrationCard = ({
2075
2562
  }
2076
2563
  ) : null,
2077
2564
  action ? /* @__PURE__ */ jsxs23(Fragment4, { children: [
2078
- /* @__PURE__ */ jsx27("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
2079
- /* @__PURE__ */ jsx27("div", { className: "relative mt-3 shrink-0", children: action })
2565
+ /* @__PURE__ */ jsx28("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
2566
+ /* @__PURE__ */ jsx28("div", { className: "relative mt-3 shrink-0", children: action })
2080
2567
  ] }) : null
2081
2568
  ] });
2082
2569
  const shellClass3 = cn(
@@ -2086,7 +2573,7 @@ var IntegrationCard = ({
2086
2573
  className
2087
2574
  );
2088
2575
  if (onClick && !action) {
2089
- return /* @__PURE__ */ jsx27(
2576
+ return /* @__PURE__ */ jsx28(
2090
2577
  "button",
2091
2578
  {
2092
2579
  type: "button",
@@ -2103,12 +2590,12 @@ var IntegrationCard = ({
2103
2590
  }
2104
2591
  );
2105
2592
  }
2106
- return /* @__PURE__ */ jsx27("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
2593
+ return /* @__PURE__ */ jsx28("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
2107
2594
  };
2108
2595
 
2109
2596
  // src/app/integrations/IntegrationsEmptyState.tsx
2110
2597
  import { useId as useId3 } from "react";
2111
- import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
2598
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2112
2599
  var IntegrationsEmptyState = ({
2113
2600
  title = "No integrations yet",
2114
2601
  description = "Connect a provider to start syncing data and powering your workforce.",
@@ -2127,7 +2614,7 @@ var IntegrationsEmptyState = ({
2127
2614
  ),
2128
2615
  "aria-labelledby": titleId,
2129
2616
  children: [
2130
- icon ? /* @__PURE__ */ jsx28(
2617
+ icon ? /* @__PURE__ */ jsx29(
2131
2618
  "span",
2132
2619
  {
2133
2620
  className: cn(
@@ -2139,21 +2626,21 @@ var IntegrationsEmptyState = ({
2139
2626
  children: icon
2140
2627
  }
2141
2628
  ) : null,
2142
- /* @__PURE__ */ jsx28("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
2143
- description ? /* @__PURE__ */ jsx28("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
2144
- action ? /* @__PURE__ */ jsx28("div", { className: "mt-1", children: action }) : null
2629
+ /* @__PURE__ */ jsx29("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
2630
+ description ? /* @__PURE__ */ jsx29("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
2631
+ action ? /* @__PURE__ */ jsx29("div", { className: "mt-1", children: action }) : null
2145
2632
  ]
2146
2633
  }
2147
2634
  );
2148
2635
  };
2149
2636
 
2150
2637
  // src/app/integrations/PlanBadge.tsx
2151
- import { jsx as jsx29 } from "react/jsx-runtime";
2638
+ import { jsx as jsx30 } from "react/jsx-runtime";
2152
2639
  var planBadgeClass = "inline-flex h-5 max-w-full shrink-0 items-center rounded-md border border-border bg-muted/90 px-2 text-[11px] font-normal text-muted-foreground dark:border-white/10 dark:bg-white/5 dark:text-muted-foreground";
2153
- var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx29("span", { className: cn(planBadgeClass, className), children });
2640
+ var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx30("span", { className: cn(planBadgeClass, className), children });
2154
2641
 
2155
2642
  // src/app/integrations/ConnectionRow.tsx
2156
- import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2643
+ import { Fragment as Fragment5, jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
2157
2644
  var logoShellClass2 = cn(
2158
2645
  "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
2159
2646
  TIMBAL_V2_LOGO_TILE
@@ -2169,13 +2656,13 @@ var ConnectionRow = ({
2169
2656
  className
2170
2657
  }) => {
2171
2658
  const inner = /* @__PURE__ */ jsxs25(Fragment5, { children: [
2172
- logo ? /* @__PURE__ */ jsx30("span", { className: logoShellClass2, children: logo }) : null,
2659
+ logo ? /* @__PURE__ */ jsx31("span", { className: logoShellClass2, children: logo }) : null,
2173
2660
  /* @__PURE__ */ jsxs25("div", { className: "min-w-0 flex-1", children: [
2174
- /* @__PURE__ */ jsx30("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
2175
- meta ? /* @__PURE__ */ jsx30("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
2661
+ /* @__PURE__ */ jsx31("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
2662
+ meta ? /* @__PURE__ */ jsx31("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
2176
2663
  ] }),
2177
- badge ? /* @__PURE__ */ jsx30("span", { className: "shrink-0", children: badge }) : null,
2178
- action ? /* @__PURE__ */ jsx30("span", { className: "shrink-0", children: action }) : null
2664
+ badge ? /* @__PURE__ */ jsx31("span", { className: "shrink-0", children: badge }) : null,
2665
+ action ? /* @__PURE__ */ jsx31("span", { className: "shrink-0", children: action }) : null
2179
2666
  ] });
2180
2667
  const rowClass2 = cn(
2181
2668
  "flex w-full items-center gap-3 px-4 py-3 text-left",
@@ -2183,7 +2670,7 @@ var ConnectionRow = ({
2183
2670
  className
2184
2671
  );
2185
2672
  if (onClick) {
2186
- return /* @__PURE__ */ jsx30(
2673
+ return /* @__PURE__ */ jsx31(
2187
2674
  "button",
2188
2675
  {
2189
2676
  type: "button",
@@ -2195,7 +2682,7 @@ var ConnectionRow = ({
2195
2682
  }
2196
2683
  );
2197
2684
  }
2198
- return /* @__PURE__ */ jsx30("div", { role: "listitem", className: rowClass2, children: inner });
2685
+ return /* @__PURE__ */ jsx31("div", { role: "listitem", className: rowClass2, children: inner });
2199
2686
  };
2200
2687
  var connectionRowListClass = cn(
2201
2688
  "overflow-hidden rounded-2xl",
@@ -2203,12 +2690,12 @@ var connectionRowListClass = cn(
2203
2690
  );
2204
2691
 
2205
2692
  // src/app/integrations/ConnectionRowList.tsx
2206
- import { jsx as jsx31 } from "react/jsx-runtime";
2693
+ import { jsx as jsx32 } from "react/jsx-runtime";
2207
2694
  var ConnectionRowList = ({
2208
2695
  children,
2209
2696
  "aria-label": ariaLabel = "Connected integrations",
2210
2697
  className
2211
- }) => /* @__PURE__ */ jsx31(
2698
+ }) => /* @__PURE__ */ jsx32(
2212
2699
  "div",
2213
2700
  {
2214
2701
  role: "list",
@@ -2219,7 +2706,7 @@ var ConnectionRowList = ({
2219
2706
  );
2220
2707
 
2221
2708
  // src/app/navigation/SubNav.tsx
2222
- import { jsx as jsx32 } from "react/jsx-runtime";
2709
+ import { jsx as jsx33 } from "react/jsx-runtime";
2223
2710
  var SubNav = ({
2224
2711
  items,
2225
2712
  activeId,
@@ -2228,7 +2715,7 @@ var SubNav = ({
2228
2715
  "aria-label": ariaLabel = "Section navigation",
2229
2716
  layoutId
2230
2717
  }) => {
2231
- return /* @__PURE__ */ jsx32("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx32(
2718
+ return /* @__PURE__ */ jsx33("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx33(
2232
2719
  PillSegmentedTabs,
2233
2720
  {
2234
2721
  value: activeId,
@@ -2242,13 +2729,13 @@ var SubNav = ({
2242
2729
  };
2243
2730
 
2244
2731
  // src/app/navigation/Breadcrumbs.tsx
2245
- import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
2732
+ import { jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
2246
2733
  var Breadcrumbs = ({ items, className }) => {
2247
- return /* @__PURE__ */ jsx33("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx33("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
2734
+ return /* @__PURE__ */ jsx34("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx34("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
2248
2735
  const isLast = index === items.length - 1;
2249
2736
  return /* @__PURE__ */ jsxs26("li", { className: "inline-flex items-center gap-1.5", children: [
2250
- index > 0 ? /* @__PURE__ */ jsx33("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
2251
- isLast ? /* @__PURE__ */ jsx33("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx33("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx33(
2737
+ index > 0 ? /* @__PURE__ */ jsx34("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
2738
+ isLast ? /* @__PURE__ */ jsx34("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx34("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx34(
2252
2739
  "button",
2253
2740
  {
2254
2741
  type: "button",
@@ -2262,7 +2749,7 @@ var Breadcrumbs = ({ items, className }) => {
2262
2749
  };
2263
2750
 
2264
2751
  // src/app/forms/Field.tsx
2265
- import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
2752
+ import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
2266
2753
  var Field = ({
2267
2754
  label,
2268
2755
  hint,
@@ -2272,10 +2759,10 @@ var Field = ({
2272
2759
  htmlFor
2273
2760
  }) => {
2274
2761
  return /* @__PURE__ */ jsxs27("div", { className: cn("aui-app-field", appFieldClass, className), children: [
2275
- /* @__PURE__ */ jsx34("label", { className: appFieldLabelClass, htmlFor, children: label }),
2762
+ /* @__PURE__ */ jsx35("label", { className: appFieldLabelClass, htmlFor, children: label }),
2276
2763
  children,
2277
- hint && !error ? /* @__PURE__ */ jsx34("p", { className: appFieldHintClass, children: hint }) : null,
2278
- error ? /* @__PURE__ */ jsx34("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
2764
+ hint && !error ? /* @__PURE__ */ jsx35("p", { className: appFieldHintClass, children: hint }) : null,
2765
+ error ? /* @__PURE__ */ jsx35("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
2279
2766
  ] });
2280
2767
  };
2281
2768
  var FieldInput = ({
@@ -2288,7 +2775,7 @@ var FieldInput = ({
2288
2775
  ...inputProps
2289
2776
  }) => {
2290
2777
  const inputId = id ?? inputProps.name;
2291
- return /* @__PURE__ */ jsx34(
2778
+ return /* @__PURE__ */ jsx35(
2292
2779
  Field,
2293
2780
  {
2294
2781
  label,
@@ -2296,7 +2783,7 @@ var FieldInput = ({
2296
2783
  error,
2297
2784
  htmlFor: inputId,
2298
2785
  className: fieldClassName,
2299
- children: /* @__PURE__ */ jsx34(
2786
+ children: /* @__PURE__ */ jsx35(
2300
2787
  "input",
2301
2788
  {
2302
2789
  id: inputId,
@@ -2310,7 +2797,7 @@ var FieldInput = ({
2310
2797
  };
2311
2798
 
2312
2799
  // src/app/forms/FieldTextarea.tsx
2313
- import { jsx as jsx35 } from "react/jsx-runtime";
2800
+ import { jsx as jsx36 } from "react/jsx-runtime";
2314
2801
  var textareaClass = cn(
2315
2802
  appInputClass,
2316
2803
  "min-h-[5.5rem] resize-y py-2.5 leading-relaxed"
@@ -2325,7 +2812,7 @@ var FieldTextarea = ({
2325
2812
  ...props
2326
2813
  }) => {
2327
2814
  const textareaId = id ?? props.name;
2328
- return /* @__PURE__ */ jsx35(
2815
+ return /* @__PURE__ */ jsx36(
2329
2816
  Field,
2330
2817
  {
2331
2818
  label,
@@ -2333,7 +2820,7 @@ var FieldTextarea = ({
2333
2820
  error,
2334
2821
  htmlFor: textareaId,
2335
2822
  className: fieldClassName,
2336
- children: /* @__PURE__ */ jsx35(
2823
+ children: /* @__PURE__ */ jsx36(
2337
2824
  "textarea",
2338
2825
  {
2339
2826
  id: textareaId,
@@ -2348,7 +2835,7 @@ var FieldTextarea = ({
2348
2835
 
2349
2836
  // src/app/forms/FieldSelect.tsx
2350
2837
  import { ChevronDownIcon } from "lucide-react";
2351
- import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
2838
+ import { jsx as jsx37, jsxs as jsxs28 } from "react/jsx-runtime";
2352
2839
  var selectWrapClass = "relative";
2353
2840
  var selectClass = cn(
2354
2841
  appInputClass,
@@ -2365,7 +2852,7 @@ var FieldSelect = ({
2365
2852
  ...props
2366
2853
  }) => {
2367
2854
  const selectId = id ?? props.name;
2368
- return /* @__PURE__ */ jsx36(
2855
+ return /* @__PURE__ */ jsx37(
2369
2856
  Field,
2370
2857
  {
2371
2858
  label,
@@ -2374,7 +2861,7 @@ var FieldSelect = ({
2374
2861
  htmlFor: selectId,
2375
2862
  className: fieldClassName,
2376
2863
  children: /* @__PURE__ */ jsxs28("div", { className: selectWrapClass, children: [
2377
- /* @__PURE__ */ jsx36(
2864
+ /* @__PURE__ */ jsx37(
2378
2865
  "select",
2379
2866
  {
2380
2867
  id: selectId,
@@ -2384,7 +2871,7 @@ var FieldSelect = ({
2384
2871
  children
2385
2872
  }
2386
2873
  ),
2387
- /* @__PURE__ */ jsx36(
2874
+ /* @__PURE__ */ jsx37(
2388
2875
  ChevronDownIcon,
2389
2876
  {
2390
2877
  className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground",
@@ -2397,7 +2884,7 @@ var FieldSelect = ({
2397
2884
  };
2398
2885
 
2399
2886
  // src/app/forms/FieldSwitch.tsx
2400
- import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
2887
+ import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
2401
2888
  var trackClass = cn(
2402
2889
  "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
2403
2890
  "peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
@@ -2428,7 +2915,7 @@ var FieldSwitch = ({
2428
2915
  htmlFor: inputId,
2429
2916
  children: [
2430
2917
  /* @__PURE__ */ jsxs29("span", { className: "relative mt-0.5", children: [
2431
- /* @__PURE__ */ jsx37(
2918
+ /* @__PURE__ */ jsx38(
2432
2919
  "input",
2433
2920
  {
2434
2921
  id: inputId,
@@ -2438,11 +2925,11 @@ var FieldSwitch = ({
2438
2925
  ...props
2439
2926
  }
2440
2927
  ),
2441
- /* @__PURE__ */ jsx37("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx37("span", { className: thumbClass }) })
2928
+ /* @__PURE__ */ jsx38("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx38("span", { className: thumbClass }) })
2442
2929
  ] }),
2443
2930
  /* @__PURE__ */ jsxs29("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
2444
- /* @__PURE__ */ jsx37("span", { className: "text-sm font-medium text-foreground", children: label }),
2445
- description ? /* @__PURE__ */ jsx37("span", { className: "text-xs text-muted-foreground", children: description }) : null
2931
+ /* @__PURE__ */ jsx38("span", { className: "text-sm font-medium text-foreground", children: label }),
2932
+ description ? /* @__PURE__ */ jsx38("span", { className: "text-xs text-muted-foreground", children: description }) : null
2446
2933
  ] })
2447
2934
  ]
2448
2935
  }
@@ -2451,7 +2938,7 @@ var FieldSwitch = ({
2451
2938
 
2452
2939
  // src/app/forms/SearchInput.tsx
2453
2940
  import { SearchIcon } from "lucide-react";
2454
- import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
2941
+ import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
2455
2942
  var SearchInput = ({
2456
2943
  className,
2457
2944
  placeholder = "Search\u2026",
@@ -2466,8 +2953,8 @@ var SearchInput = ({
2466
2953
  className
2467
2954
  ),
2468
2955
  children: [
2469
- /* @__PURE__ */ jsx38(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
2470
- /* @__PURE__ */ jsx38(
2956
+ /* @__PURE__ */ jsx39(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
2957
+ /* @__PURE__ */ jsx39(
2471
2958
  "input",
2472
2959
  {
2473
2960
  type: "search",
@@ -2482,18 +2969,18 @@ var SearchInput = ({
2482
2969
  };
2483
2970
 
2484
2971
  // src/app/forms/FormSection.tsx
2485
- import { jsx as jsx39, jsxs as jsxs31 } from "react/jsx-runtime";
2972
+ import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
2486
2973
  var FormSection = ({ title, children, className }) => {
2487
2974
  return /* @__PURE__ */ jsxs31("fieldset", { className: cn("aui-app-form-section", appSectionClass, "border-0 p-0", className), children: [
2488
- title ? /* @__PURE__ */ jsx39("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
2489
- /* @__PURE__ */ jsx39("div", { className: "flex flex-col gap-4", children })
2975
+ title ? /* @__PURE__ */ jsx40("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
2976
+ /* @__PURE__ */ jsx40("div", { className: "flex flex-col gap-4", children })
2490
2977
  ] });
2491
2978
  };
2492
2979
 
2493
2980
  // src/app/data/FilterBar.tsx
2494
- import { jsx as jsx40 } from "react/jsx-runtime";
2981
+ import { jsx as jsx41 } from "react/jsx-runtime";
2495
2982
  var FilterBar = ({ children, className }) => {
2496
- return /* @__PURE__ */ jsx40(
2983
+ return /* @__PURE__ */ jsx41(
2497
2984
  "div",
2498
2985
  {
2499
2986
  className: cn("aui-app-filter-bar", appFilterBarClass, className),
@@ -2504,20 +2991,41 @@ var FilterBar = ({ children, className }) => {
2504
2991
  );
2505
2992
  };
2506
2993
 
2994
+ // src/app/data/FilterField.tsx
2995
+ import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
2996
+ var FilterField = ({
2997
+ label,
2998
+ children,
2999
+ className
3000
+ }) => {
3001
+ return /* @__PURE__ */ jsxs32("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
3002
+ label ? /* @__PURE__ */ jsx42("span", { className: appFieldLabelClass, children: label }) : null,
3003
+ children
3004
+ ] });
3005
+ };
3006
+
2507
3007
  // src/app/data/DataTable.tsx
2508
- import { useMemo, useState as useState4 } from "react";
2509
- import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from "lucide-react";
2510
- import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
3008
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
3009
+ import {
3010
+ ArrowDownIcon,
3011
+ ArrowUpDownIcon,
3012
+ ArrowUpIcon,
3013
+ ChevronLeftIcon,
3014
+ ChevronRightIcon
3015
+ } from "lucide-react";
3016
+ import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
2511
3017
  var shellClass2 = "overflow-hidden rounded-xl border border-border bg-card shadow-card";
2512
3018
  var tableClass = "w-full border-collapse bg-transparent text-sm";
2513
3019
  var headCellClass = "border-b border-border/60 bg-transparent px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wide text-muted-foreground";
2514
3020
  var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-2.5 text-foreground";
2515
- var rowClass = "bg-transparent transition-colors hover:bg-foreground/[0.03] data-[clickable=true]:cursor-pointer";
3021
+ var rowClass = "bg-transparent transition-colors hover:bg-foreground/[0.03] data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.04]";
2516
3022
  var footCellClass = "border-t border-border/60 bg-transparent px-4 py-2.5 text-xs text-muted-foreground";
2517
3023
  var footInnerClass = "flex flex-wrap items-center gap-2";
2518
3024
  var emptyCellClass = "bg-transparent px-4 py-10 text-center text-sm text-muted-foreground";
2519
3025
  var sortButtonClass = "group inline-flex min-w-0 items-center gap-1.5 rounded-md -mx-1 px-1 py-0.5 text-left font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
2520
3026
  var stickyHeadClass = "sticky top-0 z-[1] bg-card/95 shadow-[0_1px_0_0_hsl(var(--border)/0.5)] backdrop-blur-sm [&_th]:bg-card/95";
3027
+ var selectCellClass = "w-10 px-4 py-2.5 align-middle";
3028
+ var pagerButtonClass = "inline-flex size-7 items-center justify-center rounded-md border border-border bg-transparent text-muted-foreground transition-colors hover:bg-foreground/[0.04] hover:text-foreground disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
2521
3029
  var alignClass = {
2522
3030
  left: "text-left",
2523
3031
  center: "text-center",
@@ -2547,12 +3055,12 @@ function SortIndicator({
2547
3055
  }) {
2548
3056
  const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
2549
3057
  if (!active) {
2550
- return /* @__PURE__ */ jsx41(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
3058
+ return /* @__PURE__ */ jsx43(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
2551
3059
  }
2552
3060
  if (direction === "desc") {
2553
- return /* @__PURE__ */ jsx41(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
3061
+ return /* @__PURE__ */ jsx43(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
2554
3062
  }
2555
- return /* @__PURE__ */ jsx41(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
3063
+ return /* @__PURE__ */ jsx43(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
2556
3064
  }
2557
3065
  function DataTable({
2558
3066
  columns,
@@ -2571,7 +3079,18 @@ function DataTable({
2571
3079
  onRowClick,
2572
3080
  stickyHeader = false,
2573
3081
  dense = false,
2574
- caption
3082
+ caption,
3083
+ loading = false,
3084
+ loadingRows,
3085
+ selectable = false,
3086
+ selectedKeys: selectedKeysProp,
3087
+ defaultSelectedKeys,
3088
+ onSelectionChange,
3089
+ selectAllAriaLabel = "Select all rows",
3090
+ pageSize,
3091
+ pageIndex: pageIndexProp,
3092
+ defaultPageIndex = 0,
3093
+ onPageChange
2575
3094
  }) {
2576
3095
  const [uncontrolledSort, setUncontrolledSort] = useState4(
2577
3096
  defaultSort
@@ -2584,7 +3103,28 @@ function DataTable({
2584
3103
  }
2585
3104
  onSortChange?.(next);
2586
3105
  };
2587
- const sortedRows = useMemo(() => {
3106
+ const [uncontrolledSelected, setUncontrolledSelected] = useState4(
3107
+ defaultSelectedKeys ?? []
3108
+ );
3109
+ const isSelectionControlled = selectedKeysProp !== void 0;
3110
+ const selectedKeys = isSelectionControlled ? selectedKeysProp : uncontrolledSelected;
3111
+ const selectedSet = useMemo2(() => new Set(selectedKeys), [selectedKeys]);
3112
+ const setSelected = (next) => {
3113
+ if (!isSelectionControlled) {
3114
+ setUncontrolledSelected(next);
3115
+ }
3116
+ onSelectionChange?.(next);
3117
+ };
3118
+ const [uncontrolledPage, setUncontrolledPage] = useState4(defaultPageIndex);
3119
+ const isPageControlled = pageIndexProp !== void 0;
3120
+ const rawPageIndex = isPageControlled ? pageIndexProp : uncontrolledPage;
3121
+ const setPage = (next) => {
3122
+ if (!isPageControlled) {
3123
+ setUncontrolledPage(next);
3124
+ }
3125
+ onPageChange?.(next);
3126
+ };
3127
+ const sortedRows = useMemo2(() => {
2588
3128
  if (!sort) return rows;
2589
3129
  const column = columns.find((col) => col.id === sort.columnId);
2590
3130
  if (!column?.sortable) return rows;
@@ -2600,89 +3140,218 @@ function DataTable({
2600
3140
  return sort.direction === "asc" ? cmp : -cmp;
2601
3141
  });
2602
3142
  }, [columns, rows, sort]);
3143
+ const paginated = typeof pageSize === "number" && pageSize > 0;
3144
+ const pageCount = paginated ? Math.max(1, Math.ceil(sortedRows.length / pageSize)) : 1;
3145
+ const pageIndex = Math.min(Math.max(0, rawPageIndex), pageCount - 1);
3146
+ useEffect2(() => {
3147
+ if (!paginated || isPageControlled) return;
3148
+ if (uncontrolledPage > pageCount - 1) {
3149
+ setUncontrolledPage(pageCount - 1);
3150
+ }
3151
+ }, [paginated, isPageControlled, uncontrolledPage, pageCount]);
3152
+ const visibleRows = useMemo2(() => {
3153
+ if (!paginated) return sortedRows;
3154
+ const start = pageIndex * pageSize;
3155
+ return sortedRows.slice(start, start + pageSize);
3156
+ }, [paginated, sortedRows, pageIndex, pageSize]);
2603
3157
  const cellPad = dense ? "px-3 py-2" : void 0;
2604
3158
  const headPad = dense ? "px-3 py-2" : void 0;
2605
- if (rows.length === 0 && emptyMode === "replace") {
2606
- return /* @__PURE__ */ jsx41(EmptyState, { title: emptyTitle, description: emptyDescription, className });
3159
+ const colSpan = columns.length + (selectable ? 1 : 0);
3160
+ if (!loading && rows.length === 0 && emptyMode === "replace") {
3161
+ return /* @__PURE__ */ jsx43(EmptyState, { title: emptyTitle, description: emptyDescription, className });
2607
3162
  }
3163
+ const allKeys = sortedRows.map(getRowKey);
3164
+ const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
3165
+ const headerCheckedState = allSelected ? true : selectedSet.size > 0 ? "indeterminate" : false;
3166
+ const toggleAll = () => {
3167
+ if (allSelected) {
3168
+ setSelected([]);
3169
+ } else {
3170
+ setSelected(allKeys);
3171
+ }
3172
+ };
3173
+ const toggleRow = (key) => {
3174
+ const next = new Set(selectedSet);
3175
+ if (next.has(key)) {
3176
+ next.delete(key);
3177
+ } else {
3178
+ next.add(key);
3179
+ }
3180
+ setSelected([...next]);
3181
+ };
2608
3182
  const rowCountText = rowCountLabel?.(sortedRows.length) ?? `${sortedRows.length} row${sortedRows.length === 1 ? "" : "s"}`;
2609
- const hasFoot = Boolean((showRowCount || footer) && sortedRows.length > 0);
2610
- return /* @__PURE__ */ jsx41("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx41("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs32("table", { className: tableClass, children: [
2611
- caption ? /* @__PURE__ */ jsx41("caption", { className: "sr-only", children: caption }) : null,
2612
- /* @__PURE__ */ jsx41("thead", { className: cn(stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsx41("tr", { children: columns.map((col) => {
2613
- const isSorted = sort?.columnId === col.id;
2614
- const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
2615
- const headerContent = col.sortable ? /* @__PURE__ */ jsxs32(
2616
- "button",
3183
+ const hasPager = paginated && !loading && sortedRows.length > 0;
3184
+ const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
3185
+ const skeletonCount = loadingRows ?? pageSize ?? 5;
3186
+ return /* @__PURE__ */ jsx43("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx43("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs33("table", { className: tableClass, children: [
3187
+ caption ? /* @__PURE__ */ jsx43("caption", { className: "sr-only", children: caption }) : null,
3188
+ /* @__PURE__ */ jsx43("thead", { className: cn(stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs33("tr", { children: [
3189
+ selectable ? /* @__PURE__ */ jsx43("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx43(
3190
+ Checkbox,
2617
3191
  {
2618
- type: "button",
2619
- className: sortButtonClass,
2620
- onClick: () => setSort(nextSort(sort, col.id)),
2621
- children: [
2622
- /* @__PURE__ */ jsx41("span", { className: "truncate", children: col.header }),
2623
- /* @__PURE__ */ jsx41(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
2624
- ]
3192
+ checked: headerCheckedState,
3193
+ onCheckedChange: toggleAll,
3194
+ "aria-label": selectAllAriaLabel,
3195
+ disabled: loading || allKeys.length === 0
2625
3196
  }
2626
- ) : col.header;
2627
- return /* @__PURE__ */ jsx41(
2628
- "th",
2629
- {
2630
- scope: "col",
2631
- "aria-sort": ariaSort,
2632
- className: cn(
2633
- headCellClass,
2634
- headPad,
2635
- col.align && alignClass[col.align],
2636
- col.headerClassName
2637
- ),
2638
- children: headerContent
2639
- },
2640
- col.id
2641
- );
2642
- }) }) }),
2643
- /* @__PURE__ */ jsx41("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: sortedRows.length === 0 ? /* @__PURE__ */ jsx41("tr", { children: /* @__PURE__ */ jsx41("td", { colSpan: columns.length, className: emptyCellClass, children: /* @__PURE__ */ jsxs32("div", { className: "flex flex-col items-center gap-1", children: [
2644
- /* @__PURE__ */ jsx41("p", { className: "font-medium text-foreground", children: emptyTitle }),
2645
- emptyDescription ? /* @__PURE__ */ jsx41("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
2646
- ] }) }) }) : sortedRows.map((row) => /* @__PURE__ */ jsx41(
2647
- "tr",
2648
- {
2649
- className: rowClass,
2650
- "data-clickable": onRowClick ? "true" : void 0,
2651
- onClick: onRowClick ? () => onRowClick(row) : void 0,
2652
- onKeyDown: onRowClick ? (event) => {
2653
- if (event.key === "Enter" || event.key === " ") {
2654
- event.preventDefault();
2655
- onRowClick(row);
3197
+ ) }) : null,
3198
+ columns.map((col) => {
3199
+ const isSorted = sort?.columnId === col.id;
3200
+ const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
3201
+ const headerContent = col.sortable ? /* @__PURE__ */ jsxs33(
3202
+ "button",
3203
+ {
3204
+ type: "button",
3205
+ className: sortButtonClass,
3206
+ onClick: () => setSort(nextSort(sort, col.id)),
3207
+ children: [
3208
+ /* @__PURE__ */ jsx43("span", { className: "truncate", children: col.header }),
3209
+ /* @__PURE__ */ jsx43(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
3210
+ ]
2656
3211
  }
2657
- } : void 0,
2658
- tabIndex: onRowClick ? 0 : void 0,
2659
- role: onRowClick ? "button" : void 0,
2660
- children: columns.map((col) => /* @__PURE__ */ jsx41(
2661
- "td",
3212
+ ) : col.header;
3213
+ return /* @__PURE__ */ jsx43(
3214
+ "th",
2662
3215
  {
3216
+ scope: "col",
3217
+ "aria-sort": ariaSort,
2663
3218
  className: cn(
2664
- bodyCellClass,
2665
- cellPad,
3219
+ headCellClass,
3220
+ headPad,
2666
3221
  col.align && alignClass[col.align],
2667
- col.className
3222
+ col.headerClassName
2668
3223
  ),
2669
- children: col.cell(row)
3224
+ children: headerContent
2670
3225
  },
2671
3226
  col.id
2672
- ))
2673
- },
2674
- getRowKey(row)
2675
- )) }),
2676
- hasFoot ? /* @__PURE__ */ jsx41("tfoot", { children: /* @__PURE__ */ jsx41("tr", { children: /* @__PURE__ */ jsx41("td", { colSpan: columns.length, className: footCellClass, children: /* @__PURE__ */ jsxs32(
3227
+ );
3228
+ })
3229
+ ] }) }),
3230
+ /* @__PURE__ */ jsx43("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ jsxs33("tr", { className: rowClass, "aria-hidden": true, children: [
3231
+ selectable ? /* @__PURE__ */ jsx43("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx43(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
3232
+ columns.map((col) => /* @__PURE__ */ jsx43(
3233
+ "td",
3234
+ {
3235
+ className: cn(
3236
+ bodyCellClass,
3237
+ cellPad,
3238
+ col.align && alignClass[col.align],
3239
+ col.className
3240
+ ),
3241
+ children: /* @__PURE__ */ jsx43(Skeleton, { className: "h-4 w-[60%]" })
3242
+ },
3243
+ col.id
3244
+ ))
3245
+ ] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ jsx43("tr", { children: /* @__PURE__ */ jsx43("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ jsxs33("div", { className: "flex flex-col items-center gap-1", children: [
3246
+ /* @__PURE__ */ jsx43("p", { className: "font-medium text-foreground", children: emptyTitle }),
3247
+ emptyDescription ? /* @__PURE__ */ jsx43("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
3248
+ ] }) }) }) : visibleRows.map((row) => {
3249
+ const key = getRowKey(row);
3250
+ const isSelected = selectedSet.has(key);
3251
+ return /* @__PURE__ */ jsxs33(
3252
+ "tr",
3253
+ {
3254
+ className: rowClass,
3255
+ "data-clickable": onRowClick ? "true" : void 0,
3256
+ "data-selected": isSelected ? "true" : void 0,
3257
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
3258
+ onKeyDown: onRowClick ? (event) => {
3259
+ if (event.key === "Enter" || event.key === " ") {
3260
+ event.preventDefault();
3261
+ onRowClick(row);
3262
+ }
3263
+ } : void 0,
3264
+ tabIndex: onRowClick ? 0 : void 0,
3265
+ role: onRowClick ? "button" : void 0,
3266
+ children: [
3267
+ selectable ? /* @__PURE__ */ jsx43(
3268
+ "td",
3269
+ {
3270
+ className: cn(selectCellClass, cellPad),
3271
+ onClick: (event) => event.stopPropagation(),
3272
+ children: /* @__PURE__ */ jsx43(
3273
+ Checkbox,
3274
+ {
3275
+ checked: isSelected,
3276
+ onCheckedChange: () => toggleRow(key),
3277
+ "aria-label": `Select row`
3278
+ }
3279
+ )
3280
+ }
3281
+ ) : null,
3282
+ columns.map((col) => /* @__PURE__ */ jsx43(
3283
+ "td",
3284
+ {
3285
+ className: cn(
3286
+ bodyCellClass,
3287
+ cellPad,
3288
+ col.align && alignClass[col.align],
3289
+ col.className
3290
+ ),
3291
+ children: col.cell(row)
3292
+ },
3293
+ col.id
3294
+ ))
3295
+ ]
3296
+ },
3297
+ key
3298
+ );
3299
+ }) }),
3300
+ hasFoot ? /* @__PURE__ */ jsx43("tfoot", { children: /* @__PURE__ */ jsx43("tr", { children: /* @__PURE__ */ jsx43("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs33(
2677
3301
  "div",
2678
3302
  {
2679
3303
  className: cn(
2680
3304
  footInnerClass,
2681
- showRowCount && footer ? "justify-between" : "justify-start"
3305
+ (showRowCount || footer || hasPager) && "justify-between"
2682
3306
  ),
2683
3307
  children: [
2684
- showRowCount ? /* @__PURE__ */ jsx41("span", { children: rowCountText }) : null,
2685
- footer
3308
+ /* @__PURE__ */ jsxs33("div", { className: footInnerClass, children: [
3309
+ showRowCount ? /* @__PURE__ */ jsxs33("span", { children: [
3310
+ rowCountText,
3311
+ selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
3312
+ ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs33("span", { children: [
3313
+ selectedSet.size,
3314
+ " selected"
3315
+ ] }) : null,
3316
+ footer
3317
+ ] }),
3318
+ hasPager ? /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-2", children: [
3319
+ /* @__PURE__ */ jsxs33("span", { className: "tabular-nums", children: [
3320
+ pageIndex * pageSize + 1,
3321
+ "\u2013",
3322
+ Math.min(
3323
+ (pageIndex + 1) * pageSize,
3324
+ sortedRows.length
3325
+ ),
3326
+ " ",
3327
+ "of ",
3328
+ sortedRows.length
3329
+ ] }),
3330
+ /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-1", children: [
3331
+ /* @__PURE__ */ jsx43(
3332
+ "button",
3333
+ {
3334
+ type: "button",
3335
+ className: pagerButtonClass,
3336
+ onClick: () => setPage(pageIndex - 1),
3337
+ disabled: pageIndex <= 0,
3338
+ "aria-label": "Previous page",
3339
+ children: /* @__PURE__ */ jsx43(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
3340
+ }
3341
+ ),
3342
+ /* @__PURE__ */ jsx43(
3343
+ "button",
3344
+ {
3345
+ type: "button",
3346
+ className: pagerButtonClass,
3347
+ onClick: () => setPage(pageIndex + 1),
3348
+ disabled: pageIndex >= pageCount - 1,
3349
+ "aria-label": "Next page",
3350
+ children: /* @__PURE__ */ jsx43(ChevronRightIcon, { className: "size-4", "aria-hidden": true })
3351
+ }
3352
+ )
3353
+ ] })
3354
+ ] }) : null
2686
3355
  ]
2687
3356
  }
2688
3357
  ) }) }) }) : null
@@ -2691,7 +3360,7 @@ function DataTable({
2691
3360
 
2692
3361
  // src/app/data/ChartPanel.tsx
2693
3362
  import { useId as useId4 } from "react";
2694
- import { jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
3363
+ import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
2695
3364
  var ChartPanel = ({
2696
3365
  title,
2697
3366
  description,
@@ -2699,19 +3368,20 @@ var ChartPanel = ({
2699
3368
  children,
2700
3369
  actions,
2701
3370
  height = 300,
3371
+ loading = false,
2702
3372
  className
2703
3373
  }) => {
2704
3374
  const titleId = useId4();
2705
3375
  const resolvedTitle = title ?? artifact?.title;
2706
3376
  const hasHeader = Boolean(resolvedTitle || description || actions);
2707
- const body = children ?? (artifact ? /* @__PURE__ */ jsx42(ChartArtifactView, { artifact, embedded: true, height }) : null);
2708
- return /* @__PURE__ */ jsxs33(
3377
+ const body = loading ? /* @__PURE__ */ jsx44(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ jsx44(ChartArtifactView, { artifact, embedded: true, height }) : null);
3378
+ return /* @__PURE__ */ jsxs34(
2709
3379
  "section",
2710
3380
  {
2711
3381
  className: cn(metricCardShellClass, "aui-app-chart-panel", className),
2712
3382
  "aria-labelledby": resolvedTitle ? titleId : void 0,
2713
3383
  children: [
2714
- /* @__PURE__ */ jsx42(
3384
+ /* @__PURE__ */ jsx44(
2715
3385
  MetricCardHeader,
2716
3386
  {
2717
3387
  title: resolvedTitle,
@@ -2720,14 +3390,14 @@ var ChartPanel = ({
2720
3390
  actions
2721
3391
  }
2722
3392
  ),
2723
- /* @__PURE__ */ jsx42(
3393
+ /* @__PURE__ */ jsx44(
2724
3394
  "div",
2725
3395
  {
2726
3396
  className: cn(
2727
3397
  "relative min-h-0 w-full",
2728
3398
  hasHeader ? metricChartPlotRegionClass : "pt-2 pb-3"
2729
3399
  ),
2730
- children: body ?? /* @__PURE__ */ jsx42(
3400
+ children: body ?? /* @__PURE__ */ jsx44(
2731
3401
  "div",
2732
3402
  {
2733
3403
  className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
@@ -2745,7 +3415,7 @@ var ChartPanel = ({
2745
3415
 
2746
3416
  // src/app/data/MetricRow.tsx
2747
3417
  import { useId as useId5, useState as useState5 } from "react";
2748
- import { jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
3418
+ import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
2749
3419
  var MetricRow = ({
2750
3420
  title,
2751
3421
  description,
@@ -2755,6 +3425,7 @@ var MetricRow = ({
2755
3425
  defaultActiveMetricId,
2756
3426
  onMetricChange,
2757
3427
  metricsAriaLabel = "Metrics",
3428
+ loading = false,
2758
3429
  className
2759
3430
  }) => {
2760
3431
  const titleId = useId5();
@@ -2767,13 +3438,13 @@ var MetricRow = ({
2767
3438
  if (activeMetricId == null) setInternalId(id);
2768
3439
  onMetricChange?.(id);
2769
3440
  };
2770
- return /* @__PURE__ */ jsxs34(
3441
+ return /* @__PURE__ */ jsxs35(
2771
3442
  "section",
2772
3443
  {
2773
3444
  className: cn(metricCardShellClass, className),
2774
3445
  "aria-labelledby": title ? titleId : void 0,
2775
3446
  children: [
2776
- /* @__PURE__ */ jsx43(
3447
+ /* @__PURE__ */ jsx45(
2777
3448
  MetricCardHeader,
2778
3449
  {
2779
3450
  title,
@@ -2782,17 +3453,29 @@ var MetricRow = ({
2782
3453
  actions
2783
3454
  }
2784
3455
  ),
2785
- /* @__PURE__ */ jsx43(
3456
+ /* @__PURE__ */ jsx45(
2786
3457
  "div",
2787
3458
  {
2788
3459
  role: selectable ? "group" : void 0,
2789
3460
  "aria-label": selectable ? metricsAriaLabel : void 0,
3461
+ "aria-busy": loading || void 0,
2790
3462
  className: cn(
2791
3463
  metricTilesRowClass,
2792
- metricTilesGridColsClass(metrics.length),
3464
+ metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
2793
3465
  (title || description || actions) && "mt-3"
2794
3466
  ),
2795
- children: metrics.map((m, index) => /* @__PURE__ */ jsx43(
3467
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs35(
3468
+ "div",
3469
+ {
3470
+ className: "flex min-w-0 flex-1 flex-col gap-2 px-4 py-3",
3471
+ "aria-hidden": true,
3472
+ children: [
3473
+ /* @__PURE__ */ jsx45(Skeleton, { className: "h-3 w-20" }),
3474
+ /* @__PURE__ */ jsx45(Skeleton, { className: "h-7 w-24" })
3475
+ ]
3476
+ },
3477
+ `skeleton-${index}`
3478
+ )) : metrics.map((m, index) => /* @__PURE__ */ jsx45(
2796
3479
  MetricTile,
2797
3480
  {
2798
3481
  label: m.label,
@@ -2815,7 +3498,7 @@ var MetricRow = ({
2815
3498
 
2816
3499
  // src/app/data/MetricChartCard.tsx
2817
3500
  import { useId as useId6, useState as useState6 } from "react";
2818
- import { jsx as jsx44, jsxs as jsxs35 } from "react/jsx-runtime";
3501
+ import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
2819
3502
  var MetricChartCard = ({
2820
3503
  title,
2821
3504
  description,
@@ -2831,6 +3514,7 @@ var MetricChartCard = ({
2831
3514
  formatValue,
2832
3515
  emptyLabel = "No data yet",
2833
3516
  metricsAriaLabel = "Metrics",
3517
+ loading = false,
2834
3518
  className
2835
3519
  }) => {
2836
3520
  const titleId = useId6();
@@ -2845,13 +3529,13 @@ var MetricChartCard = ({
2845
3529
  };
2846
3530
  const hasHeader = Boolean(title || description || actions);
2847
3531
  const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
2848
- return /* @__PURE__ */ jsxs35(
3532
+ return /* @__PURE__ */ jsxs36(
2849
3533
  "section",
2850
3534
  {
2851
3535
  className: cn(metricCardShellClass, className),
2852
3536
  "aria-labelledby": title ? titleId : void 0,
2853
3537
  children: [
2854
- /* @__PURE__ */ jsx44(
3538
+ /* @__PURE__ */ jsx46(
2855
3539
  MetricCardHeader,
2856
3540
  {
2857
3541
  title,
@@ -2860,17 +3544,29 @@ var MetricChartCard = ({
2860
3544
  actions
2861
3545
  }
2862
3546
  ),
2863
- /* @__PURE__ */ jsx44(
3547
+ /* @__PURE__ */ jsx46(
2864
3548
  "div",
2865
3549
  {
2866
3550
  role: "group",
2867
3551
  "aria-label": metricsAriaLabel,
3552
+ "aria-busy": loading || void 0,
2868
3553
  className: cn(
2869
3554
  metricTilesRowClass,
2870
- metricTilesGridColsClass(metrics.length),
3555
+ metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
2871
3556
  hasHeader && "mt-3"
2872
3557
  ),
2873
- children: metrics.map((m, index) => /* @__PURE__ */ jsx44(
3558
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs36(
3559
+ "div",
3560
+ {
3561
+ className: "flex min-w-0 flex-1 flex-col gap-2 px-4 py-3",
3562
+ "aria-hidden": true,
3563
+ children: [
3564
+ /* @__PURE__ */ jsx46(Skeleton, { className: "h-3 w-20" }),
3565
+ /* @__PURE__ */ jsx46(Skeleton, { className: "h-7 w-24" })
3566
+ ]
3567
+ },
3568
+ `skeleton-${index}`
3569
+ )) : metrics.map((m, index) => /* @__PURE__ */ jsx46(
2874
3570
  MetricTile,
2875
3571
  {
2876
3572
  label: m.label,
@@ -2886,7 +3582,14 @@ var MetricChartCard = ({
2886
3582
  ))
2887
3583
  }
2888
3584
  ),
2889
- /* @__PURE__ */ jsx44("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx44(
3585
+ /* @__PURE__ */ jsx46("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx46(
3586
+ Skeleton,
3587
+ {
3588
+ className: "w-full rounded-lg",
3589
+ style: { height },
3590
+ "aria-hidden": true
3591
+ }
3592
+ ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx46(
2890
3593
  LineAreaChart,
2891
3594
  {
2892
3595
  data: active.data,
@@ -2906,7 +3609,7 @@ var MetricChartCard = ({
2906
3609
  ariaLabel: chartAriaLabel
2907
3610
  },
2908
3611
  active.id
2909
- ) : /* @__PURE__ */ jsx44(
3612
+ ) : /* @__PURE__ */ jsx46(
2910
3613
  "div",
2911
3614
  {
2912
3615
  className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
@@ -2920,9 +3623,99 @@ var MetricChartCard = ({
2920
3623
  );
2921
3624
  };
2922
3625
 
3626
+ // src/hooks/use-live-query.ts
3627
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState7 } from "react";
3628
+ function useInterval(callback, delayMs) {
3629
+ const saved = useRef(callback);
3630
+ useEffect3(() => {
3631
+ saved.current = callback;
3632
+ }, [callback]);
3633
+ useEffect3(() => {
3634
+ if (delayMs === null) return;
3635
+ const id = setInterval(() => saved.current(), delayMs);
3636
+ return () => clearInterval(id);
3637
+ }, [delayMs]);
3638
+ }
3639
+ function useLiveQuery(fetcher, options = {}) {
3640
+ const {
3641
+ intervalMs = null,
3642
+ enabled = true,
3643
+ immediate = true,
3644
+ refetchOnFocus = true
3645
+ } = options;
3646
+ const [data, setData] = useState7(void 0);
3647
+ const [error, setError] = useState7(void 0);
3648
+ const [loading, setLoading] = useState7(enabled);
3649
+ const [refreshing, setRefreshing] = useState7(false);
3650
+ const [lastUpdated, setLastUpdated] = useState7(null);
3651
+ const fetcherRef = useRef(fetcher);
3652
+ useEffect3(() => {
3653
+ fetcherRef.current = fetcher;
3654
+ }, [fetcher]);
3655
+ const mounted = useRef(true);
3656
+ const requestId = useRef(0);
3657
+ const hasData = useRef(false);
3658
+ useEffect3(() => {
3659
+ mounted.current = true;
3660
+ return () => {
3661
+ mounted.current = false;
3662
+ };
3663
+ }, []);
3664
+ const run = useCallback2(() => {
3665
+ const id = ++requestId.current;
3666
+ if (hasData.current) {
3667
+ setRefreshing(true);
3668
+ } else {
3669
+ setLoading(true);
3670
+ }
3671
+ fetcherRef.current().then((result) => {
3672
+ if (!mounted.current || id !== requestId.current) return;
3673
+ setData(result);
3674
+ setError(void 0);
3675
+ setLastUpdated(Date.now());
3676
+ hasData.current = true;
3677
+ }).catch((err) => {
3678
+ if (!mounted.current || id !== requestId.current) return;
3679
+ setError(err);
3680
+ }).finally(() => {
3681
+ if (!mounted.current || id !== requestId.current) return;
3682
+ setLoading(false);
3683
+ setRefreshing(false);
3684
+ });
3685
+ }, []);
3686
+ const refetch = useCallback2(() => {
3687
+ if (!enabled) return;
3688
+ run();
3689
+ }, [enabled, run]);
3690
+ useEffect3(() => {
3691
+ if (!enabled) return;
3692
+ if (immediate) run();
3693
+ }, [enabled, immediate, run]);
3694
+ useEffect3(() => {
3695
+ if (!enabled || intervalMs === null) return;
3696
+ const tick = () => {
3697
+ if (refetchOnFocus && typeof document !== "undefined" && document.visibilityState === "hidden") {
3698
+ return;
3699
+ }
3700
+ run();
3701
+ };
3702
+ const handle = setInterval(tick, intervalMs);
3703
+ return () => clearInterval(handle);
3704
+ }, [enabled, intervalMs, refetchOnFocus, run]);
3705
+ useEffect3(() => {
3706
+ if (!enabled || !refetchOnFocus || typeof document === "undefined") return;
3707
+ const onVisible = () => {
3708
+ if (document.visibilityState === "visible") run();
3709
+ };
3710
+ document.addEventListener("visibilitychange", onVisible);
3711
+ return () => document.removeEventListener("visibilitychange", onVisible);
3712
+ }, [enabled, refetchOnFocus, run]);
3713
+ return { data, error, loading, refreshing, lastUpdated, refetch };
3714
+ }
3715
+
2923
3716
  // src/charts/sparkline.tsx
2924
3717
  import { useId as useId7 } from "react";
2925
- import { Fragment as Fragment6, jsx as jsx45, jsxs as jsxs36 } from "react/jsx-runtime";
3718
+ import { Fragment as Fragment6, jsx as jsx47, jsxs as jsxs37 } from "react/jsx-runtime";
2926
3719
  var Sparkline = ({
2927
3720
  data,
2928
3721
  dataKey = "value",
@@ -2937,7 +3730,7 @@ var Sparkline = ({
2937
3730
  const uid = useId7();
2938
3731
  const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
2939
3732
  if (values.length === 0) {
2940
- return /* @__PURE__ */ jsx45("span", { className: cn("inline-block", className), style: { width, height } });
3733
+ return /* @__PURE__ */ jsx47("span", { className: cn("inline-block", className), style: { width, height } });
2941
3734
  }
2942
3735
  const pad = strokeWidth + 1;
2943
3736
  const min = Math.min(...values);
@@ -2949,7 +3742,7 @@ var Sparkline = ({
2949
3742
  x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
2950
3743
  y: pad + innerH - (v - min) / range * innerH
2951
3744
  }));
2952
- return /* @__PURE__ */ jsxs36(
3745
+ return /* @__PURE__ */ jsxs37(
2953
3746
  "svg",
2954
3747
  {
2955
3748
  width,
@@ -2960,14 +3753,14 @@ var Sparkline = ({
2960
3753
  "aria-label": ariaLabel,
2961
3754
  preserveAspectRatio: "none",
2962
3755
  children: [
2963
- area && /* @__PURE__ */ jsxs36(Fragment6, { children: [
2964
- /* @__PURE__ */ jsx45("defs", { children: /* @__PURE__ */ jsxs36("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
2965
- /* @__PURE__ */ jsx45("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
2966
- /* @__PURE__ */ jsx45("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
3756
+ area && /* @__PURE__ */ jsxs37(Fragment6, { children: [
3757
+ /* @__PURE__ */ jsx47("defs", { children: /* @__PURE__ */ jsxs37("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
3758
+ /* @__PURE__ */ jsx47("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
3759
+ /* @__PURE__ */ jsx47("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
2967
3760
  ] }) }),
2968
- /* @__PURE__ */ jsx45("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
3761
+ /* @__PURE__ */ jsx47("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
2969
3762
  ] }),
2970
- /* @__PURE__ */ jsx45(
3763
+ /* @__PURE__ */ jsx47(
2971
3764
  "path",
2972
3765
  {
2973
3766
  d: monotoneLinePath(points),
@@ -2984,7 +3777,17 @@ var Sparkline = ({
2984
3777
  };
2985
3778
 
2986
3779
  export {
3780
+ SEMANTIC_COLOR_TOKENS,
3781
+ RESERVED_GRADIENT_TOKENS,
3782
+ TAILWIND_PALETTE_COLORS,
3783
+ COLOR_UTILITY_PREFIXES,
3784
+ SLOP_BUDGETS,
3785
+ HOUSE_RULES,
2987
3786
  APP_KIT_AGENT_INSTRUCTIONS,
3787
+ lintGeneratedUi,
3788
+ formatLintReport,
3789
+ reviewGeneratedUi,
3790
+ UI_REVIEW_AGENT_INSTRUCTIONS,
2988
3791
  createTimbalTheme,
2989
3792
  themeToCss,
2990
3793
  ensureThemeFontLink,
@@ -3006,9 +3809,11 @@ export {
3006
3809
  appFilterBarClass,
3007
3810
  appSearchInputClass,
3008
3811
  useAppShellChat,
3812
+ useAppShellNav,
3009
3813
  AppShell,
3010
3814
  AppShellTopbar,
3011
3815
  AppShellChatTrigger,
3816
+ AppShellSidebarTrigger,
3012
3817
  PageHeader,
3013
3818
  Page,
3014
3819
  Section,
@@ -3048,9 +3853,12 @@ export {
3048
3853
  SearchInput,
3049
3854
  FormSection,
3050
3855
  FilterBar,
3856
+ FilterField,
3051
3857
  DataTable,
3052
3858
  ChartPanel,
3053
3859
  MetricRow,
3054
3860
  MetricChartCard,
3861
+ useInterval,
3862
+ useLiveQuery,
3055
3863
  Sparkline
3056
3864
  };