@timbal-ai/timbal-react 1.4.0 → 1.6.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 (57) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +43 -4
  3. package/dist/app.cjs +3770 -1506
  4. package/dist/app.d.cts +76 -31
  5. package/dist/app.d.ts +76 -31
  6. package/dist/app.esm.js +30 -8
  7. package/dist/{chart-artifact-C8-Py6lc.d.cts → chart-artifact-C2pZQsaP.d.ts} +247 -41
  8. package/dist/{chart-artifact-CMnDys2t.d.ts → chart-artifact-VAqgH-My.d.cts} +247 -41
  9. package/dist/{chat-ClmzWzCX.d.cts → chat-DDsp-Vzz.d.cts} +1 -1
  10. package/dist/{chat-ClmzWzCX.d.ts → chat-DDsp-Vzz.d.ts} +1 -1
  11. package/dist/chat.cjs +280 -123
  12. package/dist/chat.d.cts +3 -3
  13. package/dist/chat.d.ts +3 -3
  14. package/dist/chat.esm.js +4 -3
  15. package/dist/chunk-24B4I4XC.esm.js +232 -0
  16. package/dist/{chunk-VOWNCS3F.esm.js → chunk-6SQMTBPL.esm.js} +1669 -504
  17. package/dist/chunk-EDEKQYSU.esm.js +10 -0
  18. package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
  19. package/dist/{chunk-THBA27QY.esm.js → chunk-HSL36SJ4.esm.js} +243 -124
  20. package/dist/chunk-JJOO4PR5.esm.js +391 -0
  21. package/dist/{chunk-QU7ET55D.esm.js → chunk-MBS7XHV2.esm.js} +335 -192
  22. package/dist/chunk-NO5AWNWT.esm.js +1066 -0
  23. package/dist/{chunk-VXMM2HX7.esm.js → chunk-R4RQT2XQ.esm.js} +3 -3
  24. package/dist/{chunk-OFWC4MIY.esm.js → chunk-TMP7RIA7.esm.js} +5 -3
  25. package/dist/{chunk-GQBYZRD7.esm.js → chunk-WQIQW7EM.esm.js} +40 -28
  26. package/dist/{chunk-OH23AX2V.esm.js → chunk-YYEI6XME.esm.js} +441 -957
  27. package/dist/{circular-progress-Ci8L-Hfa.d.cts → circular-progress-B9nnwzCu.d.cts} +20 -78
  28. package/dist/{circular-progress-Ci8L-Hfa.d.ts → circular-progress-B9nnwzCu.d.ts} +20 -78
  29. package/dist/index.cjs +5547 -3192
  30. package/dist/index.d.cts +10 -8
  31. package/dist/index.d.ts +10 -8
  32. package/dist/index.esm.js +76 -44
  33. package/dist/kanban-FFBeaZPS.d.cts +212 -0
  34. package/dist/kanban-FFBeaZPS.d.ts +212 -0
  35. package/dist/{layout-BTJyU8wd.d.ts → layout-CuKeSY74.d.ts} +1 -1
  36. package/dist/{layout-C2G-FcER.d.cts → layout-PzVwkJyL.d.cts} +1 -1
  37. package/dist/site.cjs +429 -0
  38. package/dist/site.d.cts +198 -0
  39. package/dist/site.d.ts +198 -0
  40. package/dist/site.esm.js +23 -0
  41. package/dist/studio.cjs +722 -363
  42. package/dist/studio.d.cts +2 -2
  43. package/dist/studio.d.ts +2 -2
  44. package/dist/studio.esm.js +8 -6
  45. package/dist/styles.css +56 -0
  46. package/dist/{timbal-v2-button-CNfdwGq4.d.cts → timbal-v2-button-DCAZNyUx.d.cts} +3 -3
  47. package/dist/{timbal-v2-button-CNfdwGq4.d.ts → timbal-v2-button-DCAZNyUx.d.ts} +3 -3
  48. package/dist/ui.cjs +1553 -708
  49. package/dist/ui.d.cts +11 -4
  50. package/dist/ui.d.ts +11 -4
  51. package/dist/ui.esm.js +45 -36
  52. package/dist/{welcome-DXqsGTwH.d.ts → welcome-B00oH5Io.d.cts} +5 -1
  53. package/dist/{welcome-BFGRoNfK.d.cts → welcome-DU-4NTjZ.d.ts} +5 -1
  54. package/package.json +9 -1
  55. package/dist/button-BoyX5pM_.d.cts +0 -18
  56. package/dist/button-BoyX5pM_.d.ts +0 -18
  57. package/dist/chunk-UCGVL7ZY.esm.js +0 -52
@@ -4,38 +4,51 @@ import {
4
4
  ShellInsetProvider,
5
5
  studioChromeShellStyle,
6
6
  studioSidebarWidthTransition
7
- } from "./chunk-QIABF4KB.esm.js";
7
+ } from "./chunk-ELEY66OH.esm.js";
8
8
  import {
9
9
  ChartArtifactView,
10
10
  LineAreaChart,
11
11
  Thread,
12
12
  TimbalRuntimeProvider,
13
+ formatCompact,
13
14
  monotoneAreaPath,
14
15
  monotoneLinePath,
15
16
  studioIntegrationCardClass,
16
17
  studioTopbarPillHeightClass,
17
18
  toNum
18
- } from "./chunk-GQBYZRD7.esm.js";
19
+ } from "./chunk-WQIQW7EM.esm.js";
19
20
  import {
20
21
  Checkbox,
22
+ CopyButton,
23
+ Popover,
24
+ PopoverContent,
25
+ PopoverTrigger,
26
+ Select,
27
+ SelectContent,
28
+ SelectItem,
29
+ SelectTrigger,
30
+ SelectValue,
21
31
  Skeleton
22
- } from "./chunk-UCGVL7ZY.esm.js";
32
+ } from "./chunk-NO5AWNWT.esm.js";
23
33
  import {
24
34
  PillSegmentedTabs
25
- } from "./chunk-VXMM2HX7.esm.js";
35
+ } from "./chunk-R4RQT2XQ.esm.js";
26
36
  import {
27
37
  Button,
28
38
  Dialog,
29
39
  DialogContent,
30
40
  DialogTitle,
41
+ TIMBAL_V2_ELEVATED_GRADIENT,
31
42
  TIMBAL_V2_ELEVATED_SURFACE,
32
43
  TIMBAL_V2_LOGO_TILE,
33
44
  TIMBAL_V2_SWITCH_THUMB,
34
45
  TIMBAL_V2_SWITCH_TRACK_OFF,
35
46
  TimbalV2Button,
36
- cn,
37
47
  controlClass
38
- } from "./chunk-QU7ET55D.esm.js";
48
+ } from "./chunk-MBS7XHV2.esm.js";
49
+ import {
50
+ cn
51
+ } from "./chunk-EDEKQYSU.esm.js";
39
52
 
40
53
  // src/design/ui-vocabulary.ts
41
54
  var SEMANTIC_COLOR_TOKENS = [
@@ -191,6 +204,13 @@ var HOUSE_RULES = [
191
204
  rule: "Don't nest a bordered card inside another bordered card. Group with spacing or a Section instead.",
192
205
  why: "Card-in-card doubles borders and shadows for no information gain."
193
206
  },
207
+ {
208
+ id: "no-table-in-card",
209
+ rule: "Never wrap a DataTable or table inside a Card, SurfaceCard, or ArtifactCard.",
210
+ why: "The DataTable is already designed to sit flat with its own borders and shadows. Wrapping it inside a card adds redundant borders, margins, and shadows, which looks like slop.",
211
+ slop: "<Card><DataTable columns={columns} rows={rows} getRowKey={getRowKey} /></Card>",
212
+ good: "<DataTable columns={columns} rows={rows} getRowKey={getRowKey} />"
213
+ },
194
214
  {
195
215
  id: "no-row-dividers",
196
216
  rule: "Don't put a divider between every list row. Use spacing or zebra striping.",
@@ -212,6 +232,27 @@ var HOUSE_RULES = [
212
232
  why: "Hand-rolled controls drift from the shared control-surface skin and look foreign next to kit controls.",
213
233
  slop: `<button className="rounded-lg border border-input bg-transparent px-3 h-9">`,
214
234
  good: `<SelectTrigger><SelectValue /></SelectTrigger>`
235
+ },
236
+ {
237
+ id: "no-title-repetition",
238
+ rule: "Never repeat the page title/description inside a Section, Card, or Table header.",
239
+ why: "If the Page already has a title, repeating it in the first child section or table is redundant and wastes vertical space.",
240
+ slop: `<Page title="Orders"><Section title="Orders"><DataTable ... /></Section></Page>`,
241
+ good: `<Page title="Orders"><DataTable ... /></Page>`
242
+ },
243
+ {
244
+ id: "no-chat-wrapping",
245
+ rule: "Never wrap TimbalChat or AppChatPanel in a Card, Section, or custom bordered/padded container, and never add custom heading/status blocks above it.",
246
+ why: "The chat component is a full-bleed, full-height surface that handles its own layout, welcome screen, and suggestions. Wrapping it or hand-rolling headers/online badges inside the page content is redundant and wastes layout space.",
247
+ slop: `<Page title="Assistant"><Card><div className="flex justify-between"><h3>TIBA Concierge</h3><span>Online</span></div><TimbalChat /></Card></Page>`,
248
+ good: `<Page fill><TimbalChat workforceId="..." welcome={{ heading: "Hola, soy el Concierge de TIBA", subheading: "Preg\xFAntame sobre..." }} /></Page>`
249
+ },
250
+ {
251
+ id: "no-colored-hover",
252
+ rule: "Interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
253
+ why: "Colored hover highlights (like hover:bg-primary/5 or hover:bg-emerald-500/5) look dirty and break the neutral chrome aesthetic. Use the kit's clean, neutral hover states, or AlertCard, or TIMBAL_V2_SECONDARY_CHROME hover layers.",
254
+ slop: `<Card className="hover:bg-emerald-500/10 hover:border-emerald-500/30">`,
255
+ good: `<AlertCard onClick={handleClick}>`
215
256
  }
216
257
  ];
217
258
 
@@ -246,14 +287,14 @@ The most common failure is shipping the **same** layout every time: sidebar + to
246
287
  | Archetype | When | Compose |
247
288
  |-----------|------|---------|
248
289
  | **Sidebar dashboard** | Multi-section product (CRM, billing, ops) with nav | \`StudioSidebar\` in \`AppShell.sidebar\` + \`Page\` \u2192 \`Section\` |
249
- | **Focused / no-chrome** | A single tool or one-screen utility | \`AppShell\` (no sidebar) + \`Page\` (optionally just \`AppShellTopbar\`); or a centered narrow column |
290
+ | **Focused / no-chrome** | A single tool or one-screen utility | \`AppShell\` (no sidebar) + \`Page width="narrow"\` / \`"prose"\` (optionally with a custom topbar) for a centered focused column |
250
291
  | **Bento overview** | Home / at-a-glance dashboards | \`Page\` + an **asymmetric grid** of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` spanning different widths (not a uniform row + table) |
251
292
  | **Split master\u2013detail** | Inbox, triage queue, record browser, log explorer | \`AppShell contentFill\` + \`Page fill\` + a two-column flex row, each pane \`min-h-0 overflow-y-auto\` |
252
293
  | **Full-page chat / canvas** | Chat-first app, editor, map, single full-bleed surface | \`AppShell contentFill\` + headerless \`Page fill\` + a \`min-h-0 flex-1\` child (e.g. \`TimbalChat\`) |
253
294
  | **Copilot overlay** | A data app that also wants an assistant | any of the above + \`AppShell chat={<AppChatPanel />}\` (floating, never a second column) |
254
295
  | **Section-switcher** | One page, several views | \`SubNav\` / \`PillSegmentedTabs\` (\`trackVariant="flush"\`) switching panels with state/router |
255
296
 
256
- Mix them: vary the grid columns, density, header placement (\`Page\` actions vs. a global \`AppShellTopbar\`), and whether there's a sidebar at all. Two dashboards for two domains should not look identical.
297
+ Mix them: vary the grid columns, density, header placement (\`Page\` actions vs. a global topbar), and whether there's a sidebar at all. Two dashboards for two domains should not look identical.
257
298
 
258
299
  ### Full-height pages (chat, canvas, split views)
259
300
 
@@ -264,7 +305,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
264
305
  - Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
265
306
 
266
307
  \`\`\`tsx
267
- <AppShell contentFill topbar={<AppShellTopbar actions={<ModeToggle />} />}>
308
+ <AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
268
309
  <Page fill> {/* headerless: omit title */}
269
310
  <TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
270
311
  </Page>
@@ -273,20 +314,25 @@ The content region is a **padded scroll area** by default \u2014 great for stack
273
314
 
274
315
  **Don't** size full-height content with \`h-[calc(100dvh-\u2026)]\` (guesses chrome height \u2192 spurious scrollbar) or \`min-h-[\u2026]\` (free-growing floor \u2192 a pinned footer like the chat composer rides down on scroll). Let \`contentFill\` + \`fill\` provide the bounded height. \`Page\` with no \`title\` renders **no header** \u2014 you don't need to abandon \`Page\` to drop a heading.
275
316
 
317
+ **Full-page Assistant Guidelines (Hardened Layout):**
318
+ When creating a full-page assistant/chat page, let the chat component own the layout and welcome state completely.
319
+ - **Never wrap** \`<TimbalChat>\` or \`<AppChatPanel>\` inside standard card or section elements (like \`<Card>\`, \`<Section>\`, or custom bordered/padded panels). Wrapping degrades the viewport height calculations and wastes valuable layout estate.
320
+ - **Never add custom headings, titles, descriptions, or status badges** (e.g., "Asistente Virtual", "Online", or subtitle text blocks) inside the page. The assistant page context is already implicit. If you need a customized title or greeting, pass it through the \`welcome\` prop config: e.g., \`<TimbalChat welcome={{ heading: "Hola, soy el Concierge de TIBA", subheading: "Preg\xFAntame sobre..." }} />\`.
321
+
276
322
  ### Module layout (source folders)
277
323
 
278
324
  Presentational groups \u2014 import from the package root, not from these paths:
279
325
 
280
326
  | Folder | Components |
281
327
  |--------|------------|
282
- | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
328
+ | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`FilterDropdown\`, \`ChartPanel\` |
283
329
  | \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
284
330
  | \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
285
- | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
286
- | \`layout/\` | \`AppShell\`, \`Page\`, \`Section\` |
331
+ | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
332
+ | \`layout/\` | \`AppShell\`, \`Page\` (auto-stacks children; \`width\` ladder), \`Section\`, \`Stack\` |
287
333
  | \`charts\` (re-exported) | \`LineAreaChart\`, \`PieChart\`, \`RadialChart\`, \`RadarChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
288
334
 
289
- Also re-exported: \`Button\`, \`TimbalChat\`, \`ChartArtifactView\`, \`APP_KIT_AGENT_INSTRUCTIONS\`.
335
+ Also re-exported from \`/app\`: \`Button\`, \`Avatar\` / \`AvatarImage\` / \`AvatarFallback\`, \`Banner\`, \`Timeline\`, \`Kanban\` (drag-and-drop board), \`TimbalChat\`, \`ChartArtifactView\`, \`APP_KIT_AGENT_INSTRUCTIONS\`. Other UI primitives used in block recipes (\`Card\`, \`Input\`, \`Label\`, \`Sheet\`, \`Separator\`, \`AlertDialog\`, \`Badge\`, \`Select\`, \u2026) import from \`@timbal-ai/timbal-react/ui\` or the package root \u2014 **not** from \`/app\`.
290
336
 
291
337
  Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\`, \`themeToCss\`, \`applyTimbalTheme\`, \`TIMBAL_THEME_PRESETS\`, \`applyThemePreset\`, \`TimbalThemeStyle\`, \`THEME_AGENT_INSTRUCTIONS\`. Theming is **configured by the developer**, not surfaced as an end-user theme picker.
292
338
 
@@ -298,9 +344,12 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
298
344
  | **Chat panel** | \`AppChatPanel\` only; \`Thread\` uses \`variant="panel"\` internally. Dismiss with **X**; trigger is a **text-only** pill (e.g. "Assistant") \u2014 **no** MessageSquare or chat icons on the shell trigger. |
299
345
  | **Context** | Do not show raw JSON context in the panel header; keep context in \`AppCopilotProvider\` only. |
300
346
  | **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\`). Apply the theme **programmatically** \u2014 do **not** add an end-user theme selector to generated apps. See \`THEME_AGENT_INSTRUCTIONS\`. |
301
- | **Layout chrome** | \`Page\` \u2192 \`Section\` for main content hierarchy. Default to **no global topbar** \u2014 put account/theme/global actions in the \`Page\` \`actions\` slot (or the sidebar). Add \`AppShellTopbar\` only when a full-width global bar is explicitly requested. |
347
+ | **Layout chrome** | \`Page\` \u2192 \`Section\` for main content hierarchy. Default to **no global topbar** \u2014 put account/theme/global actions in the \`Page\` \`actions\` slot (or the sidebar). Add a topbar only when a full-width global bar is explicitly requested. |
348
+ | **Spacing / gaps** | \`Page\` **auto-stacks its direct children with a vertical gap** \u2014 drop blocks straight in (e.g. \`Page\` \u2192 \`FilterBar\` + \`DataTable\`, or \`MetricRow\` + \`ChartPanel\`) and they breathe; do **not** wrap every block in an extra \`<div>\` (that collapses the gap). For ad-hoc clusters inside a card/row use \`Stack\` (\`gap\`, \`direction\`) instead of bare flex with no gap. Grids still need their own \`gap-*\`. |
349
+ | **Width** | \`Page\` defaults to a wide centered column. For focused / reading / form pages pass \`width\` (\`default\`, \`centered\`, \`narrow\`, \`prose\`) instead of always running full-bleed \u2014 not everything needs the full width. \`width="full"\` opts into edge-to-edge. For full-height pages that should stay centered use \`fill\` + \`fillPadded\`. |
302
350
  | **Density** | Set \`density="compact"\` on \`Page\` for tighter dashboards (full-width column, smaller section gaps, card padding, metric tiles, default chart height 220). Default is \`"default"\` (platform spacing). Wrap custom layouts with \`AppDensityProvider\` when not using \`Page\`. Per-section override: \`Section density="compact"\`. Do **not** hand-tune five layers of \`className\` padding when density covers the need. |
303
351
  | **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. |
352
+ | **Boards** | For status/triage workflows (pipelines, sprint boards, review queues) use \`Kanban\` \u2014 pass \`columns\` (each with \`cards\`) + \`renderCard\`; handle \`onColumnsChange\`/\`onMove\` to persist. It's drag-and-drop **and** keyboard accessible; don't hand-roll columns of cards. |
304
353
  | **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
305
354
  | **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
306
355
  | **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
@@ -332,7 +381,6 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
332
381
  | Component | Use for |
333
382
  |-----------|---------|
334
383
  | \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen\`, **\`contentFill\`** (bounded non-scrolling content region for full-bleed pages \u2014 chat/canvas/split view). |
335
- | \`AppShellTopbar\` | Full-width top bar: \`start\`, \`actions\` slots. |
336
384
  | \`AppCopilotProvider\` | React context for copilot-aware tools (page, filters, selection, etc.). |
337
385
  | \`AppChatPanel\` | Floating thread: \`workforceId\`, \`welcome\`, \`debug\`. |
338
386
  | \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
@@ -341,16 +389,17 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
341
389
  | \`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. |
342
390
  | **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\`. |
343
391
  | \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
344
- | \`Button\` | Actions \u2014 \`variant="secondary"\` for catalog/secondary CTAs; \`variant="default"\` for primary. |
392
+ | \`Button\` | Actions. Untitled UI-style \`color\` alias (\`primary\` \\| \`secondary\` \\| \`tertiary\` \\| \`link\` \\| \`primary-destructive\` \\| \`secondary-destructive\`) + \`iconLeading\` / \`iconTrailing\`; or legacy \`variant="secondary"\` (catalog CTAs) / \`variant="default"\` (primary). \`color="primary-destructive"\` is the solid red delete CTA. |
345
393
  | \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
346
394
  | \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
347
395
  | \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
348
396
  | \`FilterField\` | Optional label wrapper for a filter control inside \`FilterBar\` (severity, status, \u2026). Omit \`label\` for search-only fields. |
397
+ | \`FilterDropdown\` | Single-button **multi-facet** filter popover for dense list/table views \u2014 **data-driven**: pass \`fields\` describing your **actual columns** (each \`{ id, label, type }\` where \`type\` is \`multiselect\` \\| \`text\` \\| \`daterange\` \\| \`numeric\`; \`multiselect\` takes \`options: [{ value, label, hint?, icon? }]\`). State is keyed by field \`id\` \u2014 controlled (\`value\` + \`onChange\`) or uncontrolled (\`defaultValue\`). Renders **removable active-filter pills** next to the trigger by default (\`showActiveChips\`); wire \`onChange\` to actually filter your rows. **Always derive \`fields\` from the table's columns/data; never ship the default example facets.** Use when one \`FilterBar\` row isn't enough. |
349
398
  | \`SearchInput\` | Filter field with consistent app styling. |
350
399
  | \`DataTable\` | Sortable table: \`columns\`, \`rows\`, \`getRowKey\`, optional \`sort\` / \`onSortChange\`, \`emptyTitle\`, \`showRowCount\`, \`caption\`, \`truncate: true\` on columns with long text. **Scales:** \`pageSize\` (built-in client pager), \`selectable\` + \`onSelectionChange\` (checkbox column for bulk actions), \`loading\` (skeleton rows). \`onRowClick\` for row \u2192 detail (open a \`Sheet\`). |
351
400
  | \`Avatar\` / \`AvatarFallback\` | User initials: \`variant="secondary"\` (or \`primary\` / \`chart\` alias) on **both** \`Avatar\` and \`AvatarFallback\` \u2014 same chrome as catalog **Action** buttons (\`Button variant="secondary"\`: elevated gradient, \`border-border\`, \`shadow-card\`, \`text-foreground\`). Never dark primary CTA fill or raw \`bg-blue-600\`. |
352
401
  | \`ChartPanel\` | Same shell as \`MetricChartCard\`: title row (\`px-4 pt-4\`), flush plot (\`pt-2\` only) with **no axis ticks** \u2014 hover tooltips show category + value. Pass \`title\` + \`artifact\` (omit \`artifact.title\` to avoid duplicates) or \`children\`. \`loading\` renders a plot-height skeleton. Default plot height follows page \`density\` (300 default, 220 compact); pass \`height\` to override. |
353
- | \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. |
402
+ | \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. **\`label\` is required** on every \`Field*\` component; for a label-less filter control use \`FilterField\` (optional label) or \`SearchInput\`. |
354
403
  | \`FormSection\` | Grouped form block. |
355
404
  | \`AppConfirmDialog\` | Confirm/cancel modal: \`open\`, \`onOpenChange\`, \`title\`, \`description\`, \`onConfirm\`. |
356
405
  | \`SurfaceCard\`, \`EmptyState\` | Generic surfaces when needed. |
@@ -396,9 +445,28 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
396
445
  | Component | Use for |
397
446
  |-----------|---------|
398
447
  | \`InfoCard\` | Soft callout: \`icon\`, \`title\`, body, \`action\`, \`tone\` (\`info\`/\`success\`/\`warn\`/\`danger\`). |
448
+ | \`AlertCard\` | Actionable / AI-generated alert card with integrated status tags, clear titles, descriptive bodies, and automated action footers. Supports: \`title\`, \`description?\`, \`category?\`, \`categoryTone?\`, \`status?\`, \`statusTone?\`, \`action?\` (action description text), \`trailing?\` (custom element), and \`onClick?\`. Renders with a beautiful neutral hover state, avoiding loud/colored hover background effects. |
449
+ | \`CatalogCard\` | Highly sophisticated marketplace/routing catalog tile (models, datasets, tools). Supports: \`title\`, \`subtitle?\`, \`logo?\` (e.g. brand logo), \`badge?\` (e.g. model type badge), \`description?\`, \`tags?\` (metadata array), \`href?\` (title link), \`footerLinks?\` (bottom-left links), \`copyValue?\` (copy ID button), \`actions?\`, and \`onClick?\` (neutral-hover interactive state). |
399
450
  | \`DescriptionList\` | Read-only key/value metadata: \`items: [{ label, value }]\`, optional \`stacked\`. |
400
451
  | \`ExpandableSection\` | Collapsible block: \`title\`, \`icon\`, \`count\`, animated body (\`aria-expanded\` + \`aria-controls\`). |
401
452
  | \`StatusDot\` | Status indicator dot: \`tone\`, \`label\`, \`pulse\`. |
453
+ | \`Banner\` | Page-level announcement bar: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`), \`icon\`, \`title\`, body as children, right-aligned \`actions\`, \`onDismiss\` (renders the dismiss X). For in-form/field messages use \`InfoCard\` or \`Alert\` instead. |
454
+ | \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
455
+
456
+ #### More \`/ui\` primitives (import from \`/ui\` or the package root)
457
+
458
+ These ship in the same design system but aren't re-exported from \`/app\`. Reach for them before hand-rolling \u2014 they're all dependency-free and on the shared tokens / control surface.
459
+
460
+ | Component | Use for |
461
+ |-----------|---------|
462
+ | \`Stepper\` | Ordered step indicator for wizards / onboarding (horizontal or vertical; complete / active / upcoming states). |
463
+ | \`Rating\` | Star rating \u2014 interactive (keyboard + hover preview) or \`readOnly\`; controlled or uncontrolled. |
464
+ | \`NumberField\` | Numeric input with \u2212/+ steppers on the control surface; clamps to \`min\`/\`max\`, steps by \`step\`. |
465
+ | \`TagInput\` | Chips / token input; commits on Enter/comma, removes on Backspace, optional \`dedupe\`/\`max\`. |
466
+ | \`AvatarGroup\` | Overlapping avatar stack with an optional \`+N\` overflow chip (\`max\`, \`spacing\`). |
467
+ | \`CircularProgress\` | Lightweight SVG progress ring \u2014 determinate (optional center label) or indeterminate. |
468
+ | \`CopyButton\` | Click-to-copy with a transient check confirmation; icon-only or with a label. |
469
+ | \`Snippet\` | Single-line code / command on the elevated surface with a built-in copy button. |
402
470
 
403
471
  Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
404
472
 
@@ -466,6 +534,9 @@ import {
466
534
  DataTable,
467
535
  FilterBar,
468
536
  FilterField,
537
+ FilterDropdown,
538
+ AlertCard,
539
+ CatalogCard,
469
540
  } from "@timbal-ai/timbal-react/app";
470
541
  \`\`\`
471
542
 
@@ -476,6 +547,17 @@ import {
476
547
  | \`examples/app-kit/recipes/*\` | **Recipes** \u2014 one pattern each (~20\u201380 lines). Use for capability, not layout. |
477
548
  | \`examples/app-kit/reference/operations-dashboard.tsx\` | **Reference only** \u2014 full wired app; do not treat as the default generated layout. |
478
549
 
550
+ ### API gotchas \u2014 props that do NOT exist (don't guess, don't retry variations)
551
+
552
+ The compiler rejects these every time; write against the documented shapes above instead:
553
+
554
+ - \`FieldInput\` / \`FieldTextarea\` / \`FieldSelect\` / \`FieldSwitch\` **require \`label\`** \u2014 TS2741 if omitted. Label-less control \u2192 \`FilterField\` or \`SearchInput\`.
555
+ - Full-height layout: \`fill\` lives on \`Page\` (paired with \`AppShell contentFill\`) \u2014 there is no \`fill\` on \`Section\` or \`AppShell\`.
556
+ - \`WorkforceSelector\` (chat subpath / root) is a **controlled** picker: \`workforces\` (e.g. from \`useWorkforces()\`), \`value\`, \`onChange(id)\`, optional \`hideWhenSingle\` / \`placeholder\`. It does **not** fetch and has no \`workforceId\` prop \u2014 for an embedded assistant use \`AppChatPanel workforceId="\u2026"\`.
557
+ - There is **no \`Tabs\` export** \u2014 section switching uses \`SubNav\` or \`PillSegmentedTabs\`.
558
+ - \`Banner\` and \`Timeline\` exist (see menu) \u2014 import them from \`/app\`, \`/ui\`, or the root.
559
+ - If a prop still type-errors, read the actual definitions in \`node_modules/@timbal-ai/timbal-react/dist/app.d.ts\` once instead of retrying guessed prop names.
560
+
479
561
  ### Rules
480
562
 
481
563
  - Prefer stable props documented above; avoid undocumented \`design/*\` class exports (\`connectionRowListClass\` is exported but \`ConnectionRowList\` is preferred).
@@ -506,6 +588,7 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
506
588
  ]);
507
589
  var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
508
590
  var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
591
+ var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
509
592
  var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
510
593
  function stripVariants(util) {
511
594
  return util.replace(/^(?:[a-z-]+:)*/, "");
@@ -522,7 +605,14 @@ function lintGeneratedUi(source, options = {}) {
522
605
  let usesLucide = false;
523
606
  let iconUsageCount = 0;
524
607
  let dividerRunCount = 0;
608
+ let pageTitle = null;
609
+ const pageTitleMatch = source.match(/<Page\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
610
+ if (pageTitleMatch) {
611
+ pageTitle = (pageTitleMatch[1] || pageTitleMatch[2]).trim().toLowerCase();
612
+ }
613
+ const hasChat = /\b(?:TimbalChat|AppChatPanel|Thread)\b/.test(source);
525
614
  const lucideNames = /* @__PURE__ */ new Set();
615
+ const openCards = [];
526
616
  for (let i = 0; i < lines.length; i++) {
527
617
  const line = lines[i];
528
618
  const lineNo = i + 1;
@@ -538,6 +628,33 @@ function lintGeneratedUi(source, options = {}) {
538
628
  continue;
539
629
  }
540
630
  if (isCommentOrImport(line)) continue;
631
+ const cardMatch = line.match(/<(Card|SurfaceCard|ArtifactCard)\b/);
632
+ if (cardMatch) {
633
+ const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
634
+ if (!isSelfClosing) {
635
+ openCards.push({ type: cardMatch[1], line: lineNo });
636
+ }
637
+ }
638
+ const closeMatch = line.match(/<\/(Card|SurfaceCard|ArtifactCard)\b/);
639
+ if (closeMatch && openCards.length > 0) {
640
+ const idx = openCards.map((c) => c.type).lastIndexOf(closeMatch[1]);
641
+ if (idx !== -1) {
642
+ openCards.splice(idx, 1);
643
+ }
644
+ }
645
+ if (openCards.length > 0) {
646
+ const tableMatch = line.match(/<(DataTable|table|Table)\b/);
647
+ if (tableMatch) {
648
+ const parentCard = openCards[openCards.length - 1];
649
+ findings.push({
650
+ rule: "no-table-in-card",
651
+ severity: "error",
652
+ line: lineNo,
653
+ message: `Table inside card. Never wrap a <${tableMatch[1]}> or table inside a <${parentCard.type}> (opened on L${parentCard.line}). Place the table directly on the Page or Section instead.`,
654
+ snippet: line.trim().slice(0, 120)
655
+ });
656
+ }
657
+ }
541
658
  const rawColors = line.match(RAW_COLOR_RE);
542
659
  if (rawColors) {
543
660
  for (const m of rawColors) {
@@ -578,6 +695,15 @@ function lintGeneratedUi(source, options = {}) {
578
695
  snippet: line.trim().slice(0, 120)
579
696
  });
580
697
  }
698
+ if (COLORED_HOVER_RE.test(line)) {
699
+ findings.push({
700
+ rule: "no-colored-hover",
701
+ severity: "warn",
702
+ line: lineNo,
703
+ message: "Colored hover background/gradient. House style: interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
704
+ snippet: line.trim().slice(0, 120)
705
+ });
706
+ }
581
707
  if (BOLD_VALUE_RE.test(line)) {
582
708
  findings.push({
583
709
  rule: "bold-metric",
@@ -624,6 +750,44 @@ function lintGeneratedUi(source, options = {}) {
624
750
  if (hits) iconUsageCount += hits.length;
625
751
  }
626
752
  }
753
+ if (pageTitle) {
754
+ const titleMatch = line.match(/<(Section|ChartPanel|Card|DataTable|SurfaceCard)\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
755
+ if (titleMatch) {
756
+ const element = titleMatch[1];
757
+ const titleVal = (titleMatch[2] || titleMatch[3]).trim().toLowerCase();
758
+ if (titleVal === pageTitle || titleVal.includes(pageTitle) || pageTitle.includes(titleVal)) {
759
+ findings.push({
760
+ rule: "no-title-repetition",
761
+ severity: "warn",
762
+ line: lineNo,
763
+ message: `Title repetition. The <${element}> title "${titleVal}" repeats or is very similar to the <Page> title "${pageTitle}". Drop the title from the child element or use a title-less Section to avoid redundant headings.`,
764
+ snippet: line.trim().slice(0, 120)
765
+ });
766
+ }
767
+ }
768
+ }
769
+ if (hasChat) {
770
+ const wrappingMatch = line.match(/<(Card|Section|SurfaceCard|FormSection|SettingsSection)\b/);
771
+ if (wrappingMatch) {
772
+ findings.push({
773
+ rule: "no-chat-wrapping",
774
+ severity: "error",
775
+ line: lineNo,
776
+ message: `Chat component wrapping. Never wrap TimbalChat or AppChatPanel inside a <${wrappingMatch[1]}> or custom bordered container. Let the chat component fill the page or slot directly.`,
777
+ snippet: line.trim().slice(0, 120)
778
+ });
779
+ }
780
+ const headingMatch = line.match(/<(h[1-6])\b/);
781
+ if (headingMatch) {
782
+ findings.push({
783
+ rule: "no-chat-wrapping",
784
+ severity: "error",
785
+ line: lineNo,
786
+ message: `Custom heading in chat view. Do not render custom <${headingMatch[1]}> headings on the chat page. Pass welcome.heading to TimbalChat if you need to customize the welcome title.`,
787
+ snippet: line.trim().slice(0, 120)
788
+ });
789
+ }
790
+ }
627
791
  }
628
792
  if (usesLucide && iconUsageCount > maxIcons) {
629
793
  findings.push({
@@ -1356,7 +1520,20 @@ var TimbalThemeStyle = ({
1356
1520
 
1357
1521
  // src/design/app-classes.ts
1358
1522
  var appPageColumnClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
1359
- var appShellTopbarInsetClass = "w-full px-4 md:px-6";
1523
+ var PAGE_WIDTH_MAXW = {
1524
+ full: "max-w-none",
1525
+ wide: "max-w-[100rem]",
1526
+ default: "max-w-7xl",
1527
+ centered: "max-w-5xl",
1528
+ narrow: "max-w-3xl",
1529
+ prose: "max-w-2xl"
1530
+ };
1531
+ function appPageColumn(width, density = "default") {
1532
+ const lateral = density === "compact" ? "px-3 md:px-4" : "px-4 md:px-6 lg:px-8";
1533
+ const maxW = width ? PAGE_WIDTH_MAXW[width] : density === "compact" ? "max-w-none" : "max-w-[100rem]";
1534
+ return cn("mx-auto w-full", maxW, lateral);
1535
+ }
1536
+ var appShellTopbarInsetClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
1360
1537
  var appShellInsetTopClass = "pt-4 md:pt-6";
1361
1538
  var appShellInsetBottomClass = "pb-8 md:pb-10";
1362
1539
  var appShellTopbarRowClass = cn(
@@ -1423,6 +1600,15 @@ var APP_DENSITY_CLASSES = {
1423
1600
  default: appPageHeaderClass,
1424
1601
  compact: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between pb-2 pt-1"
1425
1602
  },
1603
+ /**
1604
+ * Vertical rhythm between a `Page`'s direct content blocks. Page-level gap so
1605
+ * stacked blocks (FilterBar + DataTable, MetricRow + ChartPanel, bare cards)
1606
+ * never render flush — the #1 source of gap-less generated layouts.
1607
+ */
1608
+ pageStack: {
1609
+ default: "flex flex-col gap-6",
1610
+ compact: "flex flex-col gap-4"
1611
+ },
1426
1612
  section: {
1427
1613
  default: appSectionClass,
1428
1614
  compact: "flex flex-col gap-2 py-2"
@@ -1448,12 +1634,12 @@ var APP_DENSITY_CLASSES = {
1448
1634
  compact: "relative flex min-w-0 flex-1 flex-col gap-1 px-3 py-2 text-left font-normal"
1449
1635
  },
1450
1636
  metricChartRegion: {
1451
- default: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 pt-2",
1452
- compact: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 pt-1"
1637
+ default: "relative min-h-0 w-full overflow-x-hidden pt-2",
1638
+ compact: "relative min-h-0 w-full overflow-x-hidden pt-1"
1453
1639
  },
1454
1640
  metricChartPlotRegion: {
1455
- default: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 px-0 pt-5 pb-3",
1456
- compact: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 px-0 pt-3 pb-2"
1641
+ default: "relative min-h-0 w-full overflow-x-hidden px-0 pt-5 pb-3",
1642
+ compact: "relative min-h-0 w-full overflow-x-hidden px-0 pt-3 pb-2"
1457
1643
  },
1458
1644
  chartPanelBody: {
1459
1645
  default: "pt-2 pb-3",
@@ -1482,8 +1668,155 @@ function useAppDensityClass(key, override) {
1482
1668
  return appDensityClass(key, override ?? inherited);
1483
1669
  }
1484
1670
 
1671
+ // src/charts/sparkline.tsx
1672
+ import {
1673
+ useId,
1674
+ useRef,
1675
+ useState
1676
+ } from "react";
1677
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1678
+ var Sparkline = ({
1679
+ data,
1680
+ dataKey = "value",
1681
+ color = "var(--primary, #6366f1)",
1682
+ area = true,
1683
+ width = 96,
1684
+ height = 28,
1685
+ strokeWidth = 1.5,
1686
+ className,
1687
+ ariaLabel = "Trend",
1688
+ interactive = false,
1689
+ labels,
1690
+ formatValue
1691
+ }) => {
1692
+ const uid = useId();
1693
+ const containerRef = useRef(null);
1694
+ const [activeIndex, setActiveIndex] = useState(null);
1695
+ const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
1696
+ if (values.length === 0) {
1697
+ return /* @__PURE__ */ jsx3("span", { className: cn("inline-block", className), style: { width, height } });
1698
+ }
1699
+ const padX = 0;
1700
+ const padY = strokeWidth + 1;
1701
+ const min = Math.min(...values);
1702
+ const max = Math.max(...values);
1703
+ const range = max - min || 1;
1704
+ const innerW = width - padX * 2;
1705
+ const innerH = height - padY * 2;
1706
+ const points = values.map((v, i) => ({
1707
+ x: padX + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
1708
+ y: padY + innerH - (v - min) / range * innerH
1709
+ }));
1710
+ const svg = /* @__PURE__ */ jsxs2(
1711
+ "svg",
1712
+ {
1713
+ width,
1714
+ height,
1715
+ viewBox: `0 0 ${width} ${height}`,
1716
+ className: cn("block", interactive ? "h-full w-full" : className),
1717
+ role: "img",
1718
+ "aria-label": ariaLabel,
1719
+ preserveAspectRatio: "none",
1720
+ children: [
1721
+ area && /* @__PURE__ */ jsxs2(Fragment2, { children: [
1722
+ /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
1723
+ /* @__PURE__ */ jsx3("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
1724
+ /* @__PURE__ */ jsx3("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
1725
+ ] }) }),
1726
+ /* @__PURE__ */ jsx3("path", { d: monotoneAreaPath(points, height - padY), fill: `url(#${uid}-spark)` })
1727
+ ] }),
1728
+ /* @__PURE__ */ jsx3(
1729
+ "path",
1730
+ {
1731
+ d: monotoneLinePath(points),
1732
+ fill: "none",
1733
+ stroke: color,
1734
+ strokeWidth,
1735
+ strokeLinecap: "round",
1736
+ strokeLinejoin: "round"
1737
+ }
1738
+ ),
1739
+ interactive && activeIndex != null && points[activeIndex] ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1740
+ /* @__PURE__ */ jsx3(
1741
+ "line",
1742
+ {
1743
+ x1: points[activeIndex].x,
1744
+ x2: points[activeIndex].x,
1745
+ y1: 0,
1746
+ y2: height,
1747
+ stroke: color,
1748
+ strokeWidth: 1,
1749
+ strokeOpacity: 0.3,
1750
+ vectorEffect: "non-scaling-stroke"
1751
+ }
1752
+ ),
1753
+ /* @__PURE__ */ jsx3(
1754
+ "circle",
1755
+ {
1756
+ cx: points[activeIndex].x,
1757
+ cy: points[activeIndex].y,
1758
+ r: 2.75,
1759
+ fill: color,
1760
+ stroke: "var(--background, #fff)",
1761
+ strokeWidth: 1.5,
1762
+ vectorEffect: "non-scaling-stroke"
1763
+ }
1764
+ )
1765
+ ] }) : null
1766
+ ]
1767
+ }
1768
+ );
1769
+ if (!interactive) return svg;
1770
+ const onMove = (e) => {
1771
+ const rect = e.currentTarget.getBoundingClientRect();
1772
+ if (rect.width === 0) return;
1773
+ const fraction = (e.clientX - rect.left) / rect.width;
1774
+ const index = Math.max(
1775
+ 0,
1776
+ Math.min(values.length - 1, Math.round(fraction * (values.length - 1)))
1777
+ );
1778
+ setActiveIndex(index);
1779
+ };
1780
+ const active = activeIndex != null ? points[activeIndex] : null;
1781
+ const formattedValue = activeIndex != null ? formatValue ? formatValue(values[activeIndex], activeIndex) : formatCompact(values[activeIndex]) : null;
1782
+ return /* @__PURE__ */ jsxs2(
1783
+ "span",
1784
+ {
1785
+ ref: containerRef,
1786
+ className: cn("relative block touch-none", className),
1787
+ style: { width: "100%", height: "100%" },
1788
+ onPointerMove: onMove,
1789
+ onPointerLeave: () => setActiveIndex(null),
1790
+ children: [
1791
+ svg,
1792
+ active ? /* @__PURE__ */ jsxs2(
1793
+ "span",
1794
+ {
1795
+ "aria-hidden": true,
1796
+ className: cn(
1797
+ "pointer-events-none absolute z-30 -translate-x-1/2 -translate-y-full whitespace-nowrap",
1798
+ "rounded-xl border px-3 py-2 text-[11px] font-medium leading-none tabular-nums shadow-[0_12px_40px_-10px_rgba(0,0,0,0.55)]",
1799
+ "border-white/10 bg-gradient-to-b from-neutral-800 to-neutral-950 text-white",
1800
+ "dark:border-black/10 dark:from-white dark:to-neutral-100 dark:text-neutral-900"
1801
+ ),
1802
+ style: {
1803
+ left: `${active.x / width * 100}%`,
1804
+ top: `${active.y / height * 100}%`,
1805
+ marginTop: -8
1806
+ },
1807
+ children: [
1808
+ labels?.[activeIndex] != null ? /* @__PURE__ */ jsx3("span", { className: "mr-1.5 text-neutral-300 dark:text-neutral-500", children: labels[activeIndex] }) : null,
1809
+ /* @__PURE__ */ jsx3("span", { children: formattedValue })
1810
+ ]
1811
+ }
1812
+ ) : null
1813
+ ]
1814
+ }
1815
+ );
1816
+ };
1817
+
1485
1818
  // src/app/data/metrics-shared.tsx
1486
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1819
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1487
1820
  var metricCardShellClass = cn(
1488
1821
  studioIntegrationCardClass,
1489
1822
  "aui-app-metric-card shadow-none",
@@ -1493,18 +1826,22 @@ var metricTilesRowClass = "grid w-full min-w-0";
1493
1826
  var metricCellDividerClass = "border-r border-border/40";
1494
1827
  var MetricCardHeader = ({
1495
1828
  title,
1829
+ titleTag,
1496
1830
  titleId,
1497
1831
  description,
1498
1832
  actions
1499
1833
  }) => {
1500
1834
  const headerClass = useAppDensityClass("metricCardHeader");
1501
1835
  if (!title && !description && !actions) return null;
1502
- return /* @__PURE__ */ jsxs2("header", { className: headerClass, children: [
1503
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
1504
- title ? /* @__PURE__ */ jsx3("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }) : null,
1505
- description ? /* @__PURE__ */ jsx3("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
1836
+ return /* @__PURE__ */ jsxs3("header", { className: cn(headerClass, "items-center"), children: [
1837
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
1838
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2.5", children: [
1839
+ title ? /* @__PURE__ */ jsx4("h3", { id: titleId, className: "text-sm font-semibold text-foreground tracking-tight select-none", children: title }) : null,
1840
+ titleTag ? /* @__PURE__ */ jsx4("span", { className: "inline-flex rounded-full bg-muted/70 px-2 py-0.5 text-[10px] font-medium text-muted-foreground border border-border/30 tracking-tight leading-none select-none", children: titleTag }) : null
1841
+ ] }),
1842
+ description ? /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-muted-foreground leading-normal select-none", children: description }) : null
1506
1843
  ] }),
1507
- actions ? /* @__PURE__ */ jsx3("div", { className: "shrink-0", children: actions }) : null
1844
+ actions ? /* @__PURE__ */ jsx4("div", { className: "shrink-0 flex items-center", children: actions }) : null
1508
1845
  ] });
1509
1846
  };
1510
1847
  function metricTilesGridColsClass(n) {
@@ -1515,6 +1852,8 @@ function metricTilesGridColsClass(n) {
1515
1852
  return "grid-cols-2";
1516
1853
  case 3:
1517
1854
  return "grid-cols-3";
1855
+ case 4:
1856
+ return "grid-cols-2 md:grid-cols-4";
1518
1857
  case 5:
1519
1858
  return "grid-cols-2 sm:grid-cols-5";
1520
1859
  case 6:
@@ -1525,12 +1864,34 @@ function metricTilesGridColsClass(n) {
1525
1864
  }
1526
1865
 
1527
1866
  // src/app/data/MetricTile.tsx
1528
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1867
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1529
1868
  var trendToneClass = {
1530
1869
  up: "border-border/80 bg-muted/40 text-muted-foreground",
1531
1870
  down: "border-border/80 bg-muted/40 text-muted-foreground",
1532
1871
  neutral: "border-border/80 bg-muted/30 text-muted-foreground"
1533
1872
  };
1873
+ var inlineTrendToneClass = {
1874
+ up: "text-blue-500/90 dark:text-blue-400/95 font-medium",
1875
+ down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
1876
+ neutral: "text-muted-foreground/80"
1877
+ };
1878
+ var sparklineToneColor = {
1879
+ up: "var(--primary, #3b82f6)",
1880
+ down: "var(--destructive, #f43f5e)",
1881
+ neutral: "var(--muted-foreground, #64748b)"
1882
+ };
1883
+ var sparklineBandBleed = {
1884
+ default: "-mx-4 -mb-3 h-10",
1885
+ compact: "-mx-3 -mb-2 h-8"
1886
+ };
1887
+ var activeToneClass = {
1888
+ default: "bg-foreground dark:bg-white",
1889
+ primary: "bg-primary",
1890
+ success: "bg-emerald-500",
1891
+ warn: "bg-amber-500",
1892
+ danger: "bg-destructive",
1893
+ neutral: "bg-border"
1894
+ };
1534
1895
  var metricTileInteractiveExtraClass = cn(
1535
1896
  "bg-transparent hover:bg-transparent active:bg-transparent",
1536
1897
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10"
@@ -1541,58 +1902,98 @@ var MetricTile = ({
1541
1902
  unit,
1542
1903
  trend,
1543
1904
  trendTone = "neutral",
1905
+ trendVariant = "pill",
1544
1906
  active = false,
1907
+ activeTone = "default",
1545
1908
  showDivider = false,
1546
1909
  onSelect,
1910
+ sparklineData,
1911
+ sparklineConfig,
1912
+ sparkline,
1547
1913
  ariaLabel,
1548
1914
  className
1549
1915
  }) => {
1916
+ const density = useAppDensity();
1550
1917
  const metricTileBaseClass = useAppDensityClass("metricTile");
1551
- const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
1552
- active ? /* @__PURE__ */ jsx4(
1918
+ const hasSparkline = Boolean(sparkline || sparklineData);
1919
+ const bandBleed = sparklineBandBleed[density === "compact" ? "compact" : "default"];
1920
+ const content = /* @__PURE__ */ jsxs4(Fragment3, { children: [
1921
+ active ? /* @__PURE__ */ jsx5(
1553
1922
  "span",
1554
1923
  {
1555
1924
  "aria-hidden": true,
1556
- className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground dark:bg-white"
1925
+ className: cn(
1926
+ "absolute inset-x-0 bottom-0 h-0.5 z-20 transition-colors duration-200",
1927
+ activeToneClass[activeTone]
1928
+ )
1557
1929
  }
1558
1930
  ) : null,
1559
- /* @__PURE__ */ jsx4("span", { className: "text-xs font-normal text-muted-foreground", children: label }),
1560
- /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-2", children: [
1561
- /* @__PURE__ */ jsxs3("span", { className: "flex items-baseline gap-1", children: [
1562
- /* @__PURE__ */ jsx4("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
1563
- unit ? /* @__PURE__ */ jsx4("span", { className: "text-xs font-normal text-muted-foreground", children: unit }) : null
1564
- ] }),
1565
- trend ? /* @__PURE__ */ jsx4(
1566
- "span",
1567
- {
1568
- className: cn(
1569
- "rounded-full border px-1.5 py-0.5 text-xs font-normal",
1570
- trendToneClass[trendTone]
1571
- ),
1572
- children: trend
1573
- }
1574
- ) : null
1575
- ] })
1931
+ /* @__PURE__ */ jsxs4("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
1932
+ /* @__PURE__ */ jsx5("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
1933
+ /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-2", children: [
1934
+ /* @__PURE__ */ jsxs4("span", { className: "flex items-baseline gap-1", children: [
1935
+ /* @__PURE__ */ jsx5("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
1936
+ unit ? /* @__PURE__ */ jsx5("span", { className: "text-xs font-medium text-muted-foreground", children: unit }) : null
1937
+ ] }),
1938
+ trend ? trendVariant === "inline" ? /* @__PURE__ */ jsx5("span", { className: cn("text-xs leading-none select-none", inlineTrendToneClass[trendTone]), children: trend }) : /* @__PURE__ */ jsx5(
1939
+ "span",
1940
+ {
1941
+ className: cn(
1942
+ "rounded-full border px-1.5 py-0.5 text-xs font-normal select-none",
1943
+ trendToneClass[trendTone]
1944
+ ),
1945
+ children: trend
1946
+ }
1947
+ ) : null
1948
+ ] })
1949
+ ] }),
1950
+ hasSparkline ? /* @__PURE__ */ jsx5(
1951
+ "div",
1952
+ {
1953
+ className: cn(
1954
+ "relative z-10 mt-2",
1955
+ bandBleed
1956
+ ),
1957
+ children: sparkline ?? /* @__PURE__ */ jsx5(
1958
+ Sparkline,
1959
+ {
1960
+ data: sparklineData,
1961
+ width: 160,
1962
+ height: 40,
1963
+ interactive: true,
1964
+ className: "h-full w-full opacity-90",
1965
+ color: sparklineToneColor[trendTone],
1966
+ ...sparklineConfig
1967
+ }
1968
+ )
1969
+ }
1970
+ ) : null
1576
1971
  ] });
1577
1972
  const divider = showDivider ? metricCellDividerClass : void 0;
1578
1973
  if (onSelect) {
1579
- return /* @__PURE__ */ jsx4(
1974
+ return /* @__PURE__ */ jsx5(
1580
1975
  "button",
1581
1976
  {
1582
1977
  type: "button",
1583
1978
  onClick: onSelect,
1584
1979
  "aria-pressed": active,
1585
1980
  "aria-label": ariaLabel,
1586
- className: cn(metricTileBaseClass, metricTileInteractiveExtraClass, divider, className),
1981
+ className: cn(
1982
+ metricTileBaseClass,
1983
+ "transition-all duration-200 hover:bg-muted/10",
1984
+ metricTileInteractiveExtraClass,
1985
+ divider,
1986
+ className
1987
+ ),
1587
1988
  children: content
1588
1989
  }
1589
1990
  );
1590
1991
  }
1591
- return /* @__PURE__ */ jsx4("div", { className: cn(metricTileBaseClass, divider, className), children: content });
1992
+ return /* @__PURE__ */ jsx5("div", { className: cn(metricTileBaseClass, divider, className), children: content });
1592
1993
  };
1593
1994
 
1594
1995
  // src/app/theme/ThemePresetGallery.tsx
1595
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1996
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1596
1997
 
1597
1998
  // src/app/layout/app-shell-chat-context.tsx
1598
1999
  import { createContext as createContext2, useContext as useContext2 } from "react";
@@ -1618,8 +2019,8 @@ function useAppShellNav() {
1618
2019
 
1619
2020
  // src/app/layout/AppShell.tsx
1620
2021
  import { motion, useReducedMotion } from "motion/react";
1621
- import { useCallback, useMemo, useState } from "react";
1622
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2022
+ import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
2023
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1623
2024
  var floatingTriggerClass = cn(
1624
2025
  "aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
1625
2026
  "bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
@@ -1642,21 +2043,32 @@ var AppShellBody = ({
1642
2043
  insetExpanded,
1643
2044
  children
1644
2045
  }) => {
2046
+ const [isMobile, setIsMobile] = useState2(() => {
2047
+ if (typeof window === "undefined") return false;
2048
+ return window.innerWidth < 768;
2049
+ });
2050
+ useEffect(() => {
2051
+ if (typeof window === "undefined") return;
2052
+ const onResize = () => setIsMobile(window.innerWidth < 768);
2053
+ onResize();
2054
+ window.addEventListener("resize", onResize);
2055
+ return () => window.removeEventListener("resize", onResize);
2056
+ }, []);
1645
2057
  const reducedMotion = useReducedMotion();
1646
2058
  const layoutDirection = insetExpanded ? "expand" : "collapse";
1647
2059
  const layoutTransition = studioSidebarWidthTransition(
1648
2060
  !!reducedMotion,
1649
2061
  layoutDirection
1650
2062
  );
1651
- const insetPadding = sidebar ? insetPaddingPx : 0;
1652
- return /* @__PURE__ */ jsx6(
2063
+ const insetPadding = sidebar && !isMobile ? insetPaddingPx : 0;
2064
+ return /* @__PURE__ */ jsx7(
1653
2065
  motion.div,
1654
2066
  {
1655
2067
  className: "aui-app-shell-body relative z-10 flex min-h-0 min-w-0 flex-1 flex-col",
1656
2068
  initial: false,
1657
2069
  animate: { paddingLeft: insetPadding },
1658
2070
  transition: layoutTransition,
1659
- children: /* @__PURE__ */ jsxs5(
2071
+ children: /* @__PURE__ */ jsxs6(
1660
2072
  "div",
1661
2073
  {
1662
2074
  className: cn(
@@ -1667,8 +2079,8 @@ var AppShellBody = ({
1667
2079
  !topbarContent && appShellInsetTopClass
1668
2080
  ),
1669
2081
  children: [
1670
- topbarContent ? /* @__PURE__ */ jsx6("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx6("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
1671
- /* @__PURE__ */ jsx6(
2082
+ topbarContent ? /* @__PURE__ */ jsx7("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx7("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
2083
+ /* @__PURE__ */ jsx7(
1672
2084
  "main",
1673
2085
  {
1674
2086
  className: cn(
@@ -1711,7 +2123,7 @@ var AppShell = ({
1711
2123
  }) => {
1712
2124
  const topbarContent = topbar ?? header;
1713
2125
  const hasChat = Boolean(chat);
1714
- const [uncontrolledNavOpen, setUncontrolledNavOpen] = useState(defaultNavOpen);
2126
+ const [uncontrolledNavOpen, setUncontrolledNavOpen] = useState2(defaultNavOpen);
1715
2127
  const isNavControlled = navOpenProp !== void 0;
1716
2128
  const navOpen = isNavControlled ? navOpenProp : uncontrolledNavOpen;
1717
2129
  const setNavOpen = useCallback(
@@ -1726,7 +2138,7 @@ var AppShell = ({
1726
2138
  () => ({ open: navOpen, setOpen: setNavOpen, toggle: toggleNav }),
1727
2139
  [navOpen, setNavOpen, toggleNav]
1728
2140
  );
1729
- const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultChatOpen);
2141
+ const [uncontrolledOpen, setUncontrolledOpen] = useState2(defaultChatOpen);
1730
2142
  const isChatControlled = chatOpenProp !== void 0;
1731
2143
  const chatOpen = isChatControlled ? chatOpenProp : uncontrolledOpen;
1732
2144
  const setChatOpen = useCallback(
@@ -1741,14 +2153,14 @@ var AppShell = ({
1741
2153
  const toggleChat = useCallback(() => {
1742
2154
  setChatOpen(!chatOpen);
1743
2155
  }, [chatOpen, setChatOpen]);
1744
- const [insetPaddingPx, setInsetPaddingPx] = useState(
2156
+ const [insetPaddingPx, setInsetPaddingPx] = useState2(
1745
2157
  sidebar ? SIDEBAR_INSET_PX_EXPANDED : 0
1746
2158
  );
1747
2159
  const reportShellInset = useCallback((insetPx) => {
1748
2160
  setInsetPaddingPx(insetPx);
1749
2161
  }, []);
1750
2162
  const insetExpanded = insetPaddingPx >= SIDEBAR_INSET_PX_EXPANDED;
1751
- const shellBody = /* @__PURE__ */ jsx6(
2163
+ const shellBody = /* @__PURE__ */ jsx7(
1752
2164
  AppShellBody,
1753
2165
  {
1754
2166
  sidebar,
@@ -1760,7 +2172,7 @@ var AppShell = ({
1760
2172
  children
1761
2173
  }
1762
2174
  );
1763
- const tree = /* @__PURE__ */ jsx6(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs5(
2175
+ const tree = /* @__PURE__ */ jsx7(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs6(
1764
2176
  "div",
1765
2177
  {
1766
2178
  className: cn(
@@ -1770,7 +2182,7 @@ var AppShell = ({
1770
2182
  style: studioChromeShellStyle,
1771
2183
  children: [
1772
2184
  sidebar,
1773
- sidebar && navOpen ? /* @__PURE__ */ jsx6(
2185
+ sidebar && navOpen ? /* @__PURE__ */ jsx7(
1774
2186
  "button",
1775
2187
  {
1776
2188
  type: "button",
@@ -1780,7 +2192,7 @@ var AppShell = ({
1780
2192
  }
1781
2193
  ) : null,
1782
2194
  shellBody,
1783
- hasChat && chatOpen ? /* @__PURE__ */ jsx6(
2195
+ hasChat && chatOpen ? /* @__PURE__ */ jsx7(
1784
2196
  "div",
1785
2197
  {
1786
2198
  className: floatingPanelClass,
@@ -1793,7 +2205,7 @@ var AppShell = ({
1793
2205
  children: chat
1794
2206
  }
1795
2207
  ) : null,
1796
- hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx6(
2208
+ hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx7(
1797
2209
  "button",
1798
2210
  {
1799
2211
  type: "button",
@@ -1806,11 +2218,11 @@ var AppShell = ({
1806
2218
  ]
1807
2219
  }
1808
2220
  ) });
1809
- const withNav = /* @__PURE__ */ jsx6(AppShellNavProvider, { value: navControls, children: tree });
2221
+ const withNav = /* @__PURE__ */ jsx7(AppShellNavProvider, { value: navControls, children: tree });
1810
2222
  if (!hasChat) {
1811
2223
  return withNav;
1812
2224
  }
1813
- return /* @__PURE__ */ jsx6(
2225
+ return /* @__PURE__ */ jsx7(
1814
2226
  AppShellChatProvider,
1815
2227
  {
1816
2228
  value: {
@@ -1824,23 +2236,6 @@ var AppShell = ({
1824
2236
  );
1825
2237
  };
1826
2238
 
1827
- // src/app/layout/AppShellTopbar.tsx
1828
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1829
- var AppShellTopbar = ({
1830
- start,
1831
- actions,
1832
- children,
1833
- className
1834
- }) => {
1835
- return /* @__PURE__ */ jsxs6("div", { className: cn("aui-app-shell-topbar", appShellTopbarRowClass, className), children: [
1836
- /* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1837
- start,
1838
- children
1839
- ] }),
1840
- actions ? /* @__PURE__ */ jsx7("div", { className: "aui-app-shell-topbar-actions flex shrink-0 items-center gap-2", children: actions }) : null
1841
- ] });
1842
- };
1843
-
1844
2239
  // src/app/layout/AppShellChatTrigger.tsx
1845
2240
  import { jsx as jsx8 } from "react/jsx-runtime";
1846
2241
  var floatingPositionClass = "fixed bottom-6 right-6 z-50 max-sm:bottom-4 max-sm:right-4";
@@ -1921,34 +2316,32 @@ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1921
2316
  var PageFrame = ({
1922
2317
  children,
1923
2318
  breadcrumbs,
2319
+ width,
1924
2320
  fill = false,
2321
+ fillPadded = false,
1925
2322
  className,
1926
2323
  ...headerProps
1927
2324
  }) => {
1928
2325
  const density = useAppDensity();
1929
- const pageColumnClass = useAppDensityClass("pageColumn");
1930
- return /* @__PURE__ */ jsxs8(
1931
- "div",
1932
- {
1933
- className: cn(
1934
- "aui-app-page",
1935
- fill ? "flex min-h-0 min-w-0 flex-1 flex-col" : pageColumnClass,
1936
- className
1937
- ),
1938
- "data-density": density,
1939
- children: [
1940
- breadcrumbs,
1941
- /* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
1942
- children
1943
- ]
1944
- }
1945
- );
2326
+ const stackClass = useAppDensityClass("pageStack");
2327
+ const columnClass = appPageColumn(width, density);
2328
+ const rootClass = fill ? cn(
2329
+ "flex min-h-0 min-w-0 flex-1 flex-col",
2330
+ fillPadded && columnClass
2331
+ ) : columnClass;
2332
+ return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-page", rootClass, className), "data-density": density, children: [
2333
+ breadcrumbs,
2334
+ /* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
2335
+ fill ? children : /* @__PURE__ */ jsx11("div", { className: cn("aui-app-page-stack", stackClass), children })
2336
+ ] });
1946
2337
  };
1947
2338
  var Page = ({
1948
2339
  density = "default",
1949
2340
  children,
1950
2341
  breadcrumbs,
2342
+ width,
1951
2343
  fill = false,
2344
+ fillPadded = false,
1952
2345
  className,
1953
2346
  ...headerProps
1954
2347
  }) => {
@@ -1956,7 +2349,9 @@ var Page = ({
1956
2349
  PageFrame,
1957
2350
  {
1958
2351
  breadcrumbs,
2352
+ width,
1959
2353
  fill,
2354
+ fillPadded,
1960
2355
  className,
1961
2356
  ...headerProps,
1962
2357
  children
@@ -1981,15 +2376,61 @@ var Section = ({
1981
2376
  ] });
1982
2377
  };
1983
2378
 
2379
+ // src/app/layout/Stack.tsx
2380
+ import { jsx as jsx13 } from "react/jsx-runtime";
2381
+ var GAP_CLASS = {
2382
+ none: "gap-0",
2383
+ xs: "gap-1",
2384
+ sm: "gap-2",
2385
+ md: "gap-4",
2386
+ lg: "gap-6",
2387
+ xl: "gap-8"
2388
+ };
2389
+ var ALIGN_CLASS = {
2390
+ start: "items-start",
2391
+ center: "items-center",
2392
+ end: "items-end",
2393
+ stretch: "items-stretch"
2394
+ };
2395
+ var JUSTIFY_CLASS = {
2396
+ start: "justify-start",
2397
+ center: "justify-center",
2398
+ end: "justify-end",
2399
+ between: "justify-between"
2400
+ };
2401
+ var Stack = ({
2402
+ children,
2403
+ direction = "vertical",
2404
+ gap = "md",
2405
+ align,
2406
+ justify,
2407
+ wrap = false,
2408
+ className
2409
+ }) => /* @__PURE__ */ jsx13(
2410
+ "div",
2411
+ {
2412
+ className: cn(
2413
+ "flex min-w-0",
2414
+ direction === "vertical" ? "flex-col" : "flex-row",
2415
+ GAP_CLASS[gap],
2416
+ align && ALIGN_CLASS[align],
2417
+ justify && JUSTIFY_CLASS[justify],
2418
+ wrap && "flex-wrap",
2419
+ className
2420
+ ),
2421
+ children
2422
+ }
2423
+ );
2424
+
1984
2425
  // src/app/copilot/app-copilot-context.tsx
1985
2426
  import { createContext as createContext4, useContext as useContext4 } from "react";
1986
- import { jsx as jsx13 } from "react/jsx-runtime";
2427
+ import { jsx as jsx14 } from "react/jsx-runtime";
1987
2428
  var AppCopilotContext = createContext4(null);
1988
2429
  var AppCopilotProvider = ({
1989
2430
  value,
1990
2431
  children
1991
2432
  }) => {
1992
- return /* @__PURE__ */ jsx13(AppCopilotContext.Provider, { value, children });
2433
+ return /* @__PURE__ */ jsx14(AppCopilotContext.Provider, { value, children });
1993
2434
  };
1994
2435
  function useAppCopilotContext() {
1995
2436
  return useContext4(AppCopilotContext) ?? {};
@@ -1997,7 +2438,7 @@ function useAppCopilotContext() {
1997
2438
 
1998
2439
  // src/app/chat/AppChatPanel.tsx
1999
2440
  import { XIcon } from "lucide-react";
2000
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
2441
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
2001
2442
  var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
2002
2443
  var chromeClass = cn(
2003
2444
  "aui-app-chat-panel-chrome relative z-20 flex min-h-10 shrink-0 items-center justify-end px-2 pt-2"
@@ -2043,17 +2484,17 @@ var AppChatPanel = ({
2043
2484
  }) => {
2044
2485
  const shellChat = useAppShellChat();
2045
2486
  return /* @__PURE__ */ jsxs10("div", { className: cn(shellClass, className), children: [
2046
- shellChat?.collapsible ? /* @__PURE__ */ jsx14("div", { className: chromeClass, children: /* @__PURE__ */ jsx14(
2487
+ shellChat?.collapsible ? /* @__PURE__ */ jsx15("div", { className: chromeClass, children: /* @__PURE__ */ jsx15(
2047
2488
  "button",
2048
2489
  {
2049
2490
  type: "button",
2050
2491
  className: closeButtonClass,
2051
2492
  onClick: () => shellChat.setOpen(false),
2052
2493
  "aria-label": "Close assistant",
2053
- children: /* @__PURE__ */ jsx14(XIcon, { className: "size-4", "aria-hidden": true })
2494
+ children: /* @__PURE__ */ jsx15(XIcon, { className: "size-4", "aria-hidden": true })
2054
2495
  }
2055
2496
  ) }) : null,
2056
- /* @__PURE__ */ jsx14("div", { className: bodyClass, children: /* @__PURE__ */ jsx14(
2497
+ /* @__PURE__ */ jsx15("div", { className: bodyClass, children: /* @__PURE__ */ jsx15(
2057
2498
  TimbalRuntimeProvider,
2058
2499
  {
2059
2500
  workforceId,
@@ -2063,7 +2504,7 @@ var AppChatPanel = ({
2063
2504
  attachmentsUploadUrl,
2064
2505
  attachmentsAccept,
2065
2506
  debug,
2066
- children: /* @__PURE__ */ jsx14(
2507
+ children: /* @__PURE__ */ jsx15(
2067
2508
  Thread,
2068
2509
  {
2069
2510
  variant: "panel",
@@ -2084,41 +2525,121 @@ var AppChatPanel = ({
2084
2525
  };
2085
2526
 
2086
2527
  // src/app/surfaces/SurfaceCard.tsx
2087
- import { jsx as jsx15 } from "react/jsx-runtime";
2088
- var SurfaceCard = ({ children, className }) => {
2528
+ import { jsx as jsx16 } from "react/jsx-runtime";
2529
+ var surfaceVariantClass = {
2530
+ default: "",
2531
+ muted: "bg-none bg-muted/40 shadow-none border border-border/20",
2532
+ outline: "bg-none bg-transparent border border-border shadow-none",
2533
+ elevated: "shadow-card-elevated border border-border/80",
2534
+ flat: "bg-card/70 border border-border/50 shadow-[0_1px_3px_rgba(0,0,0,0.03),0_10px_15px_-3px_rgba(0,0,0,0.01)] dark:bg-card/40",
2535
+ hierarchical: "bg-card/90 border border-border/60 shadow-[0_1px_2px_rgba(0,0,0,0.02),0_8px_16px_rgba(0,0,0,0.01)] rounded-2xl dark:bg-card/50"
2536
+ };
2537
+ var surfaceToneClass = {
2538
+ default: "",
2539
+ primary: "ring-1 ring-inset ring-primary/25",
2540
+ success: "ring-1 ring-inset ring-emerald-500/25",
2541
+ warn: "ring-1 ring-inset ring-amber-500/30",
2542
+ danger: "ring-1 ring-inset ring-destructive/25"
2543
+ };
2544
+ var SurfaceCard = ({
2545
+ children,
2546
+ variant = "default",
2547
+ tone = "default",
2548
+ hoverable,
2549
+ onClick,
2550
+ ariaLabel,
2551
+ className
2552
+ }) => {
2089
2553
  const surfaceCardClass = useAppDensityClass("surfaceCard");
2090
- return /* @__PURE__ */ jsx15("div", { className: cn("aui-app-surface-card", surfaceCardClass, className), children });
2554
+ const isInteractive = Boolean(onClick);
2555
+ const shouldHover = hoverable ?? isInteractive;
2556
+ const baseClass = cn(
2557
+ "aui-app-surface-card",
2558
+ surfaceCardClass,
2559
+ surfaceVariantClass[variant],
2560
+ surfaceToneClass[tone],
2561
+ shouldHover && "transition-all duration-200 hover:border-border/80 hover:shadow-md",
2562
+ isInteractive && "cursor-pointer select-none active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2563
+ className
2564
+ );
2565
+ if (isInteractive) {
2566
+ return /* @__PURE__ */ jsx16(
2567
+ "button",
2568
+ {
2569
+ type: "button",
2570
+ onClick,
2571
+ "aria-label": ariaLabel,
2572
+ className: baseClass,
2573
+ children
2574
+ }
2575
+ );
2576
+ }
2577
+ return /* @__PURE__ */ jsx16("div", { className: baseClass, children });
2091
2578
  };
2092
2579
 
2093
2580
  // src/app/surfaces/StatTile.tsx
2094
- import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
2095
- var StatTile = ({ label, value, hint, className }) => {
2581
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
2582
+ var statValueToneClass = {
2583
+ default: "",
2584
+ primary: "text-primary",
2585
+ success: "text-emerald-700 dark:text-emerald-400",
2586
+ warn: "text-amber-700 dark:text-amber-400",
2587
+ danger: "text-destructive"
2588
+ };
2589
+ var StatTile = ({
2590
+ label,
2591
+ value,
2592
+ hint,
2593
+ tone = "default",
2594
+ className
2595
+ }) => {
2096
2596
  const statTileClass = useAppDensityClass("statTile");
2097
2597
  return /* @__PURE__ */ jsxs11("div", { className: cn("aui-app-stat-tile", statTileClass, className), children: [
2098
- /* @__PURE__ */ jsx16("span", { className: appStatLabelClass, children: label }),
2099
- /* @__PURE__ */ jsx16("span", { className: appStatValueClass, children: value }),
2100
- hint ? /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: hint }) : null
2598
+ /* @__PURE__ */ jsx17("span", { className: appStatLabelClass, children: label }),
2599
+ /* @__PURE__ */ jsx17("span", { className: cn(appStatValueClass, statValueToneClass[tone]), children: value }),
2600
+ hint ? /* @__PURE__ */ jsx17("span", { className: "text-xs text-muted-foreground", children: hint }) : null
2101
2601
  ] });
2102
2602
  };
2103
2603
 
2104
2604
  // src/app/surfaces/EmptyState.tsx
2105
- import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
2605
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
2106
2606
  var EmptyState = ({
2107
2607
  title,
2108
2608
  description,
2109
2609
  action,
2110
- className
2610
+ className,
2611
+ variant = "default"
2111
2612
  }) => {
2112
- const emptyStateClass = useAppDensityClass("emptyState");
2113
- return /* @__PURE__ */ jsxs12("div", { className: cn("aui-app-empty-state", emptyStateClass, className), children: [
2114
- /* @__PURE__ */ jsx17("p", { className: appEmptyStateTitleClass, children: title }),
2115
- description ? /* @__PURE__ */ jsx17("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
2116
- action
2613
+ const densityClass = useAppDensityClass("emptyState");
2614
+ if (variant === "layered") {
2615
+ return /* @__PURE__ */ jsx18(
2616
+ "div",
2617
+ {
2618
+ className: cn(
2619
+ "aui-app-empty-state-layered w-full rounded-2xl border border-border/40 bg-muted/20 p-6 md:p-8 flex items-center justify-center dark:bg-muted/10",
2620
+ className
2621
+ ),
2622
+ children: /* @__PURE__ */ jsxs12("div", { className: "w-full max-w-xl rounded-xl border border-border bg-card p-6 md:p-8 shadow-[0_1px_3px_rgba(0,0,0,0.02),0_10px_15px_-3px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center text-center gap-4 transition-all duration-200 hover:border-border/80 dark:bg-card/45", children: [
2623
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5 items-center justify-center", children: [
2624
+ /* @__PURE__ */ jsx18("h3", { className: "text-base font-semibold text-foreground tracking-tight", children: title }),
2625
+ description ? /* @__PURE__ */ jsx18("p", { className: "max-w-sm text-sm text-muted-foreground leading-relaxed", children: description }) : null
2626
+ ] }),
2627
+ action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
2628
+ ] })
2629
+ }
2630
+ );
2631
+ }
2632
+ const isCompact = variant === "compact";
2633
+ const finalClass = isCompact ? "aui-app-empty-state rounded-xl border border-border bg-gradient-to-b from-elevated-from to-elevated-to p-3 flex flex-col items-center justify-center gap-2 py-8 text-center" : cn("aui-app-empty-state", densityClass);
2634
+ return /* @__PURE__ */ jsxs12("div", { className: cn(finalClass, className), children: [
2635
+ /* @__PURE__ */ jsx18("p", { className: cn(appEmptyStateTitleClass, "font-semibold tracking-tight"), children: title }),
2636
+ description ? /* @__PURE__ */ jsx18("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
2637
+ action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
2117
2638
  ] });
2118
2639
  };
2119
2640
 
2120
2641
  // src/app/surfaces/StatusBadge.tsx
2121
- import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2642
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
2122
2643
  var statusBadgeToneClass = {
2123
2644
  default: "bg-muted text-foreground ring-border",
2124
2645
  primary: "bg-primary/10 text-primary ring-primary/20",
@@ -2145,13 +2666,13 @@ var StatusBadge = ({
2145
2666
  "span",
2146
2667
  {
2147
2668
  className: cn(
2148
- "aui-app-status-badge inline-flex items-center gap-1.5 rounded-full px-2 py-0.5",
2669
+ "aui-app-status-badge inline-flex w-fit shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5",
2149
2670
  "text-xs font-medium leading-none ring-1 ring-inset",
2150
2671
  statusBadgeToneClass[tone],
2151
2672
  className
2152
2673
  ),
2153
2674
  children: [
2154
- dot ? /* @__PURE__ */ jsx18(
2675
+ dot ? /* @__PURE__ */ jsx19(
2155
2676
  "span",
2156
2677
  {
2157
2678
  "aria-hidden": true,
@@ -2168,7 +2689,7 @@ var StatusBadge = ({
2168
2689
  };
2169
2690
 
2170
2691
  // src/app/surfaces/AppConfirmDialog.tsx
2171
- import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2692
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
2172
2693
  var bodyClass2 = "flex flex-col gap-4 p-6";
2173
2694
  var titleClass = "pr-8";
2174
2695
  var actionsClass = "flex flex-wrap justify-end gap-2";
@@ -2183,15 +2704,15 @@ var AppConfirmDialog = ({
2183
2704
  destructive = false,
2184
2705
  className
2185
2706
  }) => {
2186
- return /* @__PURE__ */ jsx19(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx19(
2707
+ return /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx20(
2187
2708
  DialogContent,
2188
2709
  {
2189
2710
  className: cn("gap-0 p-0 sm:max-w-md", className),
2190
2711
  children: /* @__PURE__ */ jsxs14("div", { className: bodyClass2, children: [
2191
- /* @__PURE__ */ jsx19(DialogTitle, { className: titleClass, children: title }),
2192
- description ? /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: description }) : null,
2712
+ /* @__PURE__ */ jsx20(DialogTitle, { className: titleClass, children: title }),
2713
+ description ? /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: description }) : null,
2193
2714
  /* @__PURE__ */ jsxs14("div", { className: actionsClass, children: [
2194
- /* @__PURE__ */ jsx19(
2715
+ /* @__PURE__ */ jsx20(
2195
2716
  TimbalV2Button,
2196
2717
  {
2197
2718
  type: "button",
@@ -2201,7 +2722,7 @@ var AppConfirmDialog = ({
2201
2722
  children: cancelLabel
2202
2723
  }
2203
2724
  ),
2204
- /* @__PURE__ */ jsx19(
2725
+ /* @__PURE__ */ jsx20(
2205
2726
  TimbalV2Button,
2206
2727
  {
2207
2728
  type: "button",
@@ -2221,7 +2742,7 @@ var AppConfirmDialog = ({
2221
2742
  };
2222
2743
 
2223
2744
  // src/app/surfaces/InfoCard.tsx
2224
- import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
2745
+ import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2225
2746
  var toneClass = {
2226
2747
  neutral: "border-border bg-muted/40",
2227
2748
  info: "border-primary/25 bg-primary/5",
@@ -2229,34 +2750,67 @@ var toneClass = {
2229
2750
  warn: "border-amber-500/25 bg-amber-500/5",
2230
2751
  danger: "border-destructive/25 bg-destructive/5"
2231
2752
  };
2753
+ var toneVerticalClass = {
2754
+ neutral: "border-border/60 bg-card hover:border-border dark:bg-card/40",
2755
+ info: "border-primary/20 bg-primary/5 hover:border-primary/45",
2756
+ success: "border-emerald-500/20 bg-emerald-500/5 hover:border-emerald-500/45",
2757
+ warn: "border-amber-500/25 bg-amber-500/5 hover:border-amber-500/45",
2758
+ danger: "border-destructive/20 bg-destructive/5 hover:border-destructive/45"
2759
+ };
2232
2760
  var InfoCard = ({
2233
2761
  title,
2234
2762
  children,
2235
2763
  icon,
2236
2764
  action,
2237
2765
  tone = "neutral",
2766
+ layout = "horizontal",
2767
+ onClick,
2768
+ ariaLabel,
2238
2769
  className
2239
- }) => /* @__PURE__ */ jsxs15(
2240
- "div",
2241
- {
2242
- className: cn(
2243
- "flex items-start gap-3 rounded-xl border p-4",
2244
- toneClass[tone],
2770
+ }) => {
2771
+ const isInteractive = Boolean(onClick);
2772
+ if (layout === "vertical") {
2773
+ const cardContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
2774
+ icon ? /* @__PURE__ */ jsx21("span", { className: "flex size-10 items-center justify-center rounded-xl bg-muted/50 border border-border/40 text-muted-foreground/80 transition-transform duration-200 group-hover:scale-105", children: icon }) : null,
2775
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1.5 w-full text-left", children: [
2776
+ title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-semibold text-foreground tracking-tight leading-tight", children: title }) : null,
2777
+ children ? /* @__PURE__ */ jsx21("div", { className: "text-xs text-muted-foreground leading-relaxed", children }) : null
2778
+ ] }),
2779
+ action ? /* @__PURE__ */ jsx21("div", { className: "w-full mt-1", children: action }) : null
2780
+ ] });
2781
+ const baseClass2 = cn(
2782
+ "group flex flex-col gap-4 items-start rounded-2xl border p-5 transition-all duration-200 shadow-[0_1px_3px_rgba(0,0,0,0.03),0_10px_15px_-3px_rgba(0,0,0,0.01)]",
2783
+ toneVerticalClass[tone],
2784
+ isInteractive && "cursor-pointer select-none hover:shadow-md active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2245
2785
  className
2246
- ),
2247
- children: [
2248
- icon ? /* @__PURE__ */ jsx20("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
2249
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
2250
- title ? /* @__PURE__ */ jsx20("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
2251
- children ? /* @__PURE__ */ jsx20("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
2252
- ] }),
2253
- action ? /* @__PURE__ */ jsx20("div", { className: "shrink-0", children: action }) : null
2254
- ]
2786
+ );
2787
+ if (isInteractive) {
2788
+ return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass2, children: cardContent });
2789
+ }
2790
+ return /* @__PURE__ */ jsx21("div", { className: baseClass2, children: cardContent });
2255
2791
  }
2256
- );
2792
+ const horizontalContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
2793
+ icon ? /* @__PURE__ */ jsx21("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
2794
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1 text-left", children: [
2795
+ title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
2796
+ children ? /* @__PURE__ */ jsx21("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
2797
+ ] }),
2798
+ action ? /* @__PURE__ */ jsx21("div", { className: "shrink-0", children: action }) : null
2799
+ ] });
2800
+ const baseClass = cn(
2801
+ "flex items-start gap-3 rounded-xl border p-4 transition-all duration-200",
2802
+ toneClass[tone],
2803
+ isInteractive && "cursor-pointer select-none hover:bg-muted/30 active:scale-[0.995] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2804
+ className
2805
+ );
2806
+ if (isInteractive) {
2807
+ return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass, children: horizontalContent });
2808
+ }
2809
+ return /* @__PURE__ */ jsx21("div", { className: baseClass, children: horizontalContent });
2810
+ };
2257
2811
 
2258
2812
  // src/app/surfaces/StatusDot.tsx
2259
- import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
2813
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2260
2814
  var dotClass = {
2261
2815
  online: "bg-emerald-500",
2262
2816
  busy: "bg-amber-500",
@@ -2271,7 +2825,7 @@ var StatusDot = ({
2271
2825
  className
2272
2826
  }) => /* @__PURE__ */ jsxs16("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
2273
2827
  /* @__PURE__ */ jsxs16("span", { className: "relative flex size-2", children: [
2274
- pulse ? /* @__PURE__ */ jsx21(
2828
+ pulse ? /* @__PURE__ */ jsx22(
2275
2829
  "span",
2276
2830
  {
2277
2831
  className: cn(
@@ -2280,18 +2834,18 @@ var StatusDot = ({
2280
2834
  )
2281
2835
  }
2282
2836
  ) : null,
2283
- /* @__PURE__ */ jsx21("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
2837
+ /* @__PURE__ */ jsx22("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
2284
2838
  ] }),
2285
- label ? /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: label }) : null
2839
+ label ? /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: label }) : null
2286
2840
  ] });
2287
2841
 
2288
2842
  // src/app/surfaces/DescriptionList.tsx
2289
- import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
2843
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
2290
2844
  var DescriptionList = ({
2291
2845
  items,
2292
2846
  stacked = false,
2293
2847
  className
2294
- }) => /* @__PURE__ */ jsx22(
2848
+ }) => /* @__PURE__ */ jsx23(
2295
2849
  "dl",
2296
2850
  {
2297
2851
  className: cn(
@@ -2303,16 +2857,18 @@ var DescriptionList = ({
2303
2857
  {
2304
2858
  className: cn(
2305
2859
  "px-4 py-3",
2306
- stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4"
2860
+ stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4",
2861
+ item.className
2307
2862
  ),
2308
2863
  children: [
2309
- /* @__PURE__ */ jsx22("dt", { className: "text-sm text-muted-foreground", children: item.label }),
2310
- /* @__PURE__ */ jsx22(
2864
+ /* @__PURE__ */ jsx23("dt", { className: cn("text-sm text-muted-foreground", item.labelClassName), children: item.label }),
2865
+ /* @__PURE__ */ jsx23(
2311
2866
  "dd",
2312
2867
  {
2313
2868
  className: cn(
2314
2869
  "text-sm text-foreground",
2315
- !stacked && "text-right tabular-nums"
2870
+ !stacked && "text-right tabular-nums",
2871
+ item.valueClassName
2316
2872
  ),
2317
2873
  children: item.value
2318
2874
  }
@@ -2325,10 +2881,10 @@ var DescriptionList = ({
2325
2881
  );
2326
2882
 
2327
2883
  // src/app/surfaces/ExpandableSection.tsx
2328
- import { useId, useState as useState2 } from "react";
2884
+ import { useId as useId2, useState as useState3 } from "react";
2329
2885
  import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
2330
- import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
2331
- var Chevron = ({ open }) => /* @__PURE__ */ jsx23(
2886
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2887
+ var Chevron = ({ open }) => /* @__PURE__ */ jsx24(
2332
2888
  "svg",
2333
2889
  {
2334
2890
  viewBox: "0 0 24 24",
@@ -2342,7 +2898,7 @@ var Chevron = ({ open }) => /* @__PURE__ */ jsx23(
2342
2898
  strokeLinecap: "round",
2343
2899
  strokeLinejoin: "round",
2344
2900
  "aria-hidden": true,
2345
- children: /* @__PURE__ */ jsx23("path", { d: "m6 9 6 6 6-6" })
2901
+ children: /* @__PURE__ */ jsx24("path", { d: "m6 9 6 6 6-6" })
2346
2902
  }
2347
2903
  );
2348
2904
  var ExpandableSection = ({
@@ -2356,8 +2912,8 @@ var ExpandableSection = ({
2356
2912
  className
2357
2913
  }) => {
2358
2914
  const reduceMotion = useReducedMotion2();
2359
- const panelId = useId();
2360
- const [internalOpen, setInternalOpen] = useState2(defaultOpen);
2915
+ const panelId = useId2();
2916
+ const [internalOpen, setInternalOpen] = useState3(defaultOpen);
2361
2917
  const open = openProp ?? internalOpen;
2362
2918
  const toggle = () => {
2363
2919
  if (openProp == null) setInternalOpen((o) => !o);
@@ -2374,15 +2930,15 @@ var ExpandableSection = ({
2374
2930
  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",
2375
2931
  children: [
2376
2932
  /* @__PURE__ */ jsxs18("span", { className: "flex min-w-0 items-center gap-3", children: [
2377
- icon ? /* @__PURE__ */ jsx23("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
2378
- /* @__PURE__ */ jsx23("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
2379
- count != null ? /* @__PURE__ */ jsx23("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
2933
+ icon ? /* @__PURE__ */ jsx24("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
2934
+ /* @__PURE__ */ jsx24("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
2935
+ count != null ? /* @__PURE__ */ jsx24("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
2380
2936
  ] }),
2381
- /* @__PURE__ */ jsx23(Chevron, { open })
2937
+ /* @__PURE__ */ jsx24(Chevron, { open })
2382
2938
  ]
2383
2939
  }
2384
2940
  ),
2385
- /* @__PURE__ */ jsx23(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx23(
2941
+ /* @__PURE__ */ jsx24(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx24(
2386
2942
  motion2.div,
2387
2943
  {
2388
2944
  id: panelId,
@@ -2391,7 +2947,7 @@ var ExpandableSection = ({
2391
2947
  exit: reduceMotion ? void 0 : { height: 0, opacity: 0 },
2392
2948
  transition: { duration: 0.2, ease: "easeOut" },
2393
2949
  className: "overflow-hidden",
2394
- children: /* @__PURE__ */ jsx23("div", { className: "bg-muted/20", children })
2950
+ children: /* @__PURE__ */ jsx24("div", { className: "bg-muted/20", children })
2395
2951
  },
2396
2952
  "body"
2397
2953
  ) : null })
@@ -2399,19 +2955,14 @@ var ExpandableSection = ({
2399
2955
  };
2400
2956
 
2401
2957
  // src/app/surfaces/ResourceCard.tsx
2402
- import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
2403
- var resourceCardShellClass = cn(
2404
- "flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
2405
- TIMBAL_V2_ELEVATED_SURFACE
2406
- );
2958
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
2407
2959
  var mediaShellClass = cn(
2408
2960
  "flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xl text-sm font-normal text-foreground",
2409
2961
  TIMBAL_V2_LOGO_TILE
2410
2962
  );
2411
2963
  var resourceCardInteractiveClass = cn(
2412
- resourceCardShellClass,
2413
- "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
2414
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2964
+ "flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
2965
+ TIMBAL_V2_ELEVATED_SURFACE
2415
2966
  );
2416
2967
  var ResourceCard = ({
2417
2968
  title,
@@ -2422,37 +2973,236 @@ var ResourceCard = ({
2422
2973
  action,
2423
2974
  onClick,
2424
2975
  ariaLabel,
2425
- className
2976
+ className,
2977
+ variant = "flat",
2978
+ avatarShape = "circle"
2426
2979
  }) => {
2427
- const body = /* @__PURE__ */ jsxs19(Fragment3, { children: [
2980
+ const isInteractive = Boolean(onClick);
2981
+ const shellClass3 = cn(
2982
+ "flex min-h-[8.5rem] flex-col rounded-2xl p-5 text-left font-normal transition-all duration-200",
2983
+ variant === "default" && TIMBAL_V2_ELEVATED_SURFACE,
2984
+ variant === "flat" && "border border-border/50 bg-card/65 shadow-[0_1px_3px_rgba(0,0,0,0.05),0_10px_15px_-3px_rgba(0,0,0,0.01)] hover:border-border hover:shadow-md dark:bg-card/45",
2985
+ variant === "outline" && "border border-border/70 bg-transparent shadow-none hover:bg-muted/5",
2986
+ isInteractive && "cursor-pointer select-none active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2987
+ );
2988
+ const dynamicMediaShellClass = cn(
2989
+ "flex size-10 shrink-0 items-center justify-center overflow-hidden text-sm font-normal text-foreground transition-all duration-200",
2990
+ avatarShape === "circle" ? "rounded-full" : "rounded-xl",
2991
+ TIMBAL_V2_LOGO_TILE
2992
+ );
2993
+ const body = /* @__PURE__ */ jsxs19(Fragment5, { children: [
2428
2994
  /* @__PURE__ */ jsxs19("div", { className: "flex items-start gap-3", children: [
2429
- media ? /* @__PURE__ */ jsx24("span", { className: mediaShellClass, children: media }) : null,
2995
+ media ? /* @__PURE__ */ jsx25("span", { className: dynamicMediaShellClass, children: media }) : null,
2430
2996
  /* @__PURE__ */ jsxs19("div", { className: "min-w-0 flex-1 pt-0.5", children: [
2431
- /* @__PURE__ */ jsx24("p", { className: "truncate text-sm font-normal leading-snug text-foreground", children: title }),
2432
- subtitle ? /* @__PURE__ */ jsx24("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground", children: subtitle }) : null
2997
+ /* @__PURE__ */ jsx25("p", { className: "truncate text-sm font-semibold leading-snug text-foreground tracking-tight", children: title }),
2998
+ subtitle ? /* @__PURE__ */ jsx25("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground/90 leading-relaxed", children: subtitle }) : null
2433
2999
  ] }),
2434
- badge ? /* @__PURE__ */ jsx24("span", { className: "shrink-0 pt-0.5", children: badge }) : null
3000
+ badge ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 pt-0.5", children: badge }) : null
2435
3001
  ] }),
2436
3002
  footer || action ? /* @__PURE__ */ jsxs19("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: [
2437
- /* @__PURE__ */ jsx24("span", { className: "min-w-0 truncate", children: footer }),
2438
- action ? /* @__PURE__ */ jsx24("span", { className: "shrink-0 opacity-80", children: action }) : null
3003
+ /* @__PURE__ */ jsx25("span", { className: "min-w-0 truncate", children: footer }),
3004
+ action ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 opacity-90", children: action }) : null
2439
3005
  ] }) : null
2440
3006
  ] });
2441
3007
  if (onClick) {
2442
- return /* @__PURE__ */ jsx24("button", { type: "button", onClick, "aria-label": ariaLabel, className: cn(resourceCardInteractiveClass, className), children: body });
3008
+ return /* @__PURE__ */ jsx25(
3009
+ "button",
3010
+ {
3011
+ type: "button",
3012
+ onClick,
3013
+ "aria-label": ariaLabel,
3014
+ className: cn(shellClass3, className),
3015
+ children: body
3016
+ }
3017
+ );
2443
3018
  }
2444
- return /* @__PURE__ */ jsx24("article", { className: cn(resourceCardShellClass, className), children: body });
3019
+ return /* @__PURE__ */ jsx25("article", { className: cn(shellClass3, className), children: body });
3020
+ };
3021
+
3022
+ // src/app/surfaces/AlertCard.tsx
3023
+ import { ChevronRight } from "lucide-react";
3024
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
3025
+ var alertCardShellClass = cn(
3026
+ "flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
3027
+ TIMBAL_V2_ELEVATED_GRADIENT
3028
+ );
3029
+ var alertCardInteractiveClass = cn(
3030
+ "flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card cursor-pointer",
3031
+ TIMBAL_V2_ELEVATED_GRADIENT,
3032
+ "transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
3033
+ "hover:border-foreground/20",
3034
+ "hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
3035
+ "active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
3036
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
3037
+ );
3038
+ var AlertCard = ({
3039
+ title,
3040
+ description,
3041
+ category,
3042
+ categoryTone = "default",
3043
+ status,
3044
+ statusTone = "default",
3045
+ action,
3046
+ trailing,
3047
+ onClick,
3048
+ ariaLabel,
3049
+ className
3050
+ }) => {
3051
+ const showTags = Boolean(category || status);
3052
+ const showTrailing = Boolean(trailing || onClick);
3053
+ const bodyContent = /* @__PURE__ */ jsxs20("div", { className: "flex-1 min-w-0 flex flex-col h-full", children: [
3054
+ showTags ? /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap items-center gap-1.5 mb-2.5", children: [
3055
+ category ? /* @__PURE__ */ jsx26(StatusBadge, { tone: categoryTone, children: category }) : null,
3056
+ status ? /* @__PURE__ */ jsx26(StatusBadge, { tone: statusTone, children: status }) : null
3057
+ ] }) : null,
3058
+ /* @__PURE__ */ jsx26("h4", { className: "text-sm font-medium leading-snug text-foreground", children: title }),
3059
+ description ? /* @__PURE__ */ jsx26("p", { className: "mt-1.5 text-xs text-muted-foreground leading-normal", children: description }) : null,
3060
+ action ? /* @__PURE__ */ jsxs20("div", { className: "mt-auto pt-3 text-xs text-muted-foreground/90 leading-normal", children: [
3061
+ /* @__PURE__ */ jsx26("strong", { className: "font-semibold text-foreground/80", children: "Action: " }),
3062
+ action
3063
+ ] }) : null
3064
+ ] });
3065
+ const cardContent = /* @__PURE__ */ jsxs20("div", { className: "flex items-start justify-between gap-4 w-full h-full", children: [
3066
+ bodyContent,
3067
+ showTrailing ? /* @__PURE__ */ jsx26("div", { className: "shrink-0 flex items-center justify-center self-center text-muted-foreground/50", children: trailing || /* @__PURE__ */ jsx26(ChevronRight, { className: "size-4" }) }) : null
3068
+ ] });
3069
+ if (onClick) {
3070
+ return /* @__PURE__ */ jsx26(
3071
+ "button",
3072
+ {
3073
+ type: "button",
3074
+ onClick,
3075
+ "aria-label": ariaLabel,
3076
+ className: cn(alertCardInteractiveClass, className),
3077
+ children: cardContent
3078
+ }
3079
+ );
3080
+ }
3081
+ return /* @__PURE__ */ jsx26("article", { className: cn(alertCardShellClass, className), children: cardContent });
3082
+ };
3083
+
3084
+ // src/app/surfaces/CatalogCard.tsx
3085
+ import { ExternalLink } from "lucide-react";
3086
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
3087
+ var catalogCardShellClass = cn(
3088
+ "flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal",
3089
+ TIMBAL_V2_ELEVATED_GRADIENT
3090
+ );
3091
+ var catalogCardInteractiveClass = cn(
3092
+ "flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal cursor-pointer",
3093
+ TIMBAL_V2_ELEVATED_GRADIENT,
3094
+ "transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
3095
+ "hover:border-foreground/20",
3096
+ "hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
3097
+ "active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
3098
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
3099
+ );
3100
+ var CatalogCard = ({
3101
+ title,
3102
+ subtitle,
3103
+ logo,
3104
+ href,
3105
+ badge,
3106
+ description,
3107
+ tags,
3108
+ footerLinks,
3109
+ copyValue,
3110
+ copyLabel = "Copy ID",
3111
+ actions,
3112
+ onClick,
3113
+ ariaLabel,
3114
+ className
3115
+ }) => {
3116
+ const showHeaderTags = Boolean(subtitle || badge);
3117
+ const showFooter = Boolean(footerLinks && footerLinks.length > 0 || copyValue || actions);
3118
+ const mainContent = /* @__PURE__ */ jsxs21("div", { className: "flex-1 p-5 flex flex-col h-full min-h-0", children: [
3119
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-3", children: [
3120
+ logo ? /* @__PURE__ */ jsx27("div", { className: "size-8 shrink-0 flex items-center justify-center overflow-hidden rounded-lg bg-muted/40", children: logo }) : null,
3121
+ /* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3122
+ /* @__PURE__ */ jsx27("div", { className: "flex items-center gap-1.5 flex-wrap", children: href ? /* @__PURE__ */ jsxs21(
3123
+ "a",
3124
+ {
3125
+ href,
3126
+ target: "_blank",
3127
+ rel: "noopener noreferrer",
3128
+ className: "inline-flex items-center gap-1 hover:underline text-sm font-medium leading-snug text-foreground focus-visible:outline-none focus-visible:underline",
3129
+ onClick: (e) => e.stopPropagation(),
3130
+ children: [
3131
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: title }),
3132
+ /* @__PURE__ */ jsx27(ExternalLink, { className: "size-3.5 shrink-0 text-muted-foreground/60" })
3133
+ ]
3134
+ }
3135
+ ) : /* @__PURE__ */ jsx27("h4", { className: "text-sm font-medium leading-snug text-foreground truncate", children: title }) }),
3136
+ showHeaderTags ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center gap-2 mt-1", children: [
3137
+ subtitle ? /* @__PURE__ */ jsx27("span", { className: "text-xs font-medium text-muted-foreground", children: subtitle }) : null,
3138
+ badge ? /* @__PURE__ */ jsx27("div", { className: "shrink-0 flex items-center", children: badge }) : null
3139
+ ] }) : null
3140
+ ] })
3141
+ ] }),
3142
+ description ? /* @__PURE__ */ jsx27("p", { className: "mt-3 text-xs leading-relaxed text-muted-foreground line-clamp-3", children: description }) : null,
3143
+ tags && tags.length > 0 ? /* @__PURE__ */ jsx27("div", { className: "flex flex-wrap items-center gap-2 mt-4", children: tags.map((tag, idx) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center text-xs text-muted-foreground/80", children: [
3144
+ idx > 0 && /* @__PURE__ */ jsx27("span", { className: "mr-2 text-muted-foreground/30", children: "\u2022" }),
3145
+ tag
3146
+ ] }, idx)) }) : null
3147
+ ] });
3148
+ const footerMarkup = showFooter ? /* @__PURE__ */ jsxs21("div", { className: "border-t border-border/40 px-5 py-3 flex items-center justify-between gap-4 bg-muted/[0.02]", children: [
3149
+ /* @__PURE__ */ jsx27("div", { className: "flex flex-wrap items-center gap-4", children: footerLinks && footerLinks.length > 0 ? footerLinks.map((link, idx) => /* @__PURE__ */ jsxs21(
3150
+ "a",
3151
+ {
3152
+ href: link.href,
3153
+ target: "_blank",
3154
+ rel: "noopener noreferrer",
3155
+ className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground hover:underline transition-colors focus-visible:outline-none focus-visible:underline",
3156
+ onClick: (e) => e.stopPropagation(),
3157
+ children: [
3158
+ /* @__PURE__ */ jsx27("span", { children: link.label }),
3159
+ /* @__PURE__ */ jsx27(ExternalLink, { className: "size-2.5 shrink-0 text-muted-foreground/50" })
3160
+ ]
3161
+ },
3162
+ idx
3163
+ )) : null }),
3164
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", onClick: (e) => e.stopPropagation(), children: [
3165
+ actions ? actions : null,
3166
+ !actions && copyValue ? /* @__PURE__ */ jsx27(
3167
+ CopyButton,
3168
+ {
3169
+ value: copyValue,
3170
+ className: "h-7 text-xs px-2 hover:bg-muted/40 text-muted-foreground/80 hover:text-foreground",
3171
+ children: copyLabel
3172
+ }
3173
+ ) : null
3174
+ ] })
3175
+ ] }) : null;
3176
+ if (onClick) {
3177
+ return /* @__PURE__ */ jsxs21(
3178
+ "button",
3179
+ {
3180
+ type: "button",
3181
+ onClick,
3182
+ "aria-label": ariaLabel,
3183
+ className: cn(catalogCardInteractiveClass, className),
3184
+ children: [
3185
+ mainContent,
3186
+ footerMarkup
3187
+ ]
3188
+ }
3189
+ );
3190
+ }
3191
+ return /* @__PURE__ */ jsxs21("article", { className: cn(catalogCardShellClass, className), children: [
3192
+ mainContent,
3193
+ footerMarkup
3194
+ ] });
2445
3195
  };
2446
3196
 
2447
3197
  // src/app/settings/SettingsSection.tsx
2448
- import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
3198
+ import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
2449
3199
  var SettingsSectionHeader = ({
2450
3200
  title,
2451
3201
  description,
2452
3202
  className
2453
- }) => /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col", className), children: [
2454
- /* @__PURE__ */ jsx25("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
2455
- description ? /* @__PURE__ */ jsx25("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
3203
+ }) => /* @__PURE__ */ jsxs22("div", { className: cn("flex flex-col", className), children: [
3204
+ /* @__PURE__ */ jsx28("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
3205
+ description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
2456
3206
  ] });
2457
3207
  var SettingsSection = ({
2458
3208
  title,
@@ -2461,7 +3211,7 @@ var SettingsSection = ({
2461
3211
  children,
2462
3212
  noBorderTop = false,
2463
3213
  className
2464
- }) => /* @__PURE__ */ jsxs20(
3214
+ }) => /* @__PURE__ */ jsxs22(
2465
3215
  "section",
2466
3216
  {
2467
3217
  className: cn(
@@ -2470,18 +3220,18 @@ var SettingsSection = ({
2470
3220
  className
2471
3221
  ),
2472
3222
  children: [
2473
- /* @__PURE__ */ jsxs20("div", { className: "min-w-0", children: [
2474
- /* @__PURE__ */ jsx25("h2", { className: "text-sm font-medium text-foreground", children: title }),
2475
- description ? /* @__PURE__ */ jsx25("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
2476
- descriptionFooter ? /* @__PURE__ */ jsx25("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
3223
+ /* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
3224
+ /* @__PURE__ */ jsx28("h2", { className: "text-sm font-medium text-foreground", children: title }),
3225
+ description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
3226
+ descriptionFooter ? /* @__PURE__ */ jsx28("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
2477
3227
  ] }),
2478
- /* @__PURE__ */ jsx25("div", { className: "min-w-0 space-y-3", children })
3228
+ /* @__PURE__ */ jsx28("div", { className: "min-w-0 space-y-3", children })
2479
3229
  ]
2480
3230
  }
2481
3231
  );
2482
3232
 
2483
3233
  // src/app/settings/FieldRow.tsx
2484
- import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
3234
+ import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
2485
3235
  var FieldRow = ({
2486
3236
  label,
2487
3237
  children,
@@ -2491,7 +3241,7 @@ var FieldRow = ({
2491
3241
  className
2492
3242
  }) => {
2493
3243
  if (inline) {
2494
- return /* @__PURE__ */ jsxs21(
3244
+ return /* @__PURE__ */ jsxs23(
2495
3245
  "div",
2496
3246
  {
2497
3247
  className: cn(
@@ -2499,8 +3249,8 @@ var FieldRow = ({
2499
3249
  className
2500
3250
  ),
2501
3251
  children: [
2502
- /* @__PURE__ */ jsxs21("div", { className: "min-w-0", children: [
2503
- /* @__PURE__ */ jsx26(
3252
+ /* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
3253
+ /* @__PURE__ */ jsx29(
2504
3254
  "label",
2505
3255
  {
2506
3256
  htmlFor,
@@ -2508,25 +3258,25 @@ var FieldRow = ({
2508
3258
  children: label
2509
3259
  }
2510
3260
  ),
2511
- description ? /* @__PURE__ */ jsx26("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
3261
+ description ? /* @__PURE__ */ jsx29("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
2512
3262
  ] }),
2513
- /* @__PURE__ */ jsx26("div", { className: "shrink-0", children })
3263
+ /* @__PURE__ */ jsx29("div", { className: "shrink-0", children })
2514
3264
  ]
2515
3265
  }
2516
3266
  );
2517
3267
  }
2518
- return /* @__PURE__ */ jsxs21("div", { className: cn("flex flex-col gap-1.5", className), children: [
2519
- /* @__PURE__ */ jsx26("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
3268
+ return /* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-1.5", className), children: [
3269
+ /* @__PURE__ */ jsx29("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
2520
3270
  children,
2521
- description ? /* @__PURE__ */ jsx26("p", { className: "text-xs text-muted-foreground", children: description }) : null
3271
+ description ? /* @__PURE__ */ jsx29("p", { className: "text-xs text-muted-foreground", children: description }) : null
2522
3272
  ] });
2523
3273
  };
2524
3274
 
2525
3275
  // src/app/settings/FloatingUnsavedChangesBar.tsx
2526
- import { useEffect, useState as useState3 } from "react";
3276
+ import { useEffect as useEffect2, useState as useState4 } from "react";
2527
3277
  import { createPortal } from "react-dom";
2528
3278
  import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
2529
- import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
3279
+ import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
2530
3280
  var FloatingUnsavedChangesBar = ({
2531
3281
  visible,
2532
3282
  message = "Unsaved changes",
@@ -2540,11 +3290,11 @@ var FloatingUnsavedChangesBar = ({
2540
3290
  className
2541
3291
  }) => {
2542
3292
  const reduceMotion = useReducedMotion3();
2543
- const [mounted, setMounted] = useState3(false);
2544
- useEffect(() => setMounted(true), []);
3293
+ const [mounted, setMounted] = useState4(false);
3294
+ useEffect2(() => setMounted(true), []);
2545
3295
  if (!mounted || typeof document === "undefined") return null;
2546
3296
  return createPortal(
2547
- /* @__PURE__ */ jsx27(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx27("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs22(
3297
+ /* @__PURE__ */ jsx30(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx30("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs24(
2548
3298
  motion3.div,
2549
3299
  {
2550
3300
  role: "region",
@@ -2558,10 +3308,10 @@ var FloatingUnsavedChangesBar = ({
2558
3308
  className
2559
3309
  ),
2560
3310
  children: [
2561
- /* @__PURE__ */ jsx27("span", { className: "text-sm text-muted-foreground", children: message }),
2562
- /* @__PURE__ */ jsxs22("span", { className: "flex items-center gap-1.5", children: [
2563
- /* @__PURE__ */ jsx27(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
2564
- /* @__PURE__ */ jsx27(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
3311
+ /* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: message }),
3312
+ /* @__PURE__ */ jsxs24("span", { className: "flex items-center gap-1.5", children: [
3313
+ /* @__PURE__ */ jsx30(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
3314
+ /* @__PURE__ */ jsx30(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
2565
3315
  ] })
2566
3316
  ]
2567
3317
  }
@@ -2571,13 +3321,13 @@ var FloatingUnsavedChangesBar = ({
2571
3321
  };
2572
3322
 
2573
3323
  // src/app/settings/DangerZone.tsx
2574
- import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
3324
+ import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
2575
3325
  var DangerZoneAction = ({
2576
3326
  title,
2577
3327
  description,
2578
3328
  action,
2579
3329
  className
2580
- }) => /* @__PURE__ */ jsxs23(
3330
+ }) => /* @__PURE__ */ jsxs25(
2581
3331
  "div",
2582
3332
  {
2583
3333
  className: cn(
@@ -2585,11 +3335,11 @@ var DangerZoneAction = ({
2585
3335
  className
2586
3336
  ),
2587
3337
  children: [
2588
- /* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
2589
- /* @__PURE__ */ jsx28("p", { className: "text-sm font-medium text-foreground", children: title }),
2590
- description ? /* @__PURE__ */ jsx28("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
3338
+ /* @__PURE__ */ jsxs25("div", { className: "min-w-0", children: [
3339
+ /* @__PURE__ */ jsx31("p", { className: "text-sm font-medium text-foreground", children: title }),
3340
+ description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2591
3341
  ] }),
2592
- /* @__PURE__ */ jsx28("div", { className: "shrink-0", children: action })
3342
+ /* @__PURE__ */ jsx31("div", { className: "shrink-0", children: action })
2593
3343
  ]
2594
3344
  }
2595
3345
  );
@@ -2598,7 +3348,7 @@ var DangerZone = ({
2598
3348
  description,
2599
3349
  children,
2600
3350
  className
2601
- }) => /* @__PURE__ */ jsxs23(
3351
+ }) => /* @__PURE__ */ jsxs25(
2602
3352
  "section",
2603
3353
  {
2604
3354
  className: cn(
@@ -2606,18 +3356,18 @@ var DangerZone = ({
2606
3356
  className
2607
3357
  ),
2608
3358
  children: [
2609
- (title || description) && /* @__PURE__ */ jsxs23("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
2610
- title ? /* @__PURE__ */ jsx28("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
2611
- description ? /* @__PURE__ */ jsx28("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
3359
+ (title || description) && /* @__PURE__ */ jsxs25("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
3360
+ title ? /* @__PURE__ */ jsx31("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
3361
+ description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2612
3362
  ] }),
2613
- /* @__PURE__ */ jsx28("div", { className: "divide-y divide-border bg-card", children })
3363
+ /* @__PURE__ */ jsx31("div", { className: "divide-y divide-border bg-card", children })
2614
3364
  ]
2615
3365
  }
2616
3366
  );
2617
3367
 
2618
3368
  // src/app/integrations/IntegrationCard.tsx
2619
- import { useId as useId2 } from "react";
2620
- import { Fragment as Fragment4, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
3369
+ import { useId as useId3 } from "react";
3370
+ import { Fragment as Fragment6, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
2621
3371
  var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
2622
3372
  var statusLabel = {
2623
3373
  available: null,
@@ -2625,14 +3375,14 @@ var statusLabel = {
2625
3375
  disabled: "Disabled",
2626
3376
  locked: "Locked"
2627
3377
  };
2628
- var catalogCardShellClass = cn(
3378
+ var catalogCardShellClass2 = cn(
2629
3379
  "group relative box-border flex flex-col overflow-hidden rounded-2xl px-4 pb-4 pt-4 text-left font-normal",
2630
3380
  INTEGRATION_CATALOG_CARD_HEIGHT_CLASS,
2631
3381
  TIMBAL_V2_ELEVATED_SURFACE,
2632
3382
  "transition-opacity duration-200 ease-out"
2633
3383
  );
2634
- var catalogCardInteractiveClass = cn(
2635
- catalogCardShellClass,
3384
+ var catalogCardInteractiveClass2 = cn(
3385
+ catalogCardShellClass2,
2636
3386
  "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
2637
3387
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2638
3388
  );
@@ -2655,15 +3405,15 @@ var IntegrationCard = ({
2655
3405
  ariaLabel,
2656
3406
  className
2657
3407
  }) => {
2658
- const titleId = useId2();
3408
+ const titleId = useId3();
2659
3409
  const locked = status === "locked";
2660
3410
  const dimmed = status === "disabled" || locked;
2661
- const body = /* @__PURE__ */ jsxs24("div", { className: "flex h-full min-h-0 flex-col", children: [
2662
- /* @__PURE__ */ jsxs24("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
2663
- logo ? /* @__PURE__ */ jsx29("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
2664
- /* @__PURE__ */ jsx29("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs24("div", { className: "flex items-start justify-between gap-2", children: [
2665
- /* @__PURE__ */ jsxs24("div", { className: "min-w-0", children: [
2666
- /* @__PURE__ */ jsx29(
3411
+ const body = /* @__PURE__ */ jsxs26("div", { className: "flex h-full min-h-0 flex-col", children: [
3412
+ /* @__PURE__ */ jsxs26("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
3413
+ logo ? /* @__PURE__ */ jsx32("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
3414
+ /* @__PURE__ */ jsx32("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs26("div", { className: "flex items-start justify-between gap-2", children: [
3415
+ /* @__PURE__ */ jsxs26("div", { className: "min-w-0", children: [
3416
+ /* @__PURE__ */ jsx32(
2667
3417
  "h4",
2668
3418
  {
2669
3419
  id: onClick && !action ? void 0 : titleId,
@@ -2671,12 +3421,12 @@ var IntegrationCard = ({
2671
3421
  children: name
2672
3422
  }
2673
3423
  ),
2674
- statusLabel[status] ? /* @__PURE__ */ jsx29("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
3424
+ statusLabel[status] ? /* @__PURE__ */ jsx32("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
2675
3425
  ] }),
2676
- badge ? /* @__PURE__ */ jsx29("span", { className: "shrink-0", children: badge }) : null
3426
+ badge ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: badge }) : null
2677
3427
  ] }) })
2678
3428
  ] }),
2679
- description ? /* @__PURE__ */ jsx29(
3429
+ description ? /* @__PURE__ */ jsx32(
2680
3430
  "p",
2681
3431
  {
2682
3432
  className: cn(
@@ -2686,19 +3436,19 @@ var IntegrationCard = ({
2686
3436
  children: description
2687
3437
  }
2688
3438
  ) : null,
2689
- action ? /* @__PURE__ */ jsxs24(Fragment4, { children: [
2690
- /* @__PURE__ */ jsx29("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
2691
- /* @__PURE__ */ jsx29("div", { className: "relative mt-3 shrink-0", children: action })
3439
+ action ? /* @__PURE__ */ jsxs26(Fragment6, { children: [
3440
+ /* @__PURE__ */ jsx32("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
3441
+ /* @__PURE__ */ jsx32("div", { className: "relative mt-3 shrink-0", children: action })
2692
3442
  ] }) : null
2693
3443
  ] });
2694
3444
  const shellClass3 = cn(
2695
- catalogCardShellClass,
3445
+ catalogCardShellClass2,
2696
3446
  dimmed && catalogCardMutedClass,
2697
3447
  locked && "cursor-default opacity-75",
2698
3448
  className
2699
3449
  );
2700
3450
  if (onClick && !action) {
2701
- return /* @__PURE__ */ jsx29(
3451
+ return /* @__PURE__ */ jsx32(
2702
3452
  "button",
2703
3453
  {
2704
3454
  type: "button",
@@ -2706,7 +3456,7 @@ var IntegrationCard = ({
2706
3456
  disabled: locked,
2707
3457
  "aria-label": ariaLabel,
2708
3458
  className: cn(
2709
- catalogCardInteractiveClass,
3459
+ catalogCardInteractiveClass2,
2710
3460
  dimmed && catalogCardMutedClass,
2711
3461
  locked && "cursor-default opacity-75",
2712
3462
  className
@@ -2715,12 +3465,12 @@ var IntegrationCard = ({
2715
3465
  }
2716
3466
  );
2717
3467
  }
2718
- return /* @__PURE__ */ jsx29("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
3468
+ return /* @__PURE__ */ jsx32("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
2719
3469
  };
2720
3470
 
2721
3471
  // src/app/integrations/IntegrationsEmptyState.tsx
2722
- import { useId as useId3 } from "react";
2723
- import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
3472
+ import { useId as useId4 } from "react";
3473
+ import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
2724
3474
  var IntegrationsEmptyState = ({
2725
3475
  title = "No integrations yet",
2726
3476
  description = "Connect a provider to start syncing data and powering your workforce.",
@@ -2728,8 +3478,8 @@ var IntegrationsEmptyState = ({
2728
3478
  action,
2729
3479
  className
2730
3480
  }) => {
2731
- const titleId = useId3();
2732
- return /* @__PURE__ */ jsxs25(
3481
+ const titleId = useId4();
3482
+ return /* @__PURE__ */ jsxs27(
2733
3483
  "section",
2734
3484
  {
2735
3485
  className: cn(
@@ -2739,7 +3489,7 @@ var IntegrationsEmptyState = ({
2739
3489
  ),
2740
3490
  "aria-labelledby": titleId,
2741
3491
  children: [
2742
- icon ? /* @__PURE__ */ jsx30(
3492
+ icon ? /* @__PURE__ */ jsx33(
2743
3493
  "span",
2744
3494
  {
2745
3495
  className: cn(
@@ -2751,21 +3501,21 @@ var IntegrationsEmptyState = ({
2751
3501
  children: icon
2752
3502
  }
2753
3503
  ) : null,
2754
- /* @__PURE__ */ jsx30("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
2755
- description ? /* @__PURE__ */ jsx30("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
2756
- action ? /* @__PURE__ */ jsx30("div", { className: "mt-1", children: action }) : null
3504
+ /* @__PURE__ */ jsx33("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
3505
+ description ? /* @__PURE__ */ jsx33("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
3506
+ action ? /* @__PURE__ */ jsx33("div", { className: "mt-1", children: action }) : null
2757
3507
  ]
2758
3508
  }
2759
3509
  );
2760
3510
  };
2761
3511
 
2762
3512
  // src/app/integrations/PlanBadge.tsx
2763
- import { jsx as jsx31 } from "react/jsx-runtime";
3513
+ import { jsx as jsx34 } from "react/jsx-runtime";
2764
3514
  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";
2765
- var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx31("span", { className: cn(planBadgeClass, className), children });
3515
+ var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx34("span", { className: cn(planBadgeClass, className), children });
2766
3516
 
2767
3517
  // src/app/integrations/ConnectionRow.tsx
2768
- import { Fragment as Fragment5, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
3518
+ import { Fragment as Fragment7, jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
2769
3519
  var logoShellClass2 = cn(
2770
3520
  "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
2771
3521
  TIMBAL_V2_LOGO_TILE
@@ -2780,14 +3530,14 @@ var ConnectionRow = ({
2780
3530
  ariaLabel,
2781
3531
  className
2782
3532
  }) => {
2783
- const inner = /* @__PURE__ */ jsxs26(Fragment5, { children: [
2784
- logo ? /* @__PURE__ */ jsx32("span", { className: logoShellClass2, children: logo }) : null,
2785
- /* @__PURE__ */ jsxs26("div", { className: "min-w-0 flex-1", children: [
2786
- /* @__PURE__ */ jsx32("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
2787
- meta ? /* @__PURE__ */ jsx32("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
3533
+ const inner = /* @__PURE__ */ jsxs28(Fragment7, { children: [
3534
+ logo ? /* @__PURE__ */ jsx35("span", { className: logoShellClass2, children: logo }) : null,
3535
+ /* @__PURE__ */ jsxs28("div", { className: "min-w-0 flex-1", children: [
3536
+ /* @__PURE__ */ jsx35("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
3537
+ meta ? /* @__PURE__ */ jsx35("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
2788
3538
  ] }),
2789
- badge ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: badge }) : null,
2790
- action ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: action }) : null
3539
+ badge ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: badge }) : null,
3540
+ action ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: action }) : null
2791
3541
  ] });
2792
3542
  const rowClass2 = cn(
2793
3543
  "flex w-full items-center gap-3 px-4 py-3 text-left",
@@ -2795,7 +3545,7 @@ var ConnectionRow = ({
2795
3545
  className
2796
3546
  );
2797
3547
  if (onClick) {
2798
- return /* @__PURE__ */ jsx32(
3548
+ return /* @__PURE__ */ jsx35(
2799
3549
  "button",
2800
3550
  {
2801
3551
  type: "button",
@@ -2807,7 +3557,7 @@ var ConnectionRow = ({
2807
3557
  }
2808
3558
  );
2809
3559
  }
2810
- return /* @__PURE__ */ jsx32("div", { role: "listitem", className: rowClass2, children: inner });
3560
+ return /* @__PURE__ */ jsx35("div", { role: "listitem", className: rowClass2, children: inner });
2811
3561
  };
2812
3562
  var connectionRowListClass = cn(
2813
3563
  "overflow-hidden rounded-2xl",
@@ -2815,12 +3565,12 @@ var connectionRowListClass = cn(
2815
3565
  );
2816
3566
 
2817
3567
  // src/app/integrations/ConnectionRowList.tsx
2818
- import { jsx as jsx33 } from "react/jsx-runtime";
3568
+ import { jsx as jsx36 } from "react/jsx-runtime";
2819
3569
  var ConnectionRowList = ({
2820
3570
  children,
2821
3571
  "aria-label": ariaLabel = "Connected integrations",
2822
3572
  className
2823
- }) => /* @__PURE__ */ jsx33(
3573
+ }) => /* @__PURE__ */ jsx36(
2824
3574
  "div",
2825
3575
  {
2826
3576
  role: "list",
@@ -2831,7 +3581,7 @@ var ConnectionRowList = ({
2831
3581
  );
2832
3582
 
2833
3583
  // src/app/navigation/SubNav.tsx
2834
- import { jsx as jsx34 } from "react/jsx-runtime";
3584
+ import { jsx as jsx37 } from "react/jsx-runtime";
2835
3585
  var SubNav = ({
2836
3586
  items,
2837
3587
  activeId,
@@ -2840,7 +3590,7 @@ var SubNav = ({
2840
3590
  "aria-label": ariaLabel = "Section navigation",
2841
3591
  layoutId
2842
3592
  }) => {
2843
- return /* @__PURE__ */ jsx34("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx34(
3593
+ return /* @__PURE__ */ jsx37("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx37(
2844
3594
  PillSegmentedTabs,
2845
3595
  {
2846
3596
  value: activeId,
@@ -2854,13 +3604,13 @@ var SubNav = ({
2854
3604
  };
2855
3605
 
2856
3606
  // src/app/navigation/Breadcrumbs.tsx
2857
- import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
3607
+ import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
2858
3608
  var Breadcrumbs = ({ items, className }) => {
2859
- return /* @__PURE__ */ jsx35("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx35("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
3609
+ return /* @__PURE__ */ jsx38("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx38("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
2860
3610
  const isLast = index === items.length - 1;
2861
- return /* @__PURE__ */ jsxs27("li", { className: "inline-flex items-center gap-1.5", children: [
2862
- index > 0 ? /* @__PURE__ */ jsx35("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
2863
- isLast ? /* @__PURE__ */ jsx35("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx35("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx35(
3611
+ return /* @__PURE__ */ jsxs29("li", { className: "inline-flex items-center gap-1.5", children: [
3612
+ index > 0 ? /* @__PURE__ */ jsx38("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
3613
+ isLast ? /* @__PURE__ */ jsx38("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx38("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx38(
2864
3614
  "button",
2865
3615
  {
2866
3616
  type: "button",
@@ -2874,8 +3624,8 @@ var Breadcrumbs = ({ items, className }) => {
2874
3624
  };
2875
3625
 
2876
3626
  // src/app/forms/Field.tsx
2877
- import { useId as useId4 } from "react";
2878
- import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
3627
+ import { useId as useId5 } from "react";
3628
+ import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
2879
3629
  var Field = ({
2880
3630
  label,
2881
3631
  hint,
@@ -2884,11 +3634,11 @@ var Field = ({
2884
3634
  className,
2885
3635
  htmlFor
2886
3636
  }) => {
2887
- return /* @__PURE__ */ jsxs28("div", { className: cn("aui-app-field", appFieldClass, className), children: [
2888
- /* @__PURE__ */ jsx36("label", { className: appFieldLabelClass, htmlFor, children: label }),
3637
+ return /* @__PURE__ */ jsxs30("div", { className: cn("aui-app-field", appFieldClass, className), children: [
3638
+ /* @__PURE__ */ jsx39("label", { className: appFieldLabelClass, htmlFor, children: label }),
2889
3639
  children,
2890
- hint && !error ? /* @__PURE__ */ jsx36("p", { className: appFieldHintClass, children: hint }) : null,
2891
- error ? /* @__PURE__ */ jsx36("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
3640
+ hint && !error ? /* @__PURE__ */ jsx39("p", { className: appFieldHintClass, children: hint }) : null,
3641
+ error ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
2892
3642
  ] });
2893
3643
  };
2894
3644
  var FieldInput = ({
@@ -2900,9 +3650,9 @@ var FieldInput = ({
2900
3650
  id,
2901
3651
  ...inputProps
2902
3652
  }) => {
2903
- const autoId = useId4();
3653
+ const autoId = useId5();
2904
3654
  const inputId = id ?? inputProps.name ?? autoId;
2905
- return /* @__PURE__ */ jsx36(
3655
+ return /* @__PURE__ */ jsx39(
2906
3656
  Field,
2907
3657
  {
2908
3658
  label,
@@ -2910,7 +3660,7 @@ var FieldInput = ({
2910
3660
  error,
2911
3661
  htmlFor: inputId,
2912
3662
  className: fieldClassName,
2913
- children: /* @__PURE__ */ jsx36(
3663
+ children: /* @__PURE__ */ jsx39(
2914
3664
  "input",
2915
3665
  {
2916
3666
  id: inputId,
@@ -2924,8 +3674,8 @@ var FieldInput = ({
2924
3674
  };
2925
3675
 
2926
3676
  // src/app/forms/FieldTextarea.tsx
2927
- import { useId as useId5 } from "react";
2928
- import { jsx as jsx37 } from "react/jsx-runtime";
3677
+ import { useId as useId6 } from "react";
3678
+ import { jsx as jsx40 } from "react/jsx-runtime";
2929
3679
  var textareaClass = cn(
2930
3680
  appInputClass,
2931
3681
  "min-h-[5.5rem] resize-y py-2.5 leading-relaxed"
@@ -2939,9 +3689,9 @@ var FieldTextarea = ({
2939
3689
  id,
2940
3690
  ...props
2941
3691
  }) => {
2942
- const autoId = useId5();
3692
+ const autoId = useId6();
2943
3693
  const textareaId = id ?? props.name ?? autoId;
2944
- return /* @__PURE__ */ jsx37(
3694
+ return /* @__PURE__ */ jsx40(
2945
3695
  Field,
2946
3696
  {
2947
3697
  label,
@@ -2949,7 +3699,7 @@ var FieldTextarea = ({
2949
3699
  error,
2950
3700
  htmlFor: textareaId,
2951
3701
  className: fieldClassName,
2952
- children: /* @__PURE__ */ jsx37(
3702
+ children: /* @__PURE__ */ jsx40(
2953
3703
  "textarea",
2954
3704
  {
2955
3705
  id: textareaId,
@@ -2963,9 +3713,9 @@ var FieldTextarea = ({
2963
3713
  };
2964
3714
 
2965
3715
  // src/app/forms/FieldSelect.tsx
2966
- import { useId as useId6 } from "react";
3716
+ import { useId as useId7 } from "react";
2967
3717
  import { ChevronDownIcon } from "lucide-react";
2968
- import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
3718
+ import { jsx as jsx41, jsxs as jsxs31 } from "react/jsx-runtime";
2969
3719
  var selectWrapClass = "relative";
2970
3720
  var selectClass = cn(
2971
3721
  appInputClass,
@@ -2981,9 +3731,9 @@ var FieldSelect = ({
2981
3731
  id,
2982
3732
  ...props
2983
3733
  }) => {
2984
- const autoId = useId6();
3734
+ const autoId = useId7();
2985
3735
  const selectId = id ?? props.name ?? autoId;
2986
- return /* @__PURE__ */ jsx38(
3736
+ return /* @__PURE__ */ jsx41(
2987
3737
  Field,
2988
3738
  {
2989
3739
  label,
@@ -2991,8 +3741,8 @@ var FieldSelect = ({
2991
3741
  error,
2992
3742
  htmlFor: selectId,
2993
3743
  className: fieldClassName,
2994
- children: /* @__PURE__ */ jsxs29("div", { className: selectWrapClass, children: [
2995
- /* @__PURE__ */ jsx38(
3744
+ children: /* @__PURE__ */ jsxs31("div", { className: selectWrapClass, children: [
3745
+ /* @__PURE__ */ jsx41(
2996
3746
  "select",
2997
3747
  {
2998
3748
  id: selectId,
@@ -3002,7 +3752,7 @@ var FieldSelect = ({
3002
3752
  children
3003
3753
  }
3004
3754
  ),
3005
- /* @__PURE__ */ jsx38(
3755
+ /* @__PURE__ */ jsx41(
3006
3756
  ChevronDownIcon,
3007
3757
  {
3008
3758
  className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground",
@@ -3015,8 +3765,8 @@ var FieldSelect = ({
3015
3765
  };
3016
3766
 
3017
3767
  // src/app/forms/FieldSwitch.tsx
3018
- import { useId as useId7 } from "react";
3019
- import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
3768
+ import { useId as useId8 } from "react";
3769
+ import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
3020
3770
  var trackClass = cn(
3021
3771
  "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
3022
3772
  "peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
@@ -3036,9 +3786,9 @@ var FieldSwitch = ({
3036
3786
  id,
3037
3787
  ...props
3038
3788
  }) => {
3039
- const autoId = useId7();
3789
+ const autoId = useId8();
3040
3790
  const inputId = id ?? props.name ?? autoId;
3041
- return /* @__PURE__ */ jsxs30(
3791
+ return /* @__PURE__ */ jsxs32(
3042
3792
  "label",
3043
3793
  {
3044
3794
  className: cn(
@@ -3047,8 +3797,8 @@ var FieldSwitch = ({
3047
3797
  ),
3048
3798
  htmlFor: inputId,
3049
3799
  children: [
3050
- /* @__PURE__ */ jsxs30("span", { className: "relative mt-0.5", children: [
3051
- /* @__PURE__ */ jsx39(
3800
+ /* @__PURE__ */ jsxs32("span", { className: "relative mt-0.5", children: [
3801
+ /* @__PURE__ */ jsx42(
3052
3802
  "input",
3053
3803
  {
3054
3804
  id: inputId,
@@ -3058,11 +3808,11 @@ var FieldSwitch = ({
3058
3808
  ...props
3059
3809
  }
3060
3810
  ),
3061
- /* @__PURE__ */ jsx39("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx39("span", { className: thumbClass }) })
3811
+ /* @__PURE__ */ jsx42("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx42("span", { className: thumbClass }) })
3062
3812
  ] }),
3063
- /* @__PURE__ */ jsxs30("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3064
- /* @__PURE__ */ jsx39("span", { className: "text-sm font-medium text-foreground", children: label }),
3065
- description ? /* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: description }) : null
3813
+ /* @__PURE__ */ jsxs32("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3814
+ /* @__PURE__ */ jsx42("span", { className: "text-sm font-medium text-foreground", children: label }),
3815
+ description ? /* @__PURE__ */ jsx42("span", { className: "text-xs text-muted-foreground", children: description }) : null
3066
3816
  ] })
3067
3817
  ]
3068
3818
  }
@@ -3071,13 +3821,13 @@ var FieldSwitch = ({
3071
3821
 
3072
3822
  // src/app/forms/SearchInput.tsx
3073
3823
  import { SearchIcon } from "lucide-react";
3074
- import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
3824
+ import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
3075
3825
  var SearchInput = ({
3076
3826
  className,
3077
3827
  placeholder = "Search\u2026",
3078
3828
  ...props
3079
3829
  }) => {
3080
- return /* @__PURE__ */ jsxs31(
3830
+ return /* @__PURE__ */ jsxs33(
3081
3831
  "label",
3082
3832
  {
3083
3833
  className: cn(
@@ -3086,8 +3836,8 @@ var SearchInput = ({
3086
3836
  className
3087
3837
  ),
3088
3838
  children: [
3089
- /* @__PURE__ */ jsx40(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
3090
- /* @__PURE__ */ jsx40(
3839
+ /* @__PURE__ */ jsx43(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
3840
+ /* @__PURE__ */ jsx43(
3091
3841
  "input",
3092
3842
  {
3093
3843
  type: "search",
@@ -3102,26 +3852,26 @@ var SearchInput = ({
3102
3852
  };
3103
3853
 
3104
3854
  // src/app/forms/FormSection.tsx
3105
- import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
3855
+ import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
3106
3856
  var FormSection = ({ title, children, className }) => {
3107
3857
  const density = useAppDensity();
3108
3858
  const sectionClass = useAppDensityClass("section");
3109
- return /* @__PURE__ */ jsxs32(
3859
+ return /* @__PURE__ */ jsxs34(
3110
3860
  "fieldset",
3111
3861
  {
3112
3862
  className: cn("aui-app-form-section", sectionClass, "border-0 p-0", className),
3113
3863
  children: [
3114
- title ? /* @__PURE__ */ jsx41("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
3115
- /* @__PURE__ */ jsx41("div", { className: cn("flex flex-col", density === "compact" ? "gap-2" : "gap-4"), children })
3864
+ title ? /* @__PURE__ */ jsx44("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
3865
+ /* @__PURE__ */ jsx44("div", { className: cn("flex flex-col", density === "compact" ? "gap-2" : "gap-4"), children })
3116
3866
  ]
3117
3867
  }
3118
3868
  );
3119
3869
  };
3120
3870
 
3121
3871
  // src/app/data/FilterBar.tsx
3122
- import { jsx as jsx42 } from "react/jsx-runtime";
3872
+ import { jsx as jsx45 } from "react/jsx-runtime";
3123
3873
  var FilterBar = ({ children, className }) => {
3124
- return /* @__PURE__ */ jsx42(
3874
+ return /* @__PURE__ */ jsx45(
3125
3875
  "div",
3126
3876
  {
3127
3877
  className: cn("aui-app-filter-bar", appFilterBarClass, className),
@@ -3133,41 +3883,501 @@ var FilterBar = ({ children, className }) => {
3133
3883
  };
3134
3884
 
3135
3885
  // src/app/data/FilterField.tsx
3136
- import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
3886
+ import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
3137
3887
  var FilterField = ({
3138
3888
  label,
3139
3889
  children,
3140
3890
  className
3141
3891
  }) => {
3142
- return /* @__PURE__ */ jsxs33("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
3143
- label ? /* @__PURE__ */ jsx43("span", { className: appFieldLabelClass, children: label }) : null,
3892
+ return /* @__PURE__ */ jsxs35("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
3893
+ label ? /* @__PURE__ */ jsx46("span", { className: appFieldLabelClass, children: label }) : null,
3144
3894
  children
3145
3895
  ] });
3146
3896
  };
3147
3897
 
3898
+ // src/app/data/FilterDropdown.tsx
3899
+ import { useEffect as useEffect3, useMemo as useMemo2, useState as useState5 } from "react";
3900
+ import { ChevronRightIcon, ListFilterIcon, XIcon as XIcon2 } from "lucide-react";
3901
+ import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
3902
+ var DEFAULT_PRESETS = [
3903
+ { id: "last_7_days", label: "Last 7 days" },
3904
+ { id: "last_30_days", label: "Last 30 days" },
3905
+ { id: "last_90_days", label: "Last 90 days" },
3906
+ { id: "this_month", label: "This month" },
3907
+ { id: "this_year", label: "This year" },
3908
+ { id: "custom", label: "Custom range" }
3909
+ ];
3910
+ var DEFAULT_OPERATORS = [
3911
+ { id: "gt", label: "Greater than" },
3912
+ { id: "lt", label: "Less than" },
3913
+ { id: "eq", label: "Equals" }
3914
+ ];
3915
+ function asArray(v) {
3916
+ return Array.isArray(v) ? v : [];
3917
+ }
3918
+ function asText(v) {
3919
+ return typeof v === "string" ? v : "";
3920
+ }
3921
+ function asDate(v) {
3922
+ return v && !Array.isArray(v) && typeof v === "object" && "preset" in v ? v : { preset: null };
3923
+ }
3924
+ function asNumeric(v) {
3925
+ return v && !Array.isArray(v) && typeof v === "object" && "operator" in v ? v : { operator: "gt", value: "" };
3926
+ }
3927
+ var OPERATOR_SYMBOL = {
3928
+ gt: ">",
3929
+ lt: "<",
3930
+ eq: "="
3931
+ };
3932
+ function FilterDropdown({
3933
+ fields,
3934
+ value,
3935
+ defaultValue,
3936
+ onChange,
3937
+ label = "Filter",
3938
+ align = "start",
3939
+ showActiveChips = true,
3940
+ className
3941
+ }) {
3942
+ const [isOpen, setIsOpen] = useState5(false);
3943
+ const [activeId, setActiveId] = useState5(fields[0]?.id ?? null);
3944
+ const [isMobile, setIsMobile] = useState5(false);
3945
+ const isControlled = value !== void 0;
3946
+ const [internal, setInternal] = useState5(defaultValue ?? {});
3947
+ const values = isControlled ? value : internal;
3948
+ useEffect3(() => {
3949
+ const checkMobile = () => setIsMobile(window.innerWidth < 768);
3950
+ checkMobile();
3951
+ window.addEventListener("resize", checkMobile);
3952
+ return () => window.removeEventListener("resize", checkMobile);
3953
+ }, []);
3954
+ useEffect3(() => {
3955
+ if (!fields.some((f) => f.id === activeId)) {
3956
+ setActiveId(fields[0]?.id ?? null);
3957
+ }
3958
+ }, [fields, activeId]);
3959
+ const commit = (id, next) => {
3960
+ const merged = { ...values, [id]: next };
3961
+ if (!isControlled) setInternal(merged);
3962
+ onChange?.(merged);
3963
+ };
3964
+ const clearAll = () => {
3965
+ if (!isControlled) setInternal({});
3966
+ onChange?.({});
3967
+ };
3968
+ const activeIdx = fields.findIndex((f) => f.id === activeId);
3969
+ const activeField = activeIdx >= 0 ? fields[activeIdx] : void 0;
3970
+ const chips = [];
3971
+ for (const field of fields) {
3972
+ const v = values[field.id];
3973
+ if (field.type === "multiselect") {
3974
+ const selected = asArray(v);
3975
+ for (const optionValue of selected) {
3976
+ const opt = field.options?.find((o) => o.value === optionValue);
3977
+ chips.push({
3978
+ id: `${field.id}:${optionValue}`,
3979
+ label: `${field.label}: ${opt?.label ?? optionValue}`,
3980
+ remove: () => commit(field.id, selected.filter((x) => x !== optionValue))
3981
+ });
3982
+ }
3983
+ } else if (field.type === "text") {
3984
+ const text = asText(v);
3985
+ if (text) {
3986
+ chips.push({
3987
+ id: field.id,
3988
+ label: `${field.label}: ${text}`,
3989
+ remove: () => commit(field.id, "")
3990
+ });
3991
+ }
3992
+ } else if (field.type === "numeric") {
3993
+ const n = asNumeric(v);
3994
+ if (n.value) {
3995
+ chips.push({
3996
+ id: field.id,
3997
+ label: `${field.label} ${OPERATOR_SYMBOL[n.operator]} ${n.value}`,
3998
+ remove: () => commit(field.id, null)
3999
+ });
4000
+ }
4001
+ } else if (field.type === "daterange") {
4002
+ const d = asDate(v);
4003
+ if (d.preset) {
4004
+ const presetLabel = d.preset === "custom" ? `${d.from || "\u2026"} \u2013 ${d.to || "\u2026"}` : (field.presets ?? DEFAULT_PRESETS).find((p) => p.id === d.preset)?.label ?? d.preset;
4005
+ chips.push({
4006
+ id: field.id,
4007
+ label: `${field.label}: ${presetLabel}`,
4008
+ remove: () => commit(field.id, { preset: null })
4009
+ });
4010
+ }
4011
+ }
4012
+ }
4013
+ return /* @__PURE__ */ jsxs36("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [
4014
+ /* @__PURE__ */ jsxs36(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
4015
+ /* @__PURE__ */ jsx47(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx47(
4016
+ Button,
4017
+ {
4018
+ variant: "outline",
4019
+ size: "sm",
4020
+ className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
4021
+ iconLeading: /* @__PURE__ */ jsx47(ListFilterIcon, { className: "size-4" }),
4022
+ children: label
4023
+ }
4024
+ ) }),
4025
+ /* @__PURE__ */ jsx47(
4026
+ PopoverContent,
4027
+ {
4028
+ variant: "list",
4029
+ align,
4030
+ className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
4031
+ children: /* @__PURE__ */ jsxs36("div", { className: "relative flex flex-col md:flex-row items-stretch md:items-start w-[calc(100vw-32px)] max-w-[340px] md:w-auto md:max-w-none", children: [
4032
+ /* @__PURE__ */ jsx47("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: fields.map((field) => {
4033
+ const isActive = activeId === field.id;
4034
+ return /* @__PURE__ */ jsxs36(
4035
+ "button",
4036
+ {
4037
+ type: "button",
4038
+ className: cn(
4039
+ "flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
4040
+ isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
4041
+ ),
4042
+ onMouseEnter: () => !isMobile && setActiveId(field.id),
4043
+ onClick: () => setActiveId(field.id),
4044
+ children: [
4045
+ /* @__PURE__ */ jsxs36("span", { className: "flex items-center gap-2", children: [
4046
+ field.icon,
4047
+ /* @__PURE__ */ jsx47("span", { children: field.label })
4048
+ ] }),
4049
+ /* @__PURE__ */ jsx47(ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
4050
+ ]
4051
+ },
4052
+ field.id
4053
+ );
4054
+ }) }),
4055
+ activeField && /* @__PURE__ */ jsx47(
4056
+ "div",
4057
+ {
4058
+ className: "relative left-0 mt-2 w-full md:absolute md:left-[calc(100%+6px)] md:w-80 rounded-xl border border-border bg-popover p-3 shadow-lg transition-all duration-150 md:mt-0",
4059
+ style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
4060
+ children: /* @__PURE__ */ jsx47(
4061
+ FilterFieldControl,
4062
+ {
4063
+ field: activeField,
4064
+ value: values[activeField.id],
4065
+ onChange: (next) => commit(activeField.id, next),
4066
+ onClose: () => setIsOpen(false)
4067
+ }
4068
+ )
4069
+ }
4070
+ )
4071
+ ] })
4072
+ }
4073
+ )
4074
+ ] }),
4075
+ showActiveChips && chips.map((chip) => /* @__PURE__ */ jsx47(FilterChip, { label: chip.label, onRemove: chip.remove }, chip.id)),
4076
+ showActiveChips && chips.length > 0 && /* @__PURE__ */ jsx47(
4077
+ "button",
4078
+ {
4079
+ type: "button",
4080
+ onClick: clearAll,
4081
+ className: "rounded-full px-3 py-1 text-sm font-medium text-muted-foreground outline-none transition-colors hover:text-foreground",
4082
+ children: "Clear all"
4083
+ }
4084
+ )
4085
+ ] });
4086
+ }
4087
+ function FilterChip({ label, onRemove }) {
4088
+ return /* @__PURE__ */ jsxs36("span", { className: "inline-flex h-9 items-center gap-1.5 rounded-full border border-border bg-muted/40 pl-3 pr-1.5 text-sm font-medium text-foreground", children: [
4089
+ /* @__PURE__ */ jsx47("span", { className: "truncate", children: label }),
4090
+ /* @__PURE__ */ jsx47(
4091
+ "button",
4092
+ {
4093
+ type: "button",
4094
+ onClick: onRemove,
4095
+ "aria-label": `Remove ${label}`,
4096
+ className: "flex size-5 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground",
4097
+ children: /* @__PURE__ */ jsx47(XIcon2, { className: "size-3.5" })
4098
+ }
4099
+ )
4100
+ ] });
4101
+ }
4102
+ function FilterFieldControl({
4103
+ field,
4104
+ value,
4105
+ onChange,
4106
+ onClose
4107
+ }) {
4108
+ switch (field.type) {
4109
+ case "multiselect":
4110
+ return /* @__PURE__ */ jsx47(MultiSelectControl, { field, value: asArray(value), onChange });
4111
+ case "text":
4112
+ return /* @__PURE__ */ jsx47(TextControl, { field, value: asText(value), onChange, onClose });
4113
+ case "daterange":
4114
+ return /* @__PURE__ */ jsx47(DateRangeControl, { field, value: asDate(value), onChange, onClose });
4115
+ case "numeric":
4116
+ return /* @__PURE__ */ jsx47(NumericControl, { field, value: asNumeric(value), onChange, onClose });
4117
+ default:
4118
+ return null;
4119
+ }
4120
+ }
4121
+ function MultiSelectControl({
4122
+ field,
4123
+ value,
4124
+ onChange
4125
+ }) {
4126
+ const options = field.options ?? [];
4127
+ const [search, setSearch] = useState5("");
4128
+ const searchable = field.searchable ?? options.length > 8;
4129
+ const filtered = useMemo2(() => {
4130
+ if (!search) return options;
4131
+ const q = search.toLowerCase();
4132
+ return options.filter(
4133
+ (o) => o.label.toLowerCase().includes(q) || o.hint?.toLowerCase().includes(q)
4134
+ );
4135
+ }, [options, search]);
4136
+ const toggle = (optionValue) => {
4137
+ onChange(
4138
+ value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
4139
+ );
4140
+ };
4141
+ return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4142
+ searchable && /* @__PURE__ */ jsx47(
4143
+ SearchInput,
4144
+ {
4145
+ placeholder: field.searchPlaceholder ?? "Search\u2026",
4146
+ value: search,
4147
+ onChange: (e) => setSearch(e.target.value),
4148
+ className: "w-full min-w-0"
4149
+ }
4150
+ ),
4151
+ /* @__PURE__ */ jsx47("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto pr-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx47("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No options found" }) : filtered.map((option) => /* @__PURE__ */ jsxs36(
4152
+ "label",
4153
+ {
4154
+ className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors hover:bg-muted/50",
4155
+ children: [
4156
+ /* @__PURE__ */ jsx47(
4157
+ Checkbox,
4158
+ {
4159
+ checked: value.includes(option.value),
4160
+ onCheckedChange: () => toggle(option.value)
4161
+ }
4162
+ ),
4163
+ option.icon,
4164
+ /* @__PURE__ */ jsxs36("span", { className: "flex min-w-0 flex-col", children: [
4165
+ /* @__PURE__ */ jsx47("span", { className: "truncate text-sm font-medium text-foreground", children: option.label }),
4166
+ option.hint && /* @__PURE__ */ jsx47("span", { className: "truncate text-xs text-muted-foreground", children: option.hint })
4167
+ ] })
4168
+ ]
4169
+ },
4170
+ option.value
4171
+ )) })
4172
+ ] });
4173
+ }
4174
+ function TextControl({
4175
+ field,
4176
+ value,
4177
+ onChange,
4178
+ onClose
4179
+ }) {
4180
+ const [draft, setDraft] = useState5(value);
4181
+ useEffect3(() => setDraft(value), [value]);
4182
+ return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4183
+ /* @__PURE__ */ jsx47(
4184
+ "input",
4185
+ {
4186
+ type: "text",
4187
+ placeholder: field.placeholder ?? "Type a value\u2026",
4188
+ value: draft,
4189
+ onChange: (e) => setDraft(e.target.value),
4190
+ onKeyDown: (e) => {
4191
+ if (e.key === "Enter") {
4192
+ onChange(draft);
4193
+ onClose();
4194
+ }
4195
+ },
4196
+ className: controlClass({ size: "sm" }, "w-full")
4197
+ }
4198
+ ),
4199
+ /* @__PURE__ */ jsx47(
4200
+ ApplyClear,
4201
+ {
4202
+ onClear: () => {
4203
+ setDraft("");
4204
+ onChange("");
4205
+ onClose();
4206
+ },
4207
+ onApply: () => {
4208
+ onChange(draft);
4209
+ onClose();
4210
+ }
4211
+ }
4212
+ )
4213
+ ] });
4214
+ }
4215
+ function DateRangeControl({
4216
+ field,
4217
+ value,
4218
+ onChange,
4219
+ onClose
4220
+ }) {
4221
+ const presets = field.presets ?? DEFAULT_PRESETS;
4222
+ const [from, setFrom] = useState5(value.from ?? "");
4223
+ const [to, setTo] = useState5(value.to ?? "");
4224
+ return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-1", children: [
4225
+ presets.map((preset) => {
4226
+ const isSelected = value.preset === preset.id;
4227
+ return /* @__PURE__ */ jsxs36(
4228
+ "button",
4229
+ {
4230
+ type: "button",
4231
+ onClick: () => {
4232
+ if (preset.id === "custom") {
4233
+ onChange({ preset: "custom", from, to });
4234
+ } else {
4235
+ onChange({ preset: preset.id });
4236
+ onClose();
4237
+ }
4238
+ },
4239
+ className: cn(
4240
+ "flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-left text-sm transition-colors outline-none",
4241
+ isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
4242
+ ),
4243
+ children: [
4244
+ /* @__PURE__ */ jsx47("span", { children: preset.label }),
4245
+ preset.hint && /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground/70", children: preset.hint })
4246
+ ]
4247
+ },
4248
+ preset.id
4249
+ );
4250
+ }),
4251
+ value.preset === "custom" && /* @__PURE__ */ jsxs36("div", { className: "mt-2 flex flex-col gap-2 border-t border-border/40 pt-2", children: [
4252
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
4253
+ /* @__PURE__ */ jsx47(
4254
+ "input",
4255
+ {
4256
+ type: "date",
4257
+ value: from,
4258
+ onChange: (e) => setFrom(e.target.value),
4259
+ className: controlClass({ size: "sm" }, "w-full text-xs")
4260
+ }
4261
+ ),
4262
+ /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: "to" }),
4263
+ /* @__PURE__ */ jsx47(
4264
+ "input",
4265
+ {
4266
+ type: "date",
4267
+ value: to,
4268
+ onChange: (e) => setTo(e.target.value),
4269
+ className: controlClass({ size: "sm" }, "w-full text-xs")
4270
+ }
4271
+ )
4272
+ ] }),
4273
+ /* @__PURE__ */ jsx47("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx47(
4274
+ Button,
4275
+ {
4276
+ color: "primary",
4277
+ size: "sm",
4278
+ className: "h-8 px-3",
4279
+ onClick: () => {
4280
+ onChange({ preset: "custom", from, to });
4281
+ onClose();
4282
+ },
4283
+ children: "Apply"
4284
+ }
4285
+ ) })
4286
+ ] })
4287
+ ] });
4288
+ }
4289
+ function NumericControl({
4290
+ field,
4291
+ value,
4292
+ onChange,
4293
+ onClose
4294
+ }) {
4295
+ const operators = field.operators ?? DEFAULT_OPERATORS;
4296
+ const [operator, setOperator] = useState5(value.operator);
4297
+ const [draft, setDraft] = useState5(value.value);
4298
+ useEffect3(() => {
4299
+ setOperator(value.operator);
4300
+ setDraft(value.value);
4301
+ }, [value.operator, value.value]);
4302
+ return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4303
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
4304
+ /* @__PURE__ */ jsxs36(Select, { value: operator, onValueChange: (v) => setOperator(v), children: [
4305
+ /* @__PURE__ */ jsx47(SelectTrigger, { size: "sm", className: "shrink-0", children: /* @__PURE__ */ jsx47(SelectValue, {}) }),
4306
+ /* @__PURE__ */ jsx47(SelectContent, { children: operators.map((op) => /* @__PURE__ */ jsx47(SelectItem, { value: op.id, children: op.label }, op.id)) })
4307
+ ] }),
4308
+ /* @__PURE__ */ jsx47(
4309
+ "input",
4310
+ {
4311
+ type: "text",
4312
+ inputMode: "decimal",
4313
+ placeholder: field.placeholder ?? "0.00",
4314
+ value: draft,
4315
+ onChange: (e) => setDraft(e.target.value),
4316
+ onKeyDown: (e) => {
4317
+ if (e.key === "Enter") {
4318
+ onChange(draft ? { operator, value: draft } : null);
4319
+ onClose();
4320
+ }
4321
+ },
4322
+ className: controlClass({ size: "sm" }, "min-w-0 flex-1")
4323
+ }
4324
+ )
4325
+ ] }),
4326
+ /* @__PURE__ */ jsx47(
4327
+ ApplyClear,
4328
+ {
4329
+ onClear: () => {
4330
+ setDraft("");
4331
+ onChange(null);
4332
+ onClose();
4333
+ },
4334
+ onApply: () => {
4335
+ onChange(draft ? { operator, value: draft } : null);
4336
+ onClose();
4337
+ }
4338
+ }
4339
+ )
4340
+ ] });
4341
+ }
4342
+ function ApplyClear({ onClear, onApply }) {
4343
+ return /* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 border-t border-border/40 pt-1", children: [
4344
+ /* @__PURE__ */ jsx47(
4345
+ Button,
4346
+ {
4347
+ variant: "ghost",
4348
+ size: "sm",
4349
+ onClick: onClear,
4350
+ className: "h-8 px-3 text-muted-foreground hover:text-foreground",
4351
+ children: "Clear"
4352
+ }
4353
+ ),
4354
+ /* @__PURE__ */ jsx47(Button, { color: "primary", size: "sm", onClick: onApply, className: "h-8 px-3", children: "Apply" })
4355
+ ] });
4356
+ }
4357
+
3148
4358
  // src/app/data/DataTable.tsx
3149
- import { useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
4359
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState6 } from "react";
3150
4360
  import {
3151
4361
  ArrowDownIcon,
3152
4362
  ArrowUpDownIcon,
3153
4363
  ArrowUpIcon,
3154
4364
  ChevronLeftIcon,
3155
- ChevronRightIcon
4365
+ ChevronRightIcon as ChevronRightIcon2
3156
4366
  } from "lucide-react";
3157
- import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
3158
- var shellClass2 = "overflow-hidden rounded-xl border border-border bg-gradient-to-b from-elevated-from to-elevated-to shadow-card";
3159
- var tableClass = "w-full border-collapse bg-transparent text-sm";
3160
- var headRowClass = "bg-muted/40";
3161
- var headCellClass = "border-b border-border/70 px-4 py-2.5 text-left text-[11px] font-semibold uppercase tracking-[0.04em] text-muted-foreground/90";
3162
- var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-2.5 text-foreground";
3163
- var rowClass = "bg-transparent transition-colors hover:bg-muted/40 data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.06]";
3164
- var footCellClass = "border-t border-border/60 bg-muted/20 px-4 py-2.5 text-xs text-muted-foreground";
4367
+ import { jsx as jsx48, jsxs as jsxs37 } from "react/jsx-runtime";
4368
+ var shellClass2 = "w-full";
4369
+ var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
4370
+ var headRowClass = "";
4371
+ var headCellClass = "bg-gradient-to-b from-white to-[#F9FAFB] dark:from-[#1F242F] dark:to-[#10121C] border-t border-b border-border px-4 py-2.5 text-left text-xs font-medium text-muted-foreground/90 first:rounded-l-lg first:border-l last:rounded-r-lg last:border-r shadow-skeuomorphic-bordered";
4372
+ var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-4 text-foreground";
4373
+ var rowClass = "bg-transparent transition-colors hover:bg-muted/20 data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.04]";
4374
+ var footCellClass = "border-t border-border/40 bg-transparent px-4 py-3 text-xs text-muted-foreground";
3165
4375
  var footInnerClass = "flex flex-wrap items-center gap-2";
3166
4376
  var emptyCellClass = "bg-transparent px-4 py-10 text-center text-sm text-muted-foreground";
3167
- 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-semibold text-muted-foreground/90 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
3168
- var stickyHeadClass = "sticky top-0 z-[1] bg-muted/95 shadow-[0_1px_0_0_hsl(var(--border)/0.6)] backdrop-blur-sm";
3169
- var selectCellClass = "w-10 px-4 py-2.5 align-middle";
3170
- var pagerButtonClass = "inline-flex size-7 items-center justify-center rounded-md border border-border bg-gradient-to-b from-elevated-from to-elevated-to text-muted-foreground shadow-card transition-colors hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to hover:text-foreground disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
4377
+ 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";
4378
+ var stickyHeadClass = "sticky top-0 z-[1] bg-background/95 shadow-[0_1px_0_0_hsl(var(--border)/0.4)] backdrop-blur-sm";
4379
+ var selectCellClass = "w-10 px-4 py-4 align-middle";
4380
+ var pagerButtonClass = "inline-flex size-7 items-center justify-center rounded-md border border-border bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
3171
4381
  var alignClass = {
3172
4382
  left: "text-left",
3173
4383
  center: "text-center",
@@ -3197,12 +4407,12 @@ function SortIndicator({
3197
4407
  }) {
3198
4408
  const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
3199
4409
  if (!active) {
3200
- return /* @__PURE__ */ jsx44(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
4410
+ return /* @__PURE__ */ jsx48(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
3201
4411
  }
3202
4412
  if (direction === "desc") {
3203
- return /* @__PURE__ */ jsx44(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
4413
+ return /* @__PURE__ */ jsx48(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
3204
4414
  }
3205
- return /* @__PURE__ */ jsx44(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
4415
+ return /* @__PURE__ */ jsx48(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
3206
4416
  }
3207
4417
  function DataTable({
3208
4418
  columns,
@@ -3234,7 +4444,7 @@ function DataTable({
3234
4444
  defaultPageIndex = 0,
3235
4445
  onPageChange
3236
4446
  }) {
3237
- const [uncontrolledSort, setUncontrolledSort] = useState4(
4447
+ const [uncontrolledSort, setUncontrolledSort] = useState6(
3238
4448
  defaultSort
3239
4449
  );
3240
4450
  const isSortControlled = sortProp !== void 0;
@@ -3245,19 +4455,19 @@ function DataTable({
3245
4455
  }
3246
4456
  onSortChange?.(next);
3247
4457
  };
3248
- const [uncontrolledSelected, setUncontrolledSelected] = useState4(
4458
+ const [uncontrolledSelected, setUncontrolledSelected] = useState6(
3249
4459
  defaultSelectedKeys ?? []
3250
4460
  );
3251
4461
  const isSelectionControlled = selectedKeysProp !== void 0;
3252
4462
  const selectedKeys = isSelectionControlled ? selectedKeysProp : uncontrolledSelected;
3253
- const selectedSet = useMemo2(() => new Set(selectedKeys), [selectedKeys]);
4463
+ const selectedSet = useMemo3(() => new Set(selectedKeys), [selectedKeys]);
3254
4464
  const setSelected = (next) => {
3255
4465
  if (!isSelectionControlled) {
3256
4466
  setUncontrolledSelected(next);
3257
4467
  }
3258
4468
  onSelectionChange?.(next);
3259
4469
  };
3260
- const [uncontrolledPage, setUncontrolledPage] = useState4(defaultPageIndex);
4470
+ const [uncontrolledPage, setUncontrolledPage] = useState6(defaultPageIndex);
3261
4471
  const isPageControlled = pageIndexProp !== void 0;
3262
4472
  const rawPageIndex = isPageControlled ? pageIndexProp : uncontrolledPage;
3263
4473
  const setPage = (next) => {
@@ -3266,7 +4476,7 @@ function DataTable({
3266
4476
  }
3267
4477
  onPageChange?.(next);
3268
4478
  };
3269
- const sortedRows = useMemo2(() => {
4479
+ const sortedRows = useMemo3(() => {
3270
4480
  if (!sort) return rows;
3271
4481
  const column = columns.find((col) => col.id === sort.columnId);
3272
4482
  if (!column?.sortable) return rows;
@@ -3285,13 +4495,13 @@ function DataTable({
3285
4495
  const paginated = typeof pageSize === "number" && pageSize > 0;
3286
4496
  const pageCount = paginated ? Math.max(1, Math.ceil(sortedRows.length / pageSize)) : 1;
3287
4497
  const pageIndex = Math.min(Math.max(0, rawPageIndex), pageCount - 1);
3288
- useEffect2(() => {
4498
+ useEffect4(() => {
3289
4499
  if (!paginated || isPageControlled) return;
3290
4500
  if (uncontrolledPage > pageCount - 1) {
3291
4501
  setUncontrolledPage(pageCount - 1);
3292
4502
  }
3293
4503
  }, [paginated, isPageControlled, uncontrolledPage, pageCount]);
3294
- const visibleRows = useMemo2(() => {
4504
+ const visibleRows = useMemo3(() => {
3295
4505
  if (!paginated) return sortedRows;
3296
4506
  const start = pageIndex * pageSize;
3297
4507
  return sortedRows.slice(start, start + pageSize);
@@ -3300,7 +4510,7 @@ function DataTable({
3300
4510
  const headPad = dense ? "px-3 py-2" : void 0;
3301
4511
  const colSpan = columns.length + (selectable ? 1 : 0);
3302
4512
  if (!loading && rows.length === 0 && emptyMode === "replace") {
3303
- return /* @__PURE__ */ jsx44(EmptyState, { title: emptyTitle, description: emptyDescription, className });
4513
+ return /* @__PURE__ */ jsx48(EmptyState, { title: emptyTitle, description: emptyDescription, className });
3304
4514
  }
3305
4515
  const allKeys = sortedRows.map(getRowKey);
3306
4516
  const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
@@ -3325,10 +4535,10 @@ function DataTable({
3325
4535
  const hasPager = paginated && !loading && sortedRows.length > 0;
3326
4536
  const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
3327
4537
  const skeletonCount = loadingRows ?? pageSize ?? 5;
3328
- return /* @__PURE__ */ jsx44("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx44("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs34("table", { className: tableClass, children: [
3329
- caption ? /* @__PURE__ */ jsx44("caption", { className: "sr-only", children: caption }) : null,
3330
- /* @__PURE__ */ jsx44("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs34("tr", { children: [
3331
- selectable ? /* @__PURE__ */ jsx44("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx44(
4538
+ return /* @__PURE__ */ jsx48("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx48("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs37("table", { className: tableClass, children: [
4539
+ caption ? /* @__PURE__ */ jsx48("caption", { className: "sr-only", children: caption }) : null,
4540
+ /* @__PURE__ */ jsx48("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs37("tr", { children: [
4541
+ selectable ? /* @__PURE__ */ jsx48("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx48(
3332
4542
  Checkbox,
3333
4543
  {
3334
4544
  checked: headerCheckedState,
@@ -3340,19 +4550,19 @@ function DataTable({
3340
4550
  columns.map((col) => {
3341
4551
  const isSorted = sort?.columnId === col.id;
3342
4552
  const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
3343
- const headerContent = col.sortable ? /* @__PURE__ */ jsxs34(
4553
+ const headerContent = col.sortable ? /* @__PURE__ */ jsxs37(
3344
4554
  "button",
3345
4555
  {
3346
4556
  type: "button",
3347
4557
  className: sortButtonClass,
3348
4558
  onClick: () => setSort(nextSort(sort, col.id)),
3349
4559
  children: [
3350
- /* @__PURE__ */ jsx44("span", { className: "truncate", children: col.header }),
3351
- /* @__PURE__ */ jsx44(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
4560
+ /* @__PURE__ */ jsx48("span", { className: "truncate", children: col.header }),
4561
+ /* @__PURE__ */ jsx48(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
3352
4562
  ]
3353
4563
  }
3354
4564
  ) : col.header;
3355
- return /* @__PURE__ */ jsx44(
4565
+ return /* @__PURE__ */ jsx48(
3356
4566
  "th",
3357
4567
  {
3358
4568
  scope: "col",
@@ -3369,9 +4579,9 @@ function DataTable({
3369
4579
  );
3370
4580
  })
3371
4581
  ] }) }),
3372
- /* @__PURE__ */ jsx44("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ jsxs34("tr", { className: rowClass, "aria-hidden": true, children: [
3373
- selectable ? /* @__PURE__ */ jsx44("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx44(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
3374
- columns.map((col) => /* @__PURE__ */ jsx44(
4582
+ /* @__PURE__ */ jsx48("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ jsxs37("tr", { className: rowClass, "aria-hidden": true, children: [
4583
+ selectable ? /* @__PURE__ */ jsx48("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx48(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
4584
+ columns.map((col) => /* @__PURE__ */ jsx48(
3375
4585
  "td",
3376
4586
  {
3377
4587
  className: cn(
@@ -3380,17 +4590,17 @@ function DataTable({
3380
4590
  col.align && alignClass[col.align],
3381
4591
  col.className
3382
4592
  ),
3383
- children: /* @__PURE__ */ jsx44(Skeleton, { className: "h-4 w-[60%]" })
4593
+ children: /* @__PURE__ */ jsx48(Skeleton, { className: "h-4 w-[60%]" })
3384
4594
  },
3385
4595
  col.id
3386
4596
  ))
3387
- ] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ jsx44("tr", { children: /* @__PURE__ */ jsx44("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ jsxs34("div", { className: "flex flex-col items-center gap-1", children: [
3388
- /* @__PURE__ */ jsx44("p", { className: "font-medium text-foreground", children: emptyTitle }),
3389
- emptyDescription ? /* @__PURE__ */ jsx44("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
4597
+ ] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ jsx48("tr", { children: /* @__PURE__ */ jsx48("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ jsxs37("div", { className: "flex flex-col items-center gap-1", children: [
4598
+ /* @__PURE__ */ jsx48("p", { className: "font-medium text-foreground", children: emptyTitle }),
4599
+ emptyDescription ? /* @__PURE__ */ jsx48("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
3390
4600
  ] }) }) }) : visibleRows.map((row) => {
3391
4601
  const key = getRowKey(row);
3392
4602
  const isSelected = selectedSet.has(key);
3393
- return /* @__PURE__ */ jsxs34(
4603
+ return /* @__PURE__ */ jsxs37(
3394
4604
  "tr",
3395
4605
  {
3396
4606
  className: rowClass,
@@ -3406,12 +4616,12 @@ function DataTable({
3406
4616
  tabIndex: onRowClick ? 0 : void 0,
3407
4617
  role: onRowClick ? "button" : void 0,
3408
4618
  children: [
3409
- selectable ? /* @__PURE__ */ jsx44(
4619
+ selectable ? /* @__PURE__ */ jsx48(
3410
4620
  "td",
3411
4621
  {
3412
4622
  className: cn(selectCellClass, cellPad),
3413
4623
  onClick: (event) => event.stopPropagation(),
3414
- children: /* @__PURE__ */ jsx44(
4624
+ children: /* @__PURE__ */ jsx48(
3415
4625
  Checkbox,
3416
4626
  {
3417
4627
  checked: isSelected,
@@ -3421,7 +4631,7 @@ function DataTable({
3421
4631
  )
3422
4632
  }
3423
4633
  ) : null,
3424
- columns.map((col) => /* @__PURE__ */ jsx44(
4634
+ columns.map((col) => /* @__PURE__ */ jsx48(
3425
4635
  "td",
3426
4636
  {
3427
4637
  className: cn(
@@ -3431,7 +4641,7 @@ function DataTable({
3431
4641
  col.align && alignClass[col.align],
3432
4642
  col.className
3433
4643
  ),
3434
- children: col.truncate ? /* @__PURE__ */ jsx44("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
4644
+ children: col.truncate ? /* @__PURE__ */ jsx48("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
3435
4645
  },
3436
4646
  col.id
3437
4647
  ))
@@ -3440,7 +4650,7 @@ function DataTable({
3440
4650
  key
3441
4651
  );
3442
4652
  }) }),
3443
- hasFoot ? /* @__PURE__ */ jsx44("tfoot", { children: /* @__PURE__ */ jsx44("tr", { children: /* @__PURE__ */ jsx44("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs34(
4653
+ hasFoot ? /* @__PURE__ */ jsx48("tfoot", { children: /* @__PURE__ */ jsx48("tr", { children: /* @__PURE__ */ jsx48("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs37(
3444
4654
  "div",
3445
4655
  {
3446
4656
  className: cn(
@@ -3448,18 +4658,18 @@ function DataTable({
3448
4658
  (showRowCount || footer || hasPager) && "justify-between"
3449
4659
  ),
3450
4660
  children: [
3451
- /* @__PURE__ */ jsxs34("div", { className: footInnerClass, children: [
3452
- showRowCount ? /* @__PURE__ */ jsxs34("span", { children: [
4661
+ /* @__PURE__ */ jsxs37("div", { className: footInnerClass, children: [
4662
+ showRowCount ? /* @__PURE__ */ jsxs37("span", { children: [
3453
4663
  rowCountText,
3454
4664
  selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
3455
- ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs34("span", { children: [
4665
+ ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs37("span", { children: [
3456
4666
  selectedSet.size,
3457
4667
  " selected"
3458
4668
  ] }) : null,
3459
4669
  footer
3460
4670
  ] }),
3461
- hasPager ? /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2", children: [
3462
- /* @__PURE__ */ jsxs34("span", { className: "tabular-nums", children: [
4671
+ hasPager ? /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2", children: [
4672
+ /* @__PURE__ */ jsxs37("span", { className: "tabular-nums", children: [
3463
4673
  pageIndex * pageSize + 1,
3464
4674
  "\u2013",
3465
4675
  Math.min(
@@ -3470,8 +4680,8 @@ function DataTable({
3470
4680
  "of ",
3471
4681
  sortedRows.length
3472
4682
  ] }),
3473
- /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-1", children: [
3474
- /* @__PURE__ */ jsx44(
4683
+ /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1", children: [
4684
+ /* @__PURE__ */ jsx48(
3475
4685
  "button",
3476
4686
  {
3477
4687
  type: "button",
@@ -3479,10 +4689,10 @@ function DataTable({
3479
4689
  onClick: () => setPage(pageIndex - 1),
3480
4690
  disabled: pageIndex <= 0,
3481
4691
  "aria-label": "Previous page",
3482
- children: /* @__PURE__ */ jsx44(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
4692
+ children: /* @__PURE__ */ jsx48(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
3483
4693
  }
3484
4694
  ),
3485
- /* @__PURE__ */ jsx44(
4695
+ /* @__PURE__ */ jsx48(
3486
4696
  "button",
3487
4697
  {
3488
4698
  type: "button",
@@ -3490,7 +4700,7 @@ function DataTable({
3490
4700
  onClick: () => setPage(pageIndex + 1),
3491
4701
  disabled: pageIndex >= pageCount - 1,
3492
4702
  "aria-label": "Next page",
3493
- children: /* @__PURE__ */ jsx44(ChevronRightIcon, { className: "size-4", "aria-hidden": true })
4703
+ children: /* @__PURE__ */ jsx48(ChevronRightIcon2, { className: "size-4", "aria-hidden": true })
3494
4704
  }
3495
4705
  )
3496
4706
  ] })
@@ -3502,8 +4712,8 @@ function DataTable({
3502
4712
  }
3503
4713
 
3504
4714
  // src/app/data/ChartPanel.tsx
3505
- import { useId as useId8 } from "react";
3506
- import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
4715
+ import { useId as useId9 } from "react";
4716
+ import { jsx as jsx49, jsxs as jsxs38 } from "react/jsx-runtime";
3507
4717
  var ChartPanel = ({
3508
4718
  title,
3509
4719
  description,
@@ -3518,17 +4728,17 @@ var ChartPanel = ({
3518
4728
  const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
3519
4729
  const metricChartPlotRegionClass = useAppDensityClass("metricChartPlotRegion");
3520
4730
  const chartPanelBodyClass = useAppDensityClass("chartPanelBody");
3521
- const titleId = useId8();
4731
+ const titleId = useId9();
3522
4732
  const resolvedTitle = title ?? artifact?.title;
3523
4733
  const hasHeader = Boolean(resolvedTitle || description || actions);
3524
- const body = loading ? /* @__PURE__ */ jsx45(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ jsx45(ChartArtifactView, { artifact, embedded: true, height }) : null);
3525
- return /* @__PURE__ */ jsxs35(
4734
+ const body = loading ? /* @__PURE__ */ jsx49(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ jsx49(ChartArtifactView, { artifact, embedded: true, height }) : null);
4735
+ return /* @__PURE__ */ jsxs38(
3526
4736
  "section",
3527
4737
  {
3528
4738
  className: cn(metricCardShellClass, "aui-app-chart-panel", className),
3529
4739
  "aria-labelledby": resolvedTitle ? titleId : void 0,
3530
4740
  children: [
3531
- /* @__PURE__ */ jsx45(
4741
+ /* @__PURE__ */ jsx49(
3532
4742
  MetricCardHeader,
3533
4743
  {
3534
4744
  title: resolvedTitle,
@@ -3537,14 +4747,14 @@ var ChartPanel = ({
3537
4747
  actions
3538
4748
  }
3539
4749
  ),
3540
- /* @__PURE__ */ jsx45(
4750
+ /* @__PURE__ */ jsx49(
3541
4751
  "div",
3542
4752
  {
3543
4753
  className: cn(
3544
4754
  "relative min-h-0 w-full",
3545
4755
  hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
3546
4756
  ),
3547
- children: body ?? /* @__PURE__ */ jsx45(
4757
+ children: body ?? /* @__PURE__ */ jsx49(
3548
4758
  "div",
3549
4759
  {
3550
4760
  className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
@@ -3561,10 +4771,11 @@ var ChartPanel = ({
3561
4771
  };
3562
4772
 
3563
4773
  // src/app/data/MetricRow.tsx
3564
- import { useId as useId9, useState as useState5 } from "react";
3565
- import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
4774
+ import { useId as useId10, useState as useState7 } from "react";
4775
+ import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
3566
4776
  var MetricRow = ({
3567
4777
  title,
4778
+ titleTag,
3568
4779
  description,
3569
4780
  actions,
3570
4781
  metrics,
@@ -3576,9 +4787,9 @@ var MetricRow = ({
3576
4787
  className
3577
4788
  }) => {
3578
4789
  const metricTileClass = useAppDensityClass("metricTile");
3579
- const titleId = useId9();
4790
+ const titleId = useId10();
3580
4791
  const selectable = onMetricChange != null || activeMetricId != null;
3581
- const [internalId, setInternalId] = useState5(
4792
+ const [internalId, setInternalId] = useState7(
3582
4793
  defaultActiveMetricId ?? metrics[0]?.id
3583
4794
  );
3584
4795
  const activeId = activeMetricId ?? internalId;
@@ -3586,22 +4797,24 @@ var MetricRow = ({
3586
4797
  if (activeMetricId == null) setInternalId(id);
3587
4798
  onMetricChange?.(id);
3588
4799
  };
3589
- return /* @__PURE__ */ jsxs36(
4800
+ const hasHeader = Boolean(title || titleTag || description || actions);
4801
+ return /* @__PURE__ */ jsxs39(
3590
4802
  "section",
3591
4803
  {
3592
4804
  className: cn(metricCardShellClass, className),
3593
4805
  "aria-labelledby": title ? titleId : void 0,
3594
4806
  children: [
3595
- /* @__PURE__ */ jsx46(
4807
+ /* @__PURE__ */ jsx50(
3596
4808
  MetricCardHeader,
3597
4809
  {
3598
4810
  title,
4811
+ titleTag,
3599
4812
  titleId,
3600
4813
  description,
3601
4814
  actions
3602
4815
  }
3603
4816
  ),
3604
- /* @__PURE__ */ jsx46(
4817
+ /* @__PURE__ */ jsx50(
3605
4818
  "div",
3606
4819
  {
3607
4820
  role: selectable ? "group" : void 0,
@@ -3610,20 +4823,20 @@ var MetricRow = ({
3610
4823
  className: cn(
3611
4824
  metricTilesRowClass,
3612
4825
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
3613
- (title || description || actions) && "mt-3"
4826
+ hasHeader && "mt-3.5 border-t border-border/40"
3614
4827
  ),
3615
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs36(
4828
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs39(
3616
4829
  "div",
3617
4830
  {
3618
4831
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
3619
4832
  "aria-hidden": true,
3620
4833
  children: [
3621
- /* @__PURE__ */ jsx46(Skeleton, { className: "h-3 w-20" }),
3622
- /* @__PURE__ */ jsx46(Skeleton, { className: "h-7 w-24" })
4834
+ /* @__PURE__ */ jsx50(Skeleton, { className: "h-3 w-20" }),
4835
+ /* @__PURE__ */ jsx50(Skeleton, { className: "h-7 w-24" })
3623
4836
  ]
3624
4837
  },
3625
4838
  `skeleton-${index}`
3626
- )) : metrics.map((m, index) => /* @__PURE__ */ jsx46(
4839
+ )) : metrics.map((m, index) => /* @__PURE__ */ jsx50(
3627
4840
  MetricTile,
3628
4841
  {
3629
4842
  label: m.label,
@@ -3631,6 +4844,11 @@ var MetricRow = ({
3631
4844
  unit: m.unit,
3632
4845
  trend: m.trend,
3633
4846
  trendTone: m.trendTone,
4847
+ trendVariant: m.trendVariant,
4848
+ activeTone: m.activeTone,
4849
+ sparklineData: m.sparklineData,
4850
+ sparklineConfig: m.sparklineConfig,
4851
+ sparkline: m.sparkline,
3634
4852
  active: selectable && m.id === activeId,
3635
4853
  showDivider: index < metrics.length - 1,
3636
4854
  onSelect: selectable ? () => select(m.id) : void 0
@@ -3645,10 +4863,11 @@ var MetricRow = ({
3645
4863
  };
3646
4864
 
3647
4865
  // src/app/data/MetricChartCard.tsx
3648
- import { useId as useId10, useState as useState6 } from "react";
3649
- import { jsx as jsx47, jsxs as jsxs37 } from "react/jsx-runtime";
4866
+ import { useId as useId11, useState as useState8 } from "react";
4867
+ import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
3650
4868
  var MetricChartCard = ({
3651
4869
  title,
4870
+ titleTag,
3652
4871
  description,
3653
4872
  actions,
3654
4873
  metrics,
@@ -3669,8 +4888,8 @@ var MetricChartCard = ({
3669
4888
  const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
3670
4889
  const metricChartRegionClass = useAppDensityClass("metricChartRegion");
3671
4890
  const metricTileClass = useAppDensityClass("metricTile");
3672
- const titleId = useId10();
3673
- const [internalId, setInternalId] = useState6(
4891
+ const titleId = useId11();
4892
+ const [internalId, setInternalId] = useState8(
3674
4893
  defaultActiveMetricId ?? metrics[0]?.id
3675
4894
  );
3676
4895
  const activeId = activeMetricId ?? internalId;
@@ -3679,24 +4898,25 @@ var MetricChartCard = ({
3679
4898
  if (activeMetricId == null) setInternalId(id);
3680
4899
  onMetricChange?.(id);
3681
4900
  };
3682
- const hasHeader = Boolean(title || description || actions);
4901
+ const hasHeader = Boolean(title || titleTag || description || actions);
3683
4902
  const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
3684
- return /* @__PURE__ */ jsxs37(
4903
+ return /* @__PURE__ */ jsxs40(
3685
4904
  "section",
3686
4905
  {
3687
4906
  className: cn(metricCardShellClass, className),
3688
4907
  "aria-labelledby": title ? titleId : void 0,
3689
4908
  children: [
3690
- /* @__PURE__ */ jsx47(
4909
+ /* @__PURE__ */ jsx51(
3691
4910
  MetricCardHeader,
3692
4911
  {
3693
4912
  title,
3694
- titleId,
4913
+ titleTag,
3695
4914
  description,
3696
- actions
4915
+ actions,
4916
+ titleId
3697
4917
  }
3698
4918
  ),
3699
- /* @__PURE__ */ jsx47(
4919
+ /* @__PURE__ */ jsx51(
3700
4920
  "div",
3701
4921
  {
3702
4922
  role: "group",
@@ -3705,20 +4925,20 @@ var MetricChartCard = ({
3705
4925
  className: cn(
3706
4926
  metricTilesRowClass,
3707
4927
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
3708
- hasHeader && "mt-3"
4928
+ hasHeader && "mt-3.5 border-t border-border/40"
3709
4929
  ),
3710
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs37(
4930
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs40(
3711
4931
  "div",
3712
4932
  {
3713
4933
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
3714
4934
  "aria-hidden": true,
3715
4935
  children: [
3716
- /* @__PURE__ */ jsx47(Skeleton, { className: "h-3 w-20" }),
3717
- /* @__PURE__ */ jsx47(Skeleton, { className: "h-7 w-24" })
4936
+ /* @__PURE__ */ jsx51(Skeleton, { className: "h-3 w-20" }),
4937
+ /* @__PURE__ */ jsx51(Skeleton, { className: "h-7 w-24" })
3718
4938
  ]
3719
4939
  },
3720
4940
  `skeleton-${index}`
3721
- )) : metrics.map((m, index) => /* @__PURE__ */ jsx47(
4941
+ )) : metrics.map((m, index) => /* @__PURE__ */ jsx51(
3722
4942
  MetricTile,
3723
4943
  {
3724
4944
  label: m.label,
@@ -3726,6 +4946,11 @@ var MetricChartCard = ({
3726
4946
  unit: m.unit,
3727
4947
  trend: m.trend,
3728
4948
  trendTone: m.trendTone,
4949
+ trendVariant: m.trendVariant,
4950
+ activeTone: m.activeTone,
4951
+ sparklineData: m.sparklineData,
4952
+ sparklineConfig: m.sparklineConfig,
4953
+ sparkline: m.sparkline,
3729
4954
  active: m.id === active?.id,
3730
4955
  showDivider: index < metrics.length - 1,
3731
4956
  onSelect: () => select(m.id)
@@ -3734,14 +4959,14 @@ var MetricChartCard = ({
3734
4959
  ))
3735
4960
  }
3736
4961
  ),
3737
- /* @__PURE__ */ jsx47("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx47(
4962
+ /* @__PURE__ */ jsx51("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx51(
3738
4963
  Skeleton,
3739
4964
  {
3740
4965
  className: "w-full rounded-lg",
3741
4966
  style: { height },
3742
4967
  "aria-hidden": true
3743
4968
  }
3744
- ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx47(
4969
+ ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx51(
3745
4970
  LineAreaChart,
3746
4971
  {
3747
4972
  data: active.data,
@@ -3761,7 +4986,7 @@ var MetricChartCard = ({
3761
4986
  ariaLabel: chartAriaLabel
3762
4987
  },
3763
4988
  active.id
3764
- ) : /* @__PURE__ */ jsx47(
4989
+ ) : /* @__PURE__ */ jsx51(
3765
4990
  "div",
3766
4991
  {
3767
4992
  className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
@@ -3776,13 +5001,13 @@ var MetricChartCard = ({
3776
5001
  };
3777
5002
 
3778
5003
  // src/hooks/use-live-query.ts
3779
- import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState7 } from "react";
5004
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef2, useState as useState9 } from "react";
3780
5005
  function useInterval(callback, delayMs) {
3781
- const saved = useRef(callback);
3782
- useEffect3(() => {
5006
+ const saved = useRef2(callback);
5007
+ useEffect5(() => {
3783
5008
  saved.current = callback;
3784
5009
  }, [callback]);
3785
- useEffect3(() => {
5010
+ useEffect5(() => {
3786
5011
  if (delayMs === null) return;
3787
5012
  const id = setInterval(() => saved.current(), delayMs);
3788
5013
  return () => clearInterval(id);
@@ -3795,19 +5020,19 @@ function useLiveQuery(fetcher, options = {}) {
3795
5020
  immediate = true,
3796
5021
  refetchOnFocus = true
3797
5022
  } = options;
3798
- const [data, setData] = useState7(void 0);
3799
- const [error, setError] = useState7(void 0);
3800
- const [loading, setLoading] = useState7(enabled);
3801
- const [refreshing, setRefreshing] = useState7(false);
3802
- const [lastUpdated, setLastUpdated] = useState7(null);
3803
- const fetcherRef = useRef(fetcher);
3804
- useEffect3(() => {
5023
+ const [data, setData] = useState9(void 0);
5024
+ const [error, setError] = useState9(void 0);
5025
+ const [loading, setLoading] = useState9(enabled);
5026
+ const [refreshing, setRefreshing] = useState9(false);
5027
+ const [lastUpdated, setLastUpdated] = useState9(null);
5028
+ const fetcherRef = useRef2(fetcher);
5029
+ useEffect5(() => {
3805
5030
  fetcherRef.current = fetcher;
3806
5031
  }, [fetcher]);
3807
- const mounted = useRef(true);
3808
- const requestId = useRef(0);
3809
- const hasData = useRef(false);
3810
- useEffect3(() => {
5032
+ const mounted = useRef2(true);
5033
+ const requestId = useRef2(0);
5034
+ const hasData = useRef2(false);
5035
+ useEffect5(() => {
3811
5036
  mounted.current = true;
3812
5037
  return () => {
3813
5038
  mounted.current = false;
@@ -3839,11 +5064,11 @@ function useLiveQuery(fetcher, options = {}) {
3839
5064
  if (!enabled) return;
3840
5065
  run();
3841
5066
  }, [enabled, run]);
3842
- useEffect3(() => {
5067
+ useEffect5(() => {
3843
5068
  if (!enabled) return;
3844
5069
  if (immediate) run();
3845
5070
  }, [enabled, immediate, run]);
3846
- useEffect3(() => {
5071
+ useEffect5(() => {
3847
5072
  if (!enabled || intervalMs === null) return;
3848
5073
  const tick = () => {
3849
5074
  if (refetchOnFocus && typeof document !== "undefined" && document.visibilityState === "hidden") {
@@ -3854,7 +5079,7 @@ function useLiveQuery(fetcher, options = {}) {
3854
5079
  const handle = setInterval(tick, intervalMs);
3855
5080
  return () => clearInterval(handle);
3856
5081
  }, [enabled, intervalMs, refetchOnFocus, run]);
3857
- useEffect3(() => {
5082
+ useEffect5(() => {
3858
5083
  if (!enabled || !refetchOnFocus || typeof document === "undefined") return;
3859
5084
  const onVisible = () => {
3860
5085
  if (document.visibilityState === "visible") run();
@@ -3865,69 +5090,6 @@ function useLiveQuery(fetcher, options = {}) {
3865
5090
  return { data, error, loading, refreshing, lastUpdated, refetch };
3866
5091
  }
3867
5092
 
3868
- // src/charts/sparkline.tsx
3869
- import { useId as useId11 } from "react";
3870
- import { Fragment as Fragment6, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
3871
- var Sparkline = ({
3872
- data,
3873
- dataKey = "value",
3874
- color = "var(--primary, #6366f1)",
3875
- area = true,
3876
- width = 96,
3877
- height = 28,
3878
- strokeWidth = 1.5,
3879
- className,
3880
- ariaLabel = "Trend"
3881
- }) => {
3882
- const uid = useId11();
3883
- const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
3884
- if (values.length === 0) {
3885
- return /* @__PURE__ */ jsx48("span", { className: cn("inline-block", className), style: { width, height } });
3886
- }
3887
- const pad = strokeWidth + 1;
3888
- const min = Math.min(...values);
3889
- const max = Math.max(...values);
3890
- const range = max - min || 1;
3891
- const innerW = width - pad * 2;
3892
- const innerH = height - pad * 2;
3893
- const points = values.map((v, i) => ({
3894
- x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
3895
- y: pad + innerH - (v - min) / range * innerH
3896
- }));
3897
- return /* @__PURE__ */ jsxs38(
3898
- "svg",
3899
- {
3900
- width,
3901
- height,
3902
- viewBox: `0 0 ${width} ${height}`,
3903
- className: cn("block", className),
3904
- role: "img",
3905
- "aria-label": ariaLabel,
3906
- preserveAspectRatio: "none",
3907
- children: [
3908
- area && /* @__PURE__ */ jsxs38(Fragment6, { children: [
3909
- /* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs38("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
3910
- /* @__PURE__ */ jsx48("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
3911
- /* @__PURE__ */ jsx48("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
3912
- ] }) }),
3913
- /* @__PURE__ */ jsx48("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
3914
- ] }),
3915
- /* @__PURE__ */ jsx48(
3916
- "path",
3917
- {
3918
- d: monotoneLinePath(points),
3919
- fill: "none",
3920
- stroke: color,
3921
- strokeWidth,
3922
- strokeLinecap: "round",
3923
- strokeLinejoin: "round"
3924
- }
3925
- )
3926
- ]
3927
- }
3928
- );
3929
- };
3930
-
3931
5093
  export {
3932
5094
  SEMANTIC_COLOR_TOKENS,
3933
5095
  RESERVED_GRADIENT_TOKENS,
@@ -3964,16 +5126,17 @@ export {
3964
5126
  AppDensityProvider,
3965
5127
  useAppDensity,
3966
5128
  useAppDensityClass,
5129
+ Sparkline,
3967
5130
  MetricTile,
3968
5131
  useAppShellChat,
3969
5132
  useAppShellNav,
3970
5133
  AppShell,
3971
- AppShellTopbar,
3972
5134
  AppShellChatTrigger,
3973
5135
  AppShellSidebarTrigger,
3974
5136
  PageHeader,
3975
5137
  Page,
3976
5138
  Section,
5139
+ Stack,
3977
5140
  AppCopilotProvider,
3978
5141
  useAppCopilotContext,
3979
5142
  AppChatPanel,
@@ -3987,6 +5150,8 @@ export {
3987
5150
  DescriptionList,
3988
5151
  ExpandableSection,
3989
5152
  ResourceCard,
5153
+ AlertCard,
5154
+ CatalogCard,
3990
5155
  SettingsSectionHeader,
3991
5156
  SettingsSection,
3992
5157
  FieldRow,
@@ -4011,11 +5176,11 @@ export {
4011
5176
  FormSection,
4012
5177
  FilterBar,
4013
5178
  FilterField,
5179
+ FilterDropdown,
4014
5180
  DataTable,
4015
5181
  ChartPanel,
4016
5182
  MetricRow,
4017
5183
  MetricChartCard,
4018
5184
  useInterval,
4019
- useLiveQuery,
4020
- Sparkline
5185
+ useLiveQuery
4021
5186
  };