@timbal-ai/timbal-react 1.3.0 → 1.5.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 (53) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +28 -4
  3. package/dist/app.cjs +3610 -1489
  4. package/dist/app.d.cts +75 -30
  5. package/dist/app.d.ts +75 -30
  6. package/dist/app.esm.js +29 -7
  7. package/dist/{chart-artifact-Q5QgMtbj.d.ts → chart-artifact-2OTDTRwM.d.ts} +212 -41
  8. package/dist/{chart-artifact-WDEW9dHT.d.cts → chart-artifact-CS3qyGIY.d.cts} +212 -41
  9. package/dist/chat.cjs +264 -107
  10. package/dist/chat.d.cts +2 -2
  11. package/dist/chat.d.ts +2 -2
  12. package/dist/chat.esm.js +4 -3
  13. package/dist/chunk-5ECRZ5O7.esm.js +899 -0
  14. package/dist/{chunk-QU7ET55D.esm.js → chunk-AZL2WANO.esm.js} +320 -177
  15. package/dist/{chunk-OH23AX2V.esm.js → chunk-B4XAC4G7.esm.js} +430 -780
  16. package/dist/chunk-EDEKQYSU.esm.js +10 -0
  17. package/dist/{chunk-GQBYZRD7.esm.js → chunk-IGHBLJV3.esm.js} +38 -27
  18. package/dist/{chunk-OFWC4MIY.esm.js → chunk-JYDJRGDE.esm.js} +5 -3
  19. package/dist/{chunk-YCXN67SD.esm.js → chunk-SZDYIRMB.esm.js} +1639 -490
  20. package/dist/chunk-TZI3ID3C.esm.js +232 -0
  21. package/dist/{chunk-THBA27QY.esm.js → chunk-WMKPT5BV.esm.js} +242 -123
  22. package/dist/{chunk-VXMM2HX7.esm.js → chunk-ZNYAETFD.esm.js} +1 -1
  23. package/dist/{circular-progress-Ci8L-Hfa.d.cts → circular-progress-CDsJwIPF.d.cts} +19 -77
  24. package/dist/{circular-progress-Ci8L-Hfa.d.ts → circular-progress-CDsJwIPF.d.ts} +19 -77
  25. package/dist/index.cjs +5564 -3612
  26. package/dist/index.d.cts +7 -6
  27. package/dist/index.d.ts +7 -6
  28. package/dist/index.esm.js +45 -33
  29. package/dist/kanban-U5xNe9py.d.cts +212 -0
  30. package/dist/kanban-U5xNe9py.d.ts +212 -0
  31. package/dist/{layout-BTJyU8wd.d.ts → layout-B8r6Jbat.d.ts} +1 -1
  32. package/dist/{layout-C2G-FcER.d.cts → layout-Cu7Ijn04.d.cts} +1 -1
  33. package/dist/site.cjs +358 -0
  34. package/dist/site.d.cts +184 -0
  35. package/dist/site.d.ts +184 -0
  36. package/dist/site.esm.js +322 -0
  37. package/dist/studio.cjs +702 -343
  38. package/dist/studio.d.cts +1 -1
  39. package/dist/studio.d.ts +1 -1
  40. package/dist/studio.esm.js +7 -5
  41. package/dist/styles.css +56 -0
  42. package/dist/{timbal-v2-button-CNfdwGq4.d.cts → timbal-v2-button-B7vPs7gg.d.cts} +2 -2
  43. package/dist/{timbal-v2-button-CNfdwGq4.d.ts → timbal-v2-button-B7vPs7gg.d.ts} +2 -2
  44. package/dist/ui.cjs +1504 -659
  45. package/dist/ui.d.cts +11 -4
  46. package/dist/ui.d.ts +11 -4
  47. package/dist/ui.esm.js +35 -26
  48. package/dist/{welcome-DXqsGTwH.d.ts → welcome-DduQAC4K.d.ts} +4 -0
  49. package/dist/{welcome-BFGRoNfK.d.cts → welcome-NXZlcihe.d.cts} +4 -0
  50. package/package.json +9 -1
  51. package/dist/button-BoyX5pM_.d.cts +0 -18
  52. package/dist/button-BoyX5pM_.d.ts +0 -18
  53. package/dist/chunk-UCGVL7ZY.esm.js +0 -52
@@ -15,27 +15,36 @@ import {
15
15
  studioIntegrationCardClass,
16
16
  studioTopbarPillHeightClass,
17
17
  toNum
18
- } from "./chunk-GQBYZRD7.esm.js";
18
+ } from "./chunk-IGHBLJV3.esm.js";
19
19
  import {
20
20
  Checkbox,
21
+ CopyButton,
22
+ Popover,
23
+ PopoverContent,
24
+ PopoverTrigger,
21
25
  Skeleton
22
- } from "./chunk-UCGVL7ZY.esm.js";
26
+ } from "./chunk-5ECRZ5O7.esm.js";
23
27
  import {
24
28
  PillSegmentedTabs
25
- } from "./chunk-VXMM2HX7.esm.js";
29
+ } from "./chunk-ZNYAETFD.esm.js";
26
30
  import {
31
+ Avatar,
32
+ AvatarFallback,
27
33
  Button,
28
34
  Dialog,
29
35
  DialogContent,
30
36
  DialogTitle,
37
+ TIMBAL_V2_ELEVATED_GRADIENT,
31
38
  TIMBAL_V2_ELEVATED_SURFACE,
32
39
  TIMBAL_V2_LOGO_TILE,
33
40
  TIMBAL_V2_SWITCH_THUMB,
34
41
  TIMBAL_V2_SWITCH_TRACK_OFF,
35
42
  TimbalV2Button,
36
- cn,
37
43
  controlClass
38
- } from "./chunk-QU7ET55D.esm.js";
44
+ } from "./chunk-AZL2WANO.esm.js";
45
+ import {
46
+ cn
47
+ } from "./chunk-EDEKQYSU.esm.js";
39
48
 
40
49
  // src/design/ui-vocabulary.ts
41
50
  var SEMANTIC_COLOR_TOKENS = [
@@ -191,6 +200,13 @@ var HOUSE_RULES = [
191
200
  rule: "Don't nest a bordered card inside another bordered card. Group with spacing or a Section instead.",
192
201
  why: "Card-in-card doubles borders and shadows for no information gain."
193
202
  },
203
+ {
204
+ id: "no-table-in-card",
205
+ rule: "Never wrap a DataTable or table inside a Card, SurfaceCard, or ArtifactCard.",
206
+ 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.",
207
+ slop: "<Card><DataTable columns={columns} rows={rows} getRowKey={getRowKey} /></Card>",
208
+ good: "<DataTable columns={columns} rows={rows} getRowKey={getRowKey} />"
209
+ },
194
210
  {
195
211
  id: "no-row-dividers",
196
212
  rule: "Don't put a divider between every list row. Use spacing or zebra striping.",
@@ -212,6 +228,27 @@ var HOUSE_RULES = [
212
228
  why: "Hand-rolled controls drift from the shared control-surface skin and look foreign next to kit controls.",
213
229
  slop: `<button className="rounded-lg border border-input bg-transparent px-3 h-9">`,
214
230
  good: `<SelectTrigger><SelectValue /></SelectTrigger>`
231
+ },
232
+ {
233
+ id: "no-title-repetition",
234
+ rule: "Never repeat the page title/description inside a Section, Card, or Table header.",
235
+ why: "If the Page already has a title, repeating it in the first child section or table is redundant and wastes vertical space.",
236
+ slop: `<Page title="Orders"><Section title="Orders"><DataTable ... /></Section></Page>`,
237
+ good: `<Page title="Orders"><DataTable ... /></Page>`
238
+ },
239
+ {
240
+ id: "no-chat-wrapping",
241
+ rule: "Never wrap TimbalChat or AppChatPanel in a Card, Section, or custom bordered/padded container, and never add custom heading/status blocks above it.",
242
+ 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.",
243
+ slop: `<Page title="Assistant"><Card><div className="flex justify-between"><h3>TIBA Concierge</h3><span>Online</span></div><TimbalChat /></Card></Page>`,
244
+ good: `<Page fill><TimbalChat workforceId="..." welcome={{ heading: "Hola, soy el Concierge de TIBA", subheading: "Preg\xFAntame sobre..." }} /></Page>`
245
+ },
246
+ {
247
+ id: "no-colored-hover",
248
+ rule: "Interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
249
+ 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.",
250
+ slop: `<Card className="hover:bg-emerald-500/10 hover:border-emerald-500/30">`,
251
+ good: `<AlertCard onClick={handleClick}>`
215
252
  }
216
253
  ];
217
254
 
@@ -239,6 +276,45 @@ You are **not** required to copy any example layout, page title, section order,
239
276
 
240
277
  When in doubt: compose from the **component menu** + **guidelines**, then adapt creatively to the request.
241
278
 
279
+ ### Layout archetypes \u2014 pick the shape that fits (don't default to one)
280
+
281
+ The most common failure is shipping the **same** layout every time: sidebar + topbar + \`Page\` + one \`MetricRow\` + one full-width \`DataTable\`. That is *one* archetype, not *the* layout. Choose deliberately \u2014 different domains want different shapes, and varying the shell/page composition is encouraged.
282
+
283
+ | Archetype | When | Compose |
284
+ |-----------|------|---------|
285
+ | **Sidebar dashboard** | Multi-section product (CRM, billing, ops) with nav | \`StudioSidebar\` in \`AppShell.sidebar\` + \`Page\` \u2192 \`Section\` |
286
+ | **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 |
287
+ | **Bento overview** | Home / at-a-glance dashboards | \`Page\` + an **asymmetric grid** of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` spanning different widths (not a uniform row + table) |
288
+ | **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\` |
289
+ | **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\`) |
290
+ | **Copilot overlay** | A data app that also wants an assistant | any of the above + \`AppShell chat={<AppChatPanel />}\` (floating, never a second column) |
291
+ | **Section-switcher** | One page, several views | \`SubNav\` / \`PillSegmentedTabs\` (\`trackVariant="flush"\`) switching panels with state/router |
292
+
293
+ 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.
294
+
295
+ ### Full-height pages (chat, canvas, split views)
296
+
297
+ The content region is a **padded scroll area** by default \u2014 great for stacked \`Page\` \u2192 \`Section\` content, wrong for a surface that must fill the viewport. For full-bleed pages:
298
+
299
+ - Pass **\`contentFill\`** to \`AppShell\` \u2192 the content region becomes a bounded, non-scrolling flex column (clipped, no bottom padding).
300
+ - Pass **\`fill\`** to \`Page\` \u2192 the page becomes a \`min-h-0 flex-1\` flex column.
301
+ - 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\`.
302
+
303
+ \`\`\`tsx
304
+ <AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
305
+ <Page fill> {/* headerless: omit title */}
306
+ <TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
307
+ </Page>
308
+ </AppShell>
309
+ \`\`\`
310
+
311
+ **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.
312
+
313
+ **Full-page Assistant Guidelines (Hardened Layout):**
314
+ When creating a full-page assistant/chat page, let the chat component own the layout and welcome state completely.
315
+ - **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.
316
+ - **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..." }} />\`.
317
+
242
318
  ### Module layout (source folders)
243
319
 
244
320
  Presentational groups \u2014 import from the package root, not from these paths:
@@ -248,11 +324,11 @@ Presentational groups \u2014 import from the package root, not from these paths:
248
324
  | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
249
325
  | \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
250
326
  | \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
251
- | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
252
- | \`layout/\` | \`AppShell\`, \`Page\`, \`Section\` |
327
+ | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
328
+ | \`layout/\` | \`AppShell\`, \`Page\` (auto-stacks children; \`width\` ladder), \`Section\`, \`Stack\` |
253
329
  | \`charts\` (re-exported) | \`LineAreaChart\`, \`PieChart\`, \`RadialChart\`, \`RadarChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
254
330
 
255
- Also re-exported: \`Button\`, \`TimbalChat\`, \`ChartArtifactView\`, \`APP_KIT_AGENT_INSTRUCTIONS\`.
331
+ 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\`.
256
332
 
257
333
  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.
258
334
 
@@ -264,9 +340,12 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
264
340
  | **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. |
265
341
  | **Context** | Do not show raw JSON context in the panel header; keep context in \`AppCopilotProvider\` only. |
266
342
  | **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\`. |
267
- | **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. |
343
+ | **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. |
344
+ | **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-*\`. |
345
+ | **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\`. |
268
346
  | **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. |
269
347
  | **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. |
348
+ | **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. |
270
349
  | **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
271
350
  | **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
272
351
  | **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
@@ -297,17 +376,16 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
297
376
 
298
377
  | Component | Use for |
299
378
  |-----------|---------|
300
- | \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen\`. |
301
- | \`AppShellTopbar\` | Full-width top bar: \`start\`, \`actions\` slots. |
379
+ | \`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). |
302
380
  | \`AppCopilotProvider\` | React context for copilot-aware tools (page, filters, selection, etc.). |
303
381
  | \`AppChatPanel\` | Floating thread: \`workforceId\`, \`welcome\`, \`debug\`. |
304
382
  | \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
305
- | \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, \`density\` (\`"default"\` | \`"compact"\`), children. |
383
+ | \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, \`density\` (\`"default"\` | \`"compact"\`), children. **\`title\` is optional** \u2014 omit it for a headerless page (no \`<h1>\`). **\`fill\`** makes it a \`min-h-0 flex-1\` column for full-height content (pair with \`AppShell contentFill\`). |
306
384
  | \`Section\` | Titled block inside a page. Optional \`density\` overrides inherited page density. |
307
385
  | \`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. |
308
386
  | **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\`. |
309
387
  | \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
310
- | \`Button\` | Actions \u2014 \`variant="secondary"\` for catalog/secondary CTAs; \`variant="default"\` for primary. |
388
+ | \`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. |
311
389
  | \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
312
390
  | \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
313
391
  | \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
@@ -316,7 +394,7 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
316
394
  | \`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\`). |
317
395
  | \`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\`. |
318
396
  | \`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. |
319
- | \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. |
397
+ | \`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\`. |
320
398
  | \`FormSection\` | Grouped form block. |
321
399
  | \`AppConfirmDialog\` | Confirm/cancel modal: \`open\`, \`onOpenChange\`, \`title\`, \`description\`, \`onConfirm\`. |
322
400
  | \`SurfaceCard\`, \`EmptyState\` | Generic surfaces when needed. |
@@ -362,9 +440,13 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
362
440
  | Component | Use for |
363
441
  |-----------|---------|
364
442
  | \`InfoCard\` | Soft callout: \`icon\`, \`title\`, body, \`action\`, \`tone\` (\`info\`/\`success\`/\`warn\`/\`danger\`). |
443
+ | \`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. |
444
+ | \`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). |
365
445
  | \`DescriptionList\` | Read-only key/value metadata: \`items: [{ label, value }]\`, optional \`stacked\`. |
366
446
  | \`ExpandableSection\` | Collapsible block: \`title\`, \`icon\`, \`count\`, animated body (\`aria-expanded\` + \`aria-controls\`). |
367
447
  | \`StatusDot\` | Status indicator dot: \`tone\`, \`label\`, \`pulse\`. |
448
+ | \`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. |
449
+ | \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
368
450
 
369
451
  Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
370
452
 
@@ -395,8 +477,11 @@ Ready-made **section patterns** assembled from the components above. Each is a c
395
477
  - **Empty states** \u2014 no-data / no-results / first-run. Compose \`EmptyState\` + \`Card\` + \`Button\`.
396
478
  - **Sign-in card** \u2014 centered auth entry. Compose \`Card\` + \`Input\` + \`Label\` + \`Button\`.
397
479
 
398
- **Shells & theming**
480
+ **Shells & layouts**
399
481
  - **Minimal shell** \u2014 \`AppShell\` + \`Page\` (no sidebar/chat).
482
+ - **Bento dashboard** \u2014 \`Page\` + an asymmetric grid of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` (varied spans) for overview/home screens.
483
+ - **Split view** \u2014 master\u2013detail: \`AppShell contentFill\` + \`Page fill\` + a two-pane flex row (list + detail), each pane \`min-h-0 overflow-y-auto\`.
484
+ - **Full-page chat** \u2014 \`AppShell contentFill\` + headerless \`Page fill\` + \`TimbalChat className="min-h-0 flex-1"\` (composer pinned; no \`h-[calc(...)]\`).
400
485
  - **Copilot overlay** \u2014 \`AppShell\` + floating \`AppChatPanel\`.
401
486
  - **Theme presets** \u2014 apply a brand preset programmatically (\`applyThemePreset\` / \`applyTimbalTheme\`); never hand-author OKLCH and don't expose a theme picker to end users.
402
487
 
@@ -429,6 +514,8 @@ import {
429
514
  DataTable,
430
515
  FilterBar,
431
516
  FilterField,
517
+ AlertCard,
518
+ CatalogCard,
432
519
  } from "@timbal-ai/timbal-react/app";
433
520
  \`\`\`
434
521
 
@@ -439,6 +526,17 @@ import {
439
526
  | \`examples/app-kit/recipes/*\` | **Recipes** \u2014 one pattern each (~20\u201380 lines). Use for capability, not layout. |
440
527
  | \`examples/app-kit/reference/operations-dashboard.tsx\` | **Reference only** \u2014 full wired app; do not treat as the default generated layout. |
441
528
 
529
+ ### API gotchas \u2014 props that do NOT exist (don't guess, don't retry variations)
530
+
531
+ The compiler rejects these every time; write against the documented shapes above instead:
532
+
533
+ - \`FieldInput\` / \`FieldTextarea\` / \`FieldSelect\` / \`FieldSwitch\` **require \`label\`** \u2014 TS2741 if omitted. Label-less control \u2192 \`FilterField\` or \`SearchInput\`.
534
+ - Full-height layout: \`fill\` lives on \`Page\` (paired with \`AppShell contentFill\`) \u2014 there is no \`fill\` on \`Section\` or \`AppShell\`.
535
+ - \`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"\`.
536
+ - There is **no \`Tabs\` export** \u2014 section switching uses \`SubNav\` or \`PillSegmentedTabs\`.
537
+ - \`Banner\` and \`Timeline\` exist (see menu) \u2014 import them from \`/app\`, \`/ui\`, or the root.
538
+ - 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.
539
+
442
540
  ### Rules
443
541
 
444
542
  - Prefer stable props documented above; avoid undocumented \`design/*\` class exports (\`connectionRowListClass\` is exported but \`ConnectionRowList\` is preferred).
@@ -469,6 +567,7 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
469
567
  ]);
470
568
  var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
471
569
  var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
570
+ 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/;
472
571
  var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
473
572
  function stripVariants(util) {
474
573
  return util.replace(/^(?:[a-z-]+:)*/, "");
@@ -485,7 +584,14 @@ function lintGeneratedUi(source, options = {}) {
485
584
  let usesLucide = false;
486
585
  let iconUsageCount = 0;
487
586
  let dividerRunCount = 0;
587
+ let pageTitle = null;
588
+ const pageTitleMatch = source.match(/<Page\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
589
+ if (pageTitleMatch) {
590
+ pageTitle = (pageTitleMatch[1] || pageTitleMatch[2]).trim().toLowerCase();
591
+ }
592
+ const hasChat = /\b(?:TimbalChat|AppChatPanel|Thread)\b/.test(source);
488
593
  const lucideNames = /* @__PURE__ */ new Set();
594
+ const openCards = [];
489
595
  for (let i = 0; i < lines.length; i++) {
490
596
  const line = lines[i];
491
597
  const lineNo = i + 1;
@@ -501,6 +607,33 @@ function lintGeneratedUi(source, options = {}) {
501
607
  continue;
502
608
  }
503
609
  if (isCommentOrImport(line)) continue;
610
+ const cardMatch = line.match(/<(Card|SurfaceCard|ArtifactCard)\b/);
611
+ if (cardMatch) {
612
+ const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
613
+ if (!isSelfClosing) {
614
+ openCards.push({ type: cardMatch[1], line: lineNo });
615
+ }
616
+ }
617
+ const closeMatch = line.match(/<\/(Card|SurfaceCard|ArtifactCard)\b/);
618
+ if (closeMatch && openCards.length > 0) {
619
+ const idx = openCards.map((c) => c.type).lastIndexOf(closeMatch[1]);
620
+ if (idx !== -1) {
621
+ openCards.splice(idx, 1);
622
+ }
623
+ }
624
+ if (openCards.length > 0) {
625
+ const tableMatch = line.match(/<(DataTable|table|Table)\b/);
626
+ if (tableMatch) {
627
+ const parentCard = openCards[openCards.length - 1];
628
+ findings.push({
629
+ rule: "no-table-in-card",
630
+ severity: "error",
631
+ line: lineNo,
632
+ 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.`,
633
+ snippet: line.trim().slice(0, 120)
634
+ });
635
+ }
636
+ }
504
637
  const rawColors = line.match(RAW_COLOR_RE);
505
638
  if (rawColors) {
506
639
  for (const m of rawColors) {
@@ -541,6 +674,15 @@ function lintGeneratedUi(source, options = {}) {
541
674
  snippet: line.trim().slice(0, 120)
542
675
  });
543
676
  }
677
+ if (COLORED_HOVER_RE.test(line)) {
678
+ findings.push({
679
+ rule: "no-colored-hover",
680
+ severity: "warn",
681
+ line: lineNo,
682
+ 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.",
683
+ snippet: line.trim().slice(0, 120)
684
+ });
685
+ }
544
686
  if (BOLD_VALUE_RE.test(line)) {
545
687
  findings.push({
546
688
  rule: "bold-metric",
@@ -587,6 +729,44 @@ function lintGeneratedUi(source, options = {}) {
587
729
  if (hits) iconUsageCount += hits.length;
588
730
  }
589
731
  }
732
+ if (pageTitle) {
733
+ const titleMatch = line.match(/<(Section|ChartPanel|Card|DataTable|SurfaceCard)\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
734
+ if (titleMatch) {
735
+ const element = titleMatch[1];
736
+ const titleVal = (titleMatch[2] || titleMatch[3]).trim().toLowerCase();
737
+ if (titleVal === pageTitle || titleVal.includes(pageTitle) || pageTitle.includes(titleVal)) {
738
+ findings.push({
739
+ rule: "no-title-repetition",
740
+ severity: "warn",
741
+ line: lineNo,
742
+ 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.`,
743
+ snippet: line.trim().slice(0, 120)
744
+ });
745
+ }
746
+ }
747
+ }
748
+ if (hasChat) {
749
+ const wrappingMatch = line.match(/<(Card|Section|SurfaceCard|FormSection|SettingsSection)\b/);
750
+ if (wrappingMatch) {
751
+ findings.push({
752
+ rule: "no-chat-wrapping",
753
+ severity: "error",
754
+ line: lineNo,
755
+ 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.`,
756
+ snippet: line.trim().slice(0, 120)
757
+ });
758
+ }
759
+ const headingMatch = line.match(/<(h[1-6])\b/);
760
+ if (headingMatch) {
761
+ findings.push({
762
+ rule: "no-chat-wrapping",
763
+ severity: "error",
764
+ line: lineNo,
765
+ 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.`,
766
+ snippet: line.trim().slice(0, 120)
767
+ });
768
+ }
769
+ }
590
770
  }
591
771
  if (usesLucide && iconUsageCount > maxIcons) {
592
772
  findings.push({
@@ -1319,7 +1499,20 @@ var TimbalThemeStyle = ({
1319
1499
 
1320
1500
  // src/design/app-classes.ts
1321
1501
  var appPageColumnClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
1322
- var appShellTopbarInsetClass = "w-full px-4 md:px-6";
1502
+ var PAGE_WIDTH_MAXW = {
1503
+ full: "max-w-none",
1504
+ wide: "max-w-[100rem]",
1505
+ default: "max-w-7xl",
1506
+ centered: "max-w-5xl",
1507
+ narrow: "max-w-3xl",
1508
+ prose: "max-w-2xl"
1509
+ };
1510
+ function appPageColumn(width, density = "default") {
1511
+ const lateral = density === "compact" ? "px-3 md:px-4" : "px-4 md:px-6 lg:px-8";
1512
+ const maxW = width ? PAGE_WIDTH_MAXW[width] : density === "compact" ? "max-w-none" : "max-w-[100rem]";
1513
+ return cn("mx-auto w-full", maxW, lateral);
1514
+ }
1515
+ var appShellTopbarInsetClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
1323
1516
  var appShellInsetTopClass = "pt-4 md:pt-6";
1324
1517
  var appShellInsetBottomClass = "pb-8 md:pb-10";
1325
1518
  var appShellTopbarRowClass = cn(
@@ -1386,6 +1579,15 @@ var APP_DENSITY_CLASSES = {
1386
1579
  default: appPageHeaderClass,
1387
1580
  compact: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between pb-2 pt-1"
1388
1581
  },
1582
+ /**
1583
+ * Vertical rhythm between a `Page`'s direct content blocks. Page-level gap so
1584
+ * stacked blocks (FilterBar + DataTable, MetricRow + ChartPanel, bare cards)
1585
+ * never render flush — the #1 source of gap-less generated layouts.
1586
+ */
1587
+ pageStack: {
1588
+ default: "flex flex-col gap-6",
1589
+ compact: "flex flex-col gap-4"
1590
+ },
1389
1591
  section: {
1390
1592
  default: appSectionClass,
1391
1593
  compact: "flex flex-col gap-2 py-2"
@@ -1411,12 +1613,12 @@ var APP_DENSITY_CLASSES = {
1411
1613
  compact: "relative flex min-w-0 flex-1 flex-col gap-1 px-3 py-2 text-left font-normal"
1412
1614
  },
1413
1615
  metricChartRegion: {
1414
- default: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 pt-2",
1415
- compact: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 pt-1"
1616
+ default: "relative min-h-0 w-full overflow-x-hidden pt-2",
1617
+ compact: "relative min-h-0 w-full overflow-x-hidden pt-1"
1416
1618
  },
1417
1619
  metricChartPlotRegion: {
1418
- default: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 px-0 pt-5 pb-3",
1419
- compact: "relative min-h-0 w-full overflow-x-hidden border-t border-border/40 px-0 pt-3 pb-2"
1620
+ default: "relative min-h-0 w-full overflow-x-hidden px-0 pt-5 pb-3",
1621
+ compact: "relative min-h-0 w-full overflow-x-hidden px-0 pt-3 pb-2"
1420
1622
  },
1421
1623
  chartPanelBody: {
1422
1624
  default: "pt-2 pb-3",
@@ -1445,8 +1647,71 @@ function useAppDensityClass(key, override) {
1445
1647
  return appDensityClass(key, override ?? inherited);
1446
1648
  }
1447
1649
 
1650
+ // src/charts/sparkline.tsx
1651
+ import { useId } from "react";
1652
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1653
+ var Sparkline = ({
1654
+ data,
1655
+ dataKey = "value",
1656
+ color = "var(--primary, #6366f1)",
1657
+ area = true,
1658
+ width = 96,
1659
+ height = 28,
1660
+ strokeWidth = 1.5,
1661
+ className,
1662
+ ariaLabel = "Trend"
1663
+ }) => {
1664
+ const uid = useId();
1665
+ const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
1666
+ if (values.length === 0) {
1667
+ return /* @__PURE__ */ jsx3("span", { className: cn("inline-block", className), style: { width, height } });
1668
+ }
1669
+ const pad = strokeWidth + 1;
1670
+ const min = Math.min(...values);
1671
+ const max = Math.max(...values);
1672
+ const range = max - min || 1;
1673
+ const innerW = width - pad * 2;
1674
+ const innerH = height - pad * 2;
1675
+ const points = values.map((v, i) => ({
1676
+ x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
1677
+ y: pad + innerH - (v - min) / range * innerH
1678
+ }));
1679
+ return /* @__PURE__ */ jsxs2(
1680
+ "svg",
1681
+ {
1682
+ width,
1683
+ height,
1684
+ viewBox: `0 0 ${width} ${height}`,
1685
+ className: cn("block", className),
1686
+ role: "img",
1687
+ "aria-label": ariaLabel,
1688
+ preserveAspectRatio: "none",
1689
+ children: [
1690
+ area && /* @__PURE__ */ jsxs2(Fragment2, { children: [
1691
+ /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
1692
+ /* @__PURE__ */ jsx3("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
1693
+ /* @__PURE__ */ jsx3("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
1694
+ ] }) }),
1695
+ /* @__PURE__ */ jsx3("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
1696
+ ] }),
1697
+ /* @__PURE__ */ jsx3(
1698
+ "path",
1699
+ {
1700
+ d: monotoneLinePath(points),
1701
+ fill: "none",
1702
+ stroke: color,
1703
+ strokeWidth,
1704
+ strokeLinecap: "round",
1705
+ strokeLinejoin: "round"
1706
+ }
1707
+ )
1708
+ ]
1709
+ }
1710
+ );
1711
+ };
1712
+
1448
1713
  // src/app/data/metrics-shared.tsx
1449
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1714
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1450
1715
  var metricCardShellClass = cn(
1451
1716
  studioIntegrationCardClass,
1452
1717
  "aui-app-metric-card shadow-none",
@@ -1456,18 +1721,22 @@ var metricTilesRowClass = "grid w-full min-w-0";
1456
1721
  var metricCellDividerClass = "border-r border-border/40";
1457
1722
  var MetricCardHeader = ({
1458
1723
  title,
1724
+ titleTag,
1459
1725
  titleId,
1460
1726
  description,
1461
1727
  actions
1462
1728
  }) => {
1463
1729
  const headerClass = useAppDensityClass("metricCardHeader");
1464
1730
  if (!title && !description && !actions) return null;
1465
- return /* @__PURE__ */ jsxs2("header", { className: headerClass, children: [
1466
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
1467
- title ? /* @__PURE__ */ jsx3("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }) : null,
1468
- description ? /* @__PURE__ */ jsx3("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
1731
+ return /* @__PURE__ */ jsxs3("header", { className: cn(headerClass, "items-center"), children: [
1732
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
1733
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2.5", children: [
1734
+ title ? /* @__PURE__ */ jsx4("h3", { id: titleId, className: "text-sm font-semibold text-foreground tracking-tight select-none", children: title }) : null,
1735
+ 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
1736
+ ] }),
1737
+ description ? /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-muted-foreground leading-normal select-none", children: description }) : null
1469
1738
  ] }),
1470
- actions ? /* @__PURE__ */ jsx3("div", { className: "shrink-0", children: actions }) : null
1739
+ actions ? /* @__PURE__ */ jsx4("div", { className: "shrink-0 flex items-center", children: actions }) : null
1471
1740
  ] });
1472
1741
  };
1473
1742
  function metricTilesGridColsClass(n) {
@@ -1478,6 +1747,8 @@ function metricTilesGridColsClass(n) {
1478
1747
  return "grid-cols-2";
1479
1748
  case 3:
1480
1749
  return "grid-cols-3";
1750
+ case 4:
1751
+ return "grid-cols-2 md:grid-cols-4";
1481
1752
  case 5:
1482
1753
  return "grid-cols-2 sm:grid-cols-5";
1483
1754
  case 6:
@@ -1488,12 +1759,25 @@ function metricTilesGridColsClass(n) {
1488
1759
  }
1489
1760
 
1490
1761
  // src/app/data/MetricTile.tsx
1491
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1762
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1492
1763
  var trendToneClass = {
1493
1764
  up: "border-border/80 bg-muted/40 text-muted-foreground",
1494
1765
  down: "border-border/80 bg-muted/40 text-muted-foreground",
1495
1766
  neutral: "border-border/80 bg-muted/30 text-muted-foreground"
1496
1767
  };
1768
+ var inlineTrendToneClass = {
1769
+ up: "text-blue-500/90 dark:text-blue-400/95 font-medium",
1770
+ down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
1771
+ neutral: "text-muted-foreground/80"
1772
+ };
1773
+ var activeToneClass = {
1774
+ default: "bg-foreground dark:bg-white",
1775
+ primary: "bg-primary",
1776
+ success: "bg-emerald-500",
1777
+ warn: "bg-amber-500",
1778
+ danger: "bg-destructive",
1779
+ neutral: "bg-border"
1780
+ };
1497
1781
  var metricTileInteractiveExtraClass = cn(
1498
1782
  "bg-transparent hover:bg-transparent active:bg-transparent",
1499
1783
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10"
@@ -1504,58 +1788,86 @@ var MetricTile = ({
1504
1788
  unit,
1505
1789
  trend,
1506
1790
  trendTone = "neutral",
1791
+ trendVariant = "pill",
1507
1792
  active = false,
1793
+ activeTone = "default",
1508
1794
  showDivider = false,
1509
1795
  onSelect,
1796
+ sparklineData,
1797
+ sparklineConfig,
1798
+ sparkline,
1510
1799
  ariaLabel,
1511
1800
  className
1512
1801
  }) => {
1513
1802
  const metricTileBaseClass = useAppDensityClass("metricTile");
1514
- const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
1515
- active ? /* @__PURE__ */ jsx4(
1803
+ const hasSparkline = Boolean(sparkline || sparklineData);
1804
+ const content = /* @__PURE__ */ jsxs4(Fragment3, { children: [
1805
+ active ? /* @__PURE__ */ jsx5(
1516
1806
  "span",
1517
1807
  {
1518
1808
  "aria-hidden": true,
1519
- className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground dark:bg-white"
1809
+ className: cn(
1810
+ "absolute inset-x-0 bottom-0 h-0.5 z-20 transition-colors duration-200",
1811
+ activeToneClass[activeTone]
1812
+ )
1520
1813
  }
1521
1814
  ) : null,
1522
- /* @__PURE__ */ jsx4("span", { className: "text-xs font-normal text-muted-foreground", children: label }),
1523
- /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-2", children: [
1524
- /* @__PURE__ */ jsxs3("span", { className: "flex items-baseline gap-1", children: [
1525
- /* @__PURE__ */ jsx4("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
1526
- unit ? /* @__PURE__ */ jsx4("span", { className: "text-xs font-normal text-muted-foreground", children: unit }) : null
1527
- ] }),
1528
- trend ? /* @__PURE__ */ jsx4(
1529
- "span",
1530
- {
1531
- className: cn(
1532
- "rounded-full border px-1.5 py-0.5 text-xs font-normal",
1533
- trendToneClass[trendTone]
1534
- ),
1535
- children: trend
1536
- }
1537
- ) : null
1815
+ hasSparkline ? /* @__PURE__ */ jsx5("div", { className: "absolute inset-x-0 bottom-0.5 h-9 w-full overflow-hidden pointer-events-none z-0 opacity-45 dark:opacity-35 select-none", children: sparkline ?? /* @__PURE__ */ jsx5(
1816
+ Sparkline,
1817
+ {
1818
+ data: sparklineData,
1819
+ width: 160,
1820
+ height: 36,
1821
+ className: "w-full h-full",
1822
+ color: trendTone === "up" ? "var(--primary, #3b82f6)" : trendTone === "down" ? "var(--destructive, #f43f5e)" : "var(--muted-foreground, #64748b)",
1823
+ ...sparklineConfig
1824
+ }
1825
+ ) }) : null,
1826
+ /* @__PURE__ */ jsxs4("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
1827
+ /* @__PURE__ */ jsx5("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
1828
+ /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-2", children: [
1829
+ /* @__PURE__ */ jsxs4("span", { className: "flex items-baseline gap-1", children: [
1830
+ /* @__PURE__ */ jsx5("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
1831
+ unit ? /* @__PURE__ */ jsx5("span", { className: "text-xs font-medium text-muted-foreground", children: unit }) : null
1832
+ ] }),
1833
+ trend ? trendVariant === "inline" ? /* @__PURE__ */ jsx5("span", { className: cn("text-xs leading-none select-none", inlineTrendToneClass[trendTone]), children: trend }) : /* @__PURE__ */ jsx5(
1834
+ "span",
1835
+ {
1836
+ className: cn(
1837
+ "rounded-full border px-1.5 py-0.5 text-xs font-normal select-none",
1838
+ trendToneClass[trendTone]
1839
+ ),
1840
+ children: trend
1841
+ }
1842
+ ) : null
1843
+ ] })
1538
1844
  ] })
1539
1845
  ] });
1540
1846
  const divider = showDivider ? metricCellDividerClass : void 0;
1541
1847
  if (onSelect) {
1542
- return /* @__PURE__ */ jsx4(
1848
+ return /* @__PURE__ */ jsx5(
1543
1849
  "button",
1544
1850
  {
1545
1851
  type: "button",
1546
1852
  onClick: onSelect,
1547
1853
  "aria-pressed": active,
1548
1854
  "aria-label": ariaLabel,
1549
- className: cn(metricTileBaseClass, metricTileInteractiveExtraClass, divider, className),
1855
+ className: cn(
1856
+ metricTileBaseClass,
1857
+ "transition-all duration-200 hover:bg-muted/10",
1858
+ metricTileInteractiveExtraClass,
1859
+ divider,
1860
+ className
1861
+ ),
1550
1862
  children: content
1551
1863
  }
1552
1864
  );
1553
1865
  }
1554
- return /* @__PURE__ */ jsx4("div", { className: cn(metricTileBaseClass, divider, className), children: content });
1866
+ return /* @__PURE__ */ jsx5("div", { className: cn(metricTileBaseClass, divider, className), children: content });
1555
1867
  };
1556
1868
 
1557
1869
  // src/app/theme/ThemePresetGallery.tsx
1558
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1870
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1559
1871
 
1560
1872
  // src/app/layout/app-shell-chat-context.tsx
1561
1873
  import { createContext as createContext2, useContext as useContext2 } from "react";
@@ -1581,8 +1893,8 @@ function useAppShellNav() {
1581
1893
 
1582
1894
  // src/app/layout/AppShell.tsx
1583
1895
  import { motion, useReducedMotion } from "motion/react";
1584
- import { useCallback, useMemo, useState } from "react";
1585
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1896
+ import { useCallback, useEffect, useMemo, useState } from "react";
1897
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1586
1898
  var floatingTriggerClass = cn(
1587
1899
  "aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
1588
1900
  "bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
@@ -1600,39 +1912,57 @@ var AppShellBody = ({
1600
1912
  sidebar,
1601
1913
  topbarContent,
1602
1914
  mainClassName,
1915
+ contentFill = false,
1603
1916
  insetPaddingPx,
1604
1917
  insetExpanded,
1605
1918
  children
1606
1919
  }) => {
1920
+ const [isMobile, setIsMobile] = useState(() => {
1921
+ if (typeof window === "undefined") return false;
1922
+ return window.innerWidth < 768;
1923
+ });
1924
+ useEffect(() => {
1925
+ if (typeof window === "undefined") return;
1926
+ const onResize = () => setIsMobile(window.innerWidth < 768);
1927
+ onResize();
1928
+ window.addEventListener("resize", onResize);
1929
+ return () => window.removeEventListener("resize", onResize);
1930
+ }, []);
1607
1931
  const reducedMotion = useReducedMotion();
1608
1932
  const layoutDirection = insetExpanded ? "expand" : "collapse";
1609
1933
  const layoutTransition = studioSidebarWidthTransition(
1610
1934
  !!reducedMotion,
1611
1935
  layoutDirection
1612
1936
  );
1613
- const insetPadding = sidebar ? insetPaddingPx : 0;
1614
- return /* @__PURE__ */ jsx6(
1937
+ const insetPadding = sidebar && !isMobile ? insetPaddingPx : 0;
1938
+ return /* @__PURE__ */ jsx7(
1615
1939
  motion.div,
1616
1940
  {
1617
1941
  className: "aui-app-shell-body relative z-10 flex min-h-0 min-w-0 flex-1 flex-col",
1618
1942
  initial: false,
1619
1943
  animate: { paddingLeft: insetPadding },
1620
1944
  transition: layoutTransition,
1621
- children: /* @__PURE__ */ jsxs5(
1945
+ children: /* @__PURE__ */ jsxs6(
1622
1946
  "div",
1623
1947
  {
1624
1948
  className: cn(
1625
- "aui-app-shell-scroll flex min-h-0 flex-1 flex-col overflow-y-auto",
1949
+ "aui-app-shell-scroll flex min-h-0 flex-1 flex-col",
1950
+ // Padded scroll region by default; a full-bleed page (chat / canvas) owns
1951
+ // its own scroll, so clip here and let the bounded `main` fill exactly.
1952
+ contentFill ? "overflow-hidden" : "overflow-y-auto",
1626
1953
  !topbarContent && appShellInsetTopClass
1627
1954
  ),
1628
1955
  children: [
1629
- topbarContent ? /* @__PURE__ */ jsx6("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx6("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
1630
- /* @__PURE__ */ jsx6(
1956
+ topbarContent ? /* @__PURE__ */ jsx7("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx7("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
1957
+ /* @__PURE__ */ jsx7(
1631
1958
  "main",
1632
1959
  {
1633
1960
  className: cn(
1634
- "aui-app-shell-main min-w-0 flex-1",
1635
- appShellInsetBottomClass,
1961
+ // Bounded flex column by default so `h-full` / `flex-1 min-h-0` children
1962
+ // (full-page chat, canvas) resolve a height without `mainClassName` surgery.
1963
+ "aui-app-shell-main flex min-h-0 min-w-0 flex-1 flex-col",
1964
+ // Bottom breathing room for scrolling content; full-bleed pages skip it.
1965
+ !contentFill && appShellInsetBottomClass,
1636
1966
  mainClassName
1637
1967
  ),
1638
1968
  children
@@ -1662,7 +1992,8 @@ var AppShell = ({
1662
1992
  defaultNavOpen = false,
1663
1993
  onNavOpenChange,
1664
1994
  className,
1665
- mainClassName
1995
+ mainClassName,
1996
+ contentFill = false
1666
1997
  }) => {
1667
1998
  const topbarContent = topbar ?? header;
1668
1999
  const hasChat = Boolean(chat);
@@ -1703,18 +2034,19 @@ var AppShell = ({
1703
2034
  setInsetPaddingPx(insetPx);
1704
2035
  }, []);
1705
2036
  const insetExpanded = insetPaddingPx >= SIDEBAR_INSET_PX_EXPANDED;
1706
- const shellBody = /* @__PURE__ */ jsx6(
2037
+ const shellBody = /* @__PURE__ */ jsx7(
1707
2038
  AppShellBody,
1708
2039
  {
1709
2040
  sidebar,
1710
2041
  topbarContent,
1711
2042
  mainClassName,
2043
+ contentFill,
1712
2044
  insetPaddingPx,
1713
2045
  insetExpanded,
1714
2046
  children
1715
2047
  }
1716
2048
  );
1717
- const tree = /* @__PURE__ */ jsx6(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs5(
2049
+ const tree = /* @__PURE__ */ jsx7(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs6(
1718
2050
  "div",
1719
2051
  {
1720
2052
  className: cn(
@@ -1724,7 +2056,7 @@ var AppShell = ({
1724
2056
  style: studioChromeShellStyle,
1725
2057
  children: [
1726
2058
  sidebar,
1727
- sidebar && navOpen ? /* @__PURE__ */ jsx6(
2059
+ sidebar && navOpen ? /* @__PURE__ */ jsx7(
1728
2060
  "button",
1729
2061
  {
1730
2062
  type: "button",
@@ -1734,7 +2066,7 @@ var AppShell = ({
1734
2066
  }
1735
2067
  ) : null,
1736
2068
  shellBody,
1737
- hasChat && chatOpen ? /* @__PURE__ */ jsx6(
2069
+ hasChat && chatOpen ? /* @__PURE__ */ jsx7(
1738
2070
  "div",
1739
2071
  {
1740
2072
  className: floatingPanelClass,
@@ -1747,7 +2079,7 @@ var AppShell = ({
1747
2079
  children: chat
1748
2080
  }
1749
2081
  ) : null,
1750
- hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx6(
2082
+ hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx7(
1751
2083
  "button",
1752
2084
  {
1753
2085
  type: "button",
@@ -1760,11 +2092,11 @@ var AppShell = ({
1760
2092
  ]
1761
2093
  }
1762
2094
  ) });
1763
- const withNav = /* @__PURE__ */ jsx6(AppShellNavProvider, { value: navControls, children: tree });
2095
+ const withNav = /* @__PURE__ */ jsx7(AppShellNavProvider, { value: navControls, children: tree });
1764
2096
  if (!hasChat) {
1765
2097
  return withNav;
1766
2098
  }
1767
- return /* @__PURE__ */ jsx6(
2099
+ return /* @__PURE__ */ jsx7(
1768
2100
  AppShellChatProvider,
1769
2101
  {
1770
2102
  value: {
@@ -1778,23 +2110,6 @@ var AppShell = ({
1778
2110
  );
1779
2111
  };
1780
2112
 
1781
- // src/app/layout/AppShellTopbar.tsx
1782
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1783
- var AppShellTopbar = ({
1784
- start,
1785
- actions,
1786
- children,
1787
- className
1788
- }) => {
1789
- return /* @__PURE__ */ jsxs6("div", { className: cn("aui-app-shell-topbar", appShellTopbarRowClass, className), children: [
1790
- /* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1791
- start,
1792
- children
1793
- ] }),
1794
- actions ? /* @__PURE__ */ jsx7("div", { className: "aui-app-shell-topbar-actions flex shrink-0 items-center gap-2", children: actions }) : null
1795
- ] });
1796
- };
1797
-
1798
2113
  // src/app/layout/AppShellChatTrigger.tsx
1799
2114
  import { jsx as jsx8 } from "react/jsx-runtime";
1800
2115
  var floatingPositionClass = "fixed bottom-6 right-6 z-50 max-sm:bottom-4 max-sm:right-4";
@@ -1858,11 +2173,14 @@ var PageHeader = ({
1858
2173
  className
1859
2174
  }) => {
1860
2175
  const pageHeaderClass = useAppDensityClass("pageHeader");
2176
+ if (title == null && description == null && actions == null) {
2177
+ return null;
2178
+ }
1861
2179
  return /* @__PURE__ */ jsxs7("header", { className: cn("aui-app-page-header", pageHeaderClass, className), children: [
1862
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
1863
- /* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
2180
+ title != null || description != null ? /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
2181
+ title != null ? /* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }) : null,
1864
2182
  description ? /* @__PURE__ */ jsx10("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
1865
- ] }),
2183
+ ] }) : null,
1866
2184
  actions ? /* @__PURE__ */ jsx10("div", { className: "aui-app-page-header-actions flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
1867
2185
  ] });
1868
2186
  };
@@ -1872,32 +2190,47 @@ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1872
2190
  var PageFrame = ({
1873
2191
  children,
1874
2192
  breadcrumbs,
2193
+ width,
2194
+ fill = false,
2195
+ fillPadded = false,
1875
2196
  className,
1876
2197
  ...headerProps
1877
2198
  }) => {
1878
2199
  const density = useAppDensity();
1879
- const pageColumnClass = useAppDensityClass("pageColumn");
1880
- return /* @__PURE__ */ jsxs8(
1881
- "div",
1882
- {
1883
- className: cn("aui-app-page", pageColumnClass, className),
1884
- "data-density": density,
1885
- children: [
1886
- breadcrumbs,
1887
- /* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
1888
- children
1889
- ]
1890
- }
1891
- );
2200
+ const stackClass = useAppDensityClass("pageStack");
2201
+ const columnClass = appPageColumn(width, density);
2202
+ const rootClass = fill ? cn(
2203
+ "flex min-h-0 min-w-0 flex-1 flex-col",
2204
+ fillPadded && columnClass
2205
+ ) : columnClass;
2206
+ return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-page", rootClass, className), "data-density": density, children: [
2207
+ breadcrumbs,
2208
+ /* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
2209
+ fill ? children : /* @__PURE__ */ jsx11("div", { className: cn("aui-app-page-stack", stackClass), children })
2210
+ ] });
1892
2211
  };
1893
2212
  var Page = ({
1894
2213
  density = "default",
1895
2214
  children,
1896
2215
  breadcrumbs,
2216
+ width,
2217
+ fill = false,
2218
+ fillPadded = false,
1897
2219
  className,
1898
2220
  ...headerProps
1899
2221
  }) => {
1900
- return /* @__PURE__ */ jsx11(AppDensityProvider, { density, children: /* @__PURE__ */ jsx11(PageFrame, { breadcrumbs, className, ...headerProps, children }) });
2222
+ return /* @__PURE__ */ jsx11(AppDensityProvider, { density, children: /* @__PURE__ */ jsx11(
2223
+ PageFrame,
2224
+ {
2225
+ breadcrumbs,
2226
+ width,
2227
+ fill,
2228
+ fillPadded,
2229
+ className,
2230
+ ...headerProps,
2231
+ children
2232
+ }
2233
+ ) });
1901
2234
  };
1902
2235
 
1903
2236
  // src/app/layout/Section.tsx
@@ -1917,15 +2250,61 @@ var Section = ({
1917
2250
  ] });
1918
2251
  };
1919
2252
 
2253
+ // src/app/layout/Stack.tsx
2254
+ import { jsx as jsx13 } from "react/jsx-runtime";
2255
+ var GAP_CLASS = {
2256
+ none: "gap-0",
2257
+ xs: "gap-1",
2258
+ sm: "gap-2",
2259
+ md: "gap-4",
2260
+ lg: "gap-6",
2261
+ xl: "gap-8"
2262
+ };
2263
+ var ALIGN_CLASS = {
2264
+ start: "items-start",
2265
+ center: "items-center",
2266
+ end: "items-end",
2267
+ stretch: "items-stretch"
2268
+ };
2269
+ var JUSTIFY_CLASS = {
2270
+ start: "justify-start",
2271
+ center: "justify-center",
2272
+ end: "justify-end",
2273
+ between: "justify-between"
2274
+ };
2275
+ var Stack = ({
2276
+ children,
2277
+ direction = "vertical",
2278
+ gap = "md",
2279
+ align,
2280
+ justify,
2281
+ wrap = false,
2282
+ className
2283
+ }) => /* @__PURE__ */ jsx13(
2284
+ "div",
2285
+ {
2286
+ className: cn(
2287
+ "flex min-w-0",
2288
+ direction === "vertical" ? "flex-col" : "flex-row",
2289
+ GAP_CLASS[gap],
2290
+ align && ALIGN_CLASS[align],
2291
+ justify && JUSTIFY_CLASS[justify],
2292
+ wrap && "flex-wrap",
2293
+ className
2294
+ ),
2295
+ children
2296
+ }
2297
+ );
2298
+
1920
2299
  // src/app/copilot/app-copilot-context.tsx
1921
2300
  import { createContext as createContext4, useContext as useContext4 } from "react";
1922
- import { jsx as jsx13 } from "react/jsx-runtime";
2301
+ import { jsx as jsx14 } from "react/jsx-runtime";
1923
2302
  var AppCopilotContext = createContext4(null);
1924
2303
  var AppCopilotProvider = ({
1925
2304
  value,
1926
2305
  children
1927
2306
  }) => {
1928
- return /* @__PURE__ */ jsx13(AppCopilotContext.Provider, { value, children });
2307
+ return /* @__PURE__ */ jsx14(AppCopilotContext.Provider, { value, children });
1929
2308
  };
1930
2309
  function useAppCopilotContext() {
1931
2310
  return useContext4(AppCopilotContext) ?? {};
@@ -1933,7 +2312,7 @@ function useAppCopilotContext() {
1933
2312
 
1934
2313
  // src/app/chat/AppChatPanel.tsx
1935
2314
  import { XIcon } from "lucide-react";
1936
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
2315
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
1937
2316
  var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
1938
2317
  var chromeClass = cn(
1939
2318
  "aui-app-chat-panel-chrome relative z-20 flex min-h-10 shrink-0 items-center justify-end px-2 pt-2"
@@ -1979,17 +2358,17 @@ var AppChatPanel = ({
1979
2358
  }) => {
1980
2359
  const shellChat = useAppShellChat();
1981
2360
  return /* @__PURE__ */ jsxs10("div", { className: cn(shellClass, className), children: [
1982
- shellChat?.collapsible ? /* @__PURE__ */ jsx14("div", { className: chromeClass, children: /* @__PURE__ */ jsx14(
2361
+ shellChat?.collapsible ? /* @__PURE__ */ jsx15("div", { className: chromeClass, children: /* @__PURE__ */ jsx15(
1983
2362
  "button",
1984
2363
  {
1985
2364
  type: "button",
1986
2365
  className: closeButtonClass,
1987
2366
  onClick: () => shellChat.setOpen(false),
1988
2367
  "aria-label": "Close assistant",
1989
- children: /* @__PURE__ */ jsx14(XIcon, { className: "size-4", "aria-hidden": true })
2368
+ children: /* @__PURE__ */ jsx15(XIcon, { className: "size-4", "aria-hidden": true })
1990
2369
  }
1991
2370
  ) }) : null,
1992
- /* @__PURE__ */ jsx14("div", { className: bodyClass, children: /* @__PURE__ */ jsx14(
2371
+ /* @__PURE__ */ jsx15("div", { className: bodyClass, children: /* @__PURE__ */ jsx15(
1993
2372
  TimbalRuntimeProvider,
1994
2373
  {
1995
2374
  workforceId,
@@ -1999,7 +2378,7 @@ var AppChatPanel = ({
1999
2378
  attachmentsUploadUrl,
2000
2379
  attachmentsAccept,
2001
2380
  debug,
2002
- children: /* @__PURE__ */ jsx14(
2381
+ children: /* @__PURE__ */ jsx15(
2003
2382
  Thread,
2004
2383
  {
2005
2384
  variant: "panel",
@@ -2020,41 +2399,121 @@ var AppChatPanel = ({
2020
2399
  };
2021
2400
 
2022
2401
  // src/app/surfaces/SurfaceCard.tsx
2023
- import { jsx as jsx15 } from "react/jsx-runtime";
2024
- var SurfaceCard = ({ children, className }) => {
2402
+ import { jsx as jsx16 } from "react/jsx-runtime";
2403
+ var surfaceVariantClass = {
2404
+ default: "",
2405
+ muted: "bg-none bg-muted/40 shadow-none border border-border/20",
2406
+ outline: "bg-none bg-transparent border border-border shadow-none",
2407
+ elevated: "shadow-card-elevated border border-border/80",
2408
+ 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",
2409
+ 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"
2410
+ };
2411
+ var surfaceToneClass = {
2412
+ default: "",
2413
+ primary: "ring-1 ring-inset ring-primary/25",
2414
+ success: "ring-1 ring-inset ring-emerald-500/25",
2415
+ warn: "ring-1 ring-inset ring-amber-500/30",
2416
+ danger: "ring-1 ring-inset ring-destructive/25"
2417
+ };
2418
+ var SurfaceCard = ({
2419
+ children,
2420
+ variant = "default",
2421
+ tone = "default",
2422
+ hoverable,
2423
+ onClick,
2424
+ ariaLabel,
2425
+ className
2426
+ }) => {
2025
2427
  const surfaceCardClass = useAppDensityClass("surfaceCard");
2026
- return /* @__PURE__ */ jsx15("div", { className: cn("aui-app-surface-card", surfaceCardClass, className), children });
2428
+ const isInteractive = Boolean(onClick);
2429
+ const shouldHover = hoverable ?? isInteractive;
2430
+ const baseClass = cn(
2431
+ "aui-app-surface-card",
2432
+ surfaceCardClass,
2433
+ surfaceVariantClass[variant],
2434
+ surfaceToneClass[tone],
2435
+ shouldHover && "transition-all duration-200 hover:border-border/80 hover:shadow-md",
2436
+ 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",
2437
+ className
2438
+ );
2439
+ if (isInteractive) {
2440
+ return /* @__PURE__ */ jsx16(
2441
+ "button",
2442
+ {
2443
+ type: "button",
2444
+ onClick,
2445
+ "aria-label": ariaLabel,
2446
+ className: baseClass,
2447
+ children
2448
+ }
2449
+ );
2450
+ }
2451
+ return /* @__PURE__ */ jsx16("div", { className: baseClass, children });
2027
2452
  };
2028
2453
 
2029
2454
  // src/app/surfaces/StatTile.tsx
2030
- import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
2031
- var StatTile = ({ label, value, hint, className }) => {
2455
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
2456
+ var statValueToneClass = {
2457
+ default: "",
2458
+ primary: "text-primary",
2459
+ success: "text-emerald-700 dark:text-emerald-400",
2460
+ warn: "text-amber-700 dark:text-amber-400",
2461
+ danger: "text-destructive"
2462
+ };
2463
+ var StatTile = ({
2464
+ label,
2465
+ value,
2466
+ hint,
2467
+ tone = "default",
2468
+ className
2469
+ }) => {
2032
2470
  const statTileClass = useAppDensityClass("statTile");
2033
2471
  return /* @__PURE__ */ jsxs11("div", { className: cn("aui-app-stat-tile", statTileClass, className), children: [
2034
- /* @__PURE__ */ jsx16("span", { className: appStatLabelClass, children: label }),
2035
- /* @__PURE__ */ jsx16("span", { className: appStatValueClass, children: value }),
2036
- hint ? /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: hint }) : null
2472
+ /* @__PURE__ */ jsx17("span", { className: appStatLabelClass, children: label }),
2473
+ /* @__PURE__ */ jsx17("span", { className: cn(appStatValueClass, statValueToneClass[tone]), children: value }),
2474
+ hint ? /* @__PURE__ */ jsx17("span", { className: "text-xs text-muted-foreground", children: hint }) : null
2037
2475
  ] });
2038
2476
  };
2039
2477
 
2040
2478
  // src/app/surfaces/EmptyState.tsx
2041
- import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
2479
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
2042
2480
  var EmptyState = ({
2043
2481
  title,
2044
2482
  description,
2045
2483
  action,
2046
- className
2484
+ className,
2485
+ variant = "default"
2047
2486
  }) => {
2048
- const emptyStateClass = useAppDensityClass("emptyState");
2049
- return /* @__PURE__ */ jsxs12("div", { className: cn("aui-app-empty-state", emptyStateClass, className), children: [
2050
- /* @__PURE__ */ jsx17("p", { className: appEmptyStateTitleClass, children: title }),
2051
- description ? /* @__PURE__ */ jsx17("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
2052
- action
2487
+ const densityClass = useAppDensityClass("emptyState");
2488
+ if (variant === "layered") {
2489
+ return /* @__PURE__ */ jsx18(
2490
+ "div",
2491
+ {
2492
+ className: cn(
2493
+ "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",
2494
+ className
2495
+ ),
2496
+ 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: [
2497
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5 items-center justify-center", children: [
2498
+ /* @__PURE__ */ jsx18("h3", { className: "text-base font-semibold text-foreground tracking-tight", children: title }),
2499
+ description ? /* @__PURE__ */ jsx18("p", { className: "max-w-sm text-sm text-muted-foreground leading-relaxed", children: description }) : null
2500
+ ] }),
2501
+ action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
2502
+ ] })
2503
+ }
2504
+ );
2505
+ }
2506
+ const isCompact = variant === "compact";
2507
+ 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);
2508
+ return /* @__PURE__ */ jsxs12("div", { className: cn(finalClass, className), children: [
2509
+ /* @__PURE__ */ jsx18("p", { className: cn(appEmptyStateTitleClass, "font-semibold tracking-tight"), children: title }),
2510
+ description ? /* @__PURE__ */ jsx18("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
2511
+ action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
2053
2512
  ] });
2054
2513
  };
2055
2514
 
2056
2515
  // src/app/surfaces/StatusBadge.tsx
2057
- import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2516
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
2058
2517
  var statusBadgeToneClass = {
2059
2518
  default: "bg-muted text-foreground ring-border",
2060
2519
  primary: "bg-primary/10 text-primary ring-primary/20",
@@ -2087,7 +2546,7 @@ var StatusBadge = ({
2087
2546
  className
2088
2547
  ),
2089
2548
  children: [
2090
- dot ? /* @__PURE__ */ jsx18(
2549
+ dot ? /* @__PURE__ */ jsx19(
2091
2550
  "span",
2092
2551
  {
2093
2552
  "aria-hidden": true,
@@ -2104,7 +2563,7 @@ var StatusBadge = ({
2104
2563
  };
2105
2564
 
2106
2565
  // src/app/surfaces/AppConfirmDialog.tsx
2107
- import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2566
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
2108
2567
  var bodyClass2 = "flex flex-col gap-4 p-6";
2109
2568
  var titleClass = "pr-8";
2110
2569
  var actionsClass = "flex flex-wrap justify-end gap-2";
@@ -2119,15 +2578,15 @@ var AppConfirmDialog = ({
2119
2578
  destructive = false,
2120
2579
  className
2121
2580
  }) => {
2122
- return /* @__PURE__ */ jsx19(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx19(
2581
+ return /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx20(
2123
2582
  DialogContent,
2124
2583
  {
2125
2584
  className: cn("gap-0 p-0 sm:max-w-md", className),
2126
2585
  children: /* @__PURE__ */ jsxs14("div", { className: bodyClass2, children: [
2127
- /* @__PURE__ */ jsx19(DialogTitle, { className: titleClass, children: title }),
2128
- description ? /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: description }) : null,
2586
+ /* @__PURE__ */ jsx20(DialogTitle, { className: titleClass, children: title }),
2587
+ description ? /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: description }) : null,
2129
2588
  /* @__PURE__ */ jsxs14("div", { className: actionsClass, children: [
2130
- /* @__PURE__ */ jsx19(
2589
+ /* @__PURE__ */ jsx20(
2131
2590
  TimbalV2Button,
2132
2591
  {
2133
2592
  type: "button",
@@ -2137,7 +2596,7 @@ var AppConfirmDialog = ({
2137
2596
  children: cancelLabel
2138
2597
  }
2139
2598
  ),
2140
- /* @__PURE__ */ jsx19(
2599
+ /* @__PURE__ */ jsx20(
2141
2600
  TimbalV2Button,
2142
2601
  {
2143
2602
  type: "button",
@@ -2157,7 +2616,7 @@ var AppConfirmDialog = ({
2157
2616
  };
2158
2617
 
2159
2618
  // src/app/surfaces/InfoCard.tsx
2160
- import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
2619
+ import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2161
2620
  var toneClass = {
2162
2621
  neutral: "border-border bg-muted/40",
2163
2622
  info: "border-primary/25 bg-primary/5",
@@ -2165,34 +2624,67 @@ var toneClass = {
2165
2624
  warn: "border-amber-500/25 bg-amber-500/5",
2166
2625
  danger: "border-destructive/25 bg-destructive/5"
2167
2626
  };
2627
+ var toneVerticalClass = {
2628
+ neutral: "border-border/60 bg-card hover:border-border dark:bg-card/40",
2629
+ info: "border-primary/20 bg-primary/5 hover:border-primary/45",
2630
+ success: "border-emerald-500/20 bg-emerald-500/5 hover:border-emerald-500/45",
2631
+ warn: "border-amber-500/25 bg-amber-500/5 hover:border-amber-500/45",
2632
+ danger: "border-destructive/20 bg-destructive/5 hover:border-destructive/45"
2633
+ };
2168
2634
  var InfoCard = ({
2169
2635
  title,
2170
2636
  children,
2171
2637
  icon,
2172
2638
  action,
2173
2639
  tone = "neutral",
2640
+ layout = "horizontal",
2641
+ onClick,
2642
+ ariaLabel,
2174
2643
  className
2175
- }) => /* @__PURE__ */ jsxs15(
2176
- "div",
2177
- {
2178
- className: cn(
2179
- "flex items-start gap-3 rounded-xl border p-4",
2180
- toneClass[tone],
2181
- className
2182
- ),
2183
- children: [
2184
- icon ? /* @__PURE__ */ jsx20("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
2185
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
2186
- title ? /* @__PURE__ */ jsx20("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
2187
- children ? /* @__PURE__ */ jsx20("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
2644
+ }) => {
2645
+ const isInteractive = Boolean(onClick);
2646
+ if (layout === "vertical") {
2647
+ const cardContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
2648
+ 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,
2649
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1.5 w-full text-left", children: [
2650
+ title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-semibold text-foreground tracking-tight leading-tight", children: title }) : null,
2651
+ children ? /* @__PURE__ */ jsx21("div", { className: "text-xs text-muted-foreground leading-relaxed", children }) : null
2188
2652
  ] }),
2189
- action ? /* @__PURE__ */ jsx20("div", { className: "shrink-0", children: action }) : null
2190
- ]
2653
+ action ? /* @__PURE__ */ jsx21("div", { className: "w-full mt-1", children: action }) : null
2654
+ ] });
2655
+ const baseClass2 = cn(
2656
+ "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)]",
2657
+ toneVerticalClass[tone],
2658
+ 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",
2659
+ className
2660
+ );
2661
+ if (isInteractive) {
2662
+ return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass2, children: cardContent });
2663
+ }
2664
+ return /* @__PURE__ */ jsx21("div", { className: baseClass2, children: cardContent });
2191
2665
  }
2192
- );
2666
+ const horizontalContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
2667
+ icon ? /* @__PURE__ */ jsx21("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
2668
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1 text-left", children: [
2669
+ title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
2670
+ children ? /* @__PURE__ */ jsx21("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
2671
+ ] }),
2672
+ action ? /* @__PURE__ */ jsx21("div", { className: "shrink-0", children: action }) : null
2673
+ ] });
2674
+ const baseClass = cn(
2675
+ "flex items-start gap-3 rounded-xl border p-4 transition-all duration-200",
2676
+ toneClass[tone],
2677
+ 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",
2678
+ className
2679
+ );
2680
+ if (isInteractive) {
2681
+ return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass, children: horizontalContent });
2682
+ }
2683
+ return /* @__PURE__ */ jsx21("div", { className: baseClass, children: horizontalContent });
2684
+ };
2193
2685
 
2194
2686
  // src/app/surfaces/StatusDot.tsx
2195
- import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
2687
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2196
2688
  var dotClass = {
2197
2689
  online: "bg-emerald-500",
2198
2690
  busy: "bg-amber-500",
@@ -2207,7 +2699,7 @@ var StatusDot = ({
2207
2699
  className
2208
2700
  }) => /* @__PURE__ */ jsxs16("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
2209
2701
  /* @__PURE__ */ jsxs16("span", { className: "relative flex size-2", children: [
2210
- pulse ? /* @__PURE__ */ jsx21(
2702
+ pulse ? /* @__PURE__ */ jsx22(
2211
2703
  "span",
2212
2704
  {
2213
2705
  className: cn(
@@ -2216,18 +2708,18 @@ var StatusDot = ({
2216
2708
  )
2217
2709
  }
2218
2710
  ) : null,
2219
- /* @__PURE__ */ jsx21("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
2711
+ /* @__PURE__ */ jsx22("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
2220
2712
  ] }),
2221
- label ? /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: label }) : null
2713
+ label ? /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: label }) : null
2222
2714
  ] });
2223
2715
 
2224
2716
  // src/app/surfaces/DescriptionList.tsx
2225
- import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
2717
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
2226
2718
  var DescriptionList = ({
2227
2719
  items,
2228
2720
  stacked = false,
2229
2721
  className
2230
- }) => /* @__PURE__ */ jsx22(
2722
+ }) => /* @__PURE__ */ jsx23(
2231
2723
  "dl",
2232
2724
  {
2233
2725
  className: cn(
@@ -2239,16 +2731,18 @@ var DescriptionList = ({
2239
2731
  {
2240
2732
  className: cn(
2241
2733
  "px-4 py-3",
2242
- stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4"
2734
+ stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4",
2735
+ item.className
2243
2736
  ),
2244
2737
  children: [
2245
- /* @__PURE__ */ jsx22("dt", { className: "text-sm text-muted-foreground", children: item.label }),
2246
- /* @__PURE__ */ jsx22(
2738
+ /* @__PURE__ */ jsx23("dt", { className: cn("text-sm text-muted-foreground", item.labelClassName), children: item.label }),
2739
+ /* @__PURE__ */ jsx23(
2247
2740
  "dd",
2248
2741
  {
2249
2742
  className: cn(
2250
2743
  "text-sm text-foreground",
2251
- !stacked && "text-right tabular-nums"
2744
+ !stacked && "text-right tabular-nums",
2745
+ item.valueClassName
2252
2746
  ),
2253
2747
  children: item.value
2254
2748
  }
@@ -2261,10 +2755,10 @@ var DescriptionList = ({
2261
2755
  );
2262
2756
 
2263
2757
  // src/app/surfaces/ExpandableSection.tsx
2264
- import { useId, useState as useState2 } from "react";
2758
+ import { useId as useId2, useState as useState2 } from "react";
2265
2759
  import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
2266
- import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
2267
- var Chevron = ({ open }) => /* @__PURE__ */ jsx23(
2760
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2761
+ var Chevron = ({ open }) => /* @__PURE__ */ jsx24(
2268
2762
  "svg",
2269
2763
  {
2270
2764
  viewBox: "0 0 24 24",
@@ -2278,7 +2772,7 @@ var Chevron = ({ open }) => /* @__PURE__ */ jsx23(
2278
2772
  strokeLinecap: "round",
2279
2773
  strokeLinejoin: "round",
2280
2774
  "aria-hidden": true,
2281
- children: /* @__PURE__ */ jsx23("path", { d: "m6 9 6 6 6-6" })
2775
+ children: /* @__PURE__ */ jsx24("path", { d: "m6 9 6 6 6-6" })
2282
2776
  }
2283
2777
  );
2284
2778
  var ExpandableSection = ({
@@ -2292,7 +2786,7 @@ var ExpandableSection = ({
2292
2786
  className
2293
2787
  }) => {
2294
2788
  const reduceMotion = useReducedMotion2();
2295
- const panelId = useId();
2789
+ const panelId = useId2();
2296
2790
  const [internalOpen, setInternalOpen] = useState2(defaultOpen);
2297
2791
  const open = openProp ?? internalOpen;
2298
2792
  const toggle = () => {
@@ -2310,15 +2804,15 @@ var ExpandableSection = ({
2310
2804
  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",
2311
2805
  children: [
2312
2806
  /* @__PURE__ */ jsxs18("span", { className: "flex min-w-0 items-center gap-3", children: [
2313
- 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,
2314
- /* @__PURE__ */ jsx23("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
2315
- 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
2807
+ 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,
2808
+ /* @__PURE__ */ jsx24("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
2809
+ 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
2316
2810
  ] }),
2317
- /* @__PURE__ */ jsx23(Chevron, { open })
2811
+ /* @__PURE__ */ jsx24(Chevron, { open })
2318
2812
  ]
2319
2813
  }
2320
2814
  ),
2321
- /* @__PURE__ */ jsx23(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx23(
2815
+ /* @__PURE__ */ jsx24(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx24(
2322
2816
  motion2.div,
2323
2817
  {
2324
2818
  id: panelId,
@@ -2327,7 +2821,7 @@ var ExpandableSection = ({
2327
2821
  exit: reduceMotion ? void 0 : { height: 0, opacity: 0 },
2328
2822
  transition: { duration: 0.2, ease: "easeOut" },
2329
2823
  className: "overflow-hidden",
2330
- children: /* @__PURE__ */ jsx23("div", { className: "bg-muted/20", children })
2824
+ children: /* @__PURE__ */ jsx24("div", { className: "bg-muted/20", children })
2331
2825
  },
2332
2826
  "body"
2333
2827
  ) : null })
@@ -2335,19 +2829,14 @@ var ExpandableSection = ({
2335
2829
  };
2336
2830
 
2337
2831
  // src/app/surfaces/ResourceCard.tsx
2338
- import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
2339
- var resourceCardShellClass = cn(
2340
- "flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
2341
- TIMBAL_V2_ELEVATED_SURFACE
2342
- );
2832
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
2343
2833
  var mediaShellClass = cn(
2344
2834
  "flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xl text-sm font-normal text-foreground",
2345
2835
  TIMBAL_V2_LOGO_TILE
2346
2836
  );
2347
2837
  var resourceCardInteractiveClass = cn(
2348
- resourceCardShellClass,
2349
- "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
2350
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2838
+ "flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
2839
+ TIMBAL_V2_ELEVATED_SURFACE
2351
2840
  );
2352
2841
  var ResourceCard = ({
2353
2842
  title,
@@ -2358,37 +2847,236 @@ var ResourceCard = ({
2358
2847
  action,
2359
2848
  onClick,
2360
2849
  ariaLabel,
2361
- className
2850
+ className,
2851
+ variant = "flat",
2852
+ avatarShape = "circle"
2362
2853
  }) => {
2363
- const body = /* @__PURE__ */ jsxs19(Fragment3, { children: [
2854
+ const isInteractive = Boolean(onClick);
2855
+ const shellClass3 = cn(
2856
+ "flex min-h-[8.5rem] flex-col rounded-2xl p-5 text-left font-normal transition-all duration-200",
2857
+ variant === "default" && TIMBAL_V2_ELEVATED_SURFACE,
2858
+ 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",
2859
+ variant === "outline" && "border border-border/70 bg-transparent shadow-none hover:bg-muted/5",
2860
+ 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"
2861
+ );
2862
+ const dynamicMediaShellClass = cn(
2863
+ "flex size-10 shrink-0 items-center justify-center overflow-hidden text-sm font-normal text-foreground transition-all duration-200",
2864
+ avatarShape === "circle" ? "rounded-full" : "rounded-xl",
2865
+ TIMBAL_V2_LOGO_TILE
2866
+ );
2867
+ const body = /* @__PURE__ */ jsxs19(Fragment5, { children: [
2364
2868
  /* @__PURE__ */ jsxs19("div", { className: "flex items-start gap-3", children: [
2365
- media ? /* @__PURE__ */ jsx24("span", { className: mediaShellClass, children: media }) : null,
2869
+ media ? /* @__PURE__ */ jsx25("span", { className: dynamicMediaShellClass, children: media }) : null,
2366
2870
  /* @__PURE__ */ jsxs19("div", { className: "min-w-0 flex-1 pt-0.5", children: [
2367
- /* @__PURE__ */ jsx24("p", { className: "truncate text-sm font-normal leading-snug text-foreground", children: title }),
2368
- subtitle ? /* @__PURE__ */ jsx24("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground", children: subtitle }) : null
2871
+ /* @__PURE__ */ jsx25("p", { className: "truncate text-sm font-semibold leading-snug text-foreground tracking-tight", children: title }),
2872
+ subtitle ? /* @__PURE__ */ jsx25("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground/90 leading-relaxed", children: subtitle }) : null
2369
2873
  ] }),
2370
- badge ? /* @__PURE__ */ jsx24("span", { className: "shrink-0 pt-0.5", children: badge }) : null
2874
+ badge ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 pt-0.5", children: badge }) : null
2371
2875
  ] }),
2372
2876
  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: [
2373
- /* @__PURE__ */ jsx24("span", { className: "min-w-0 truncate", children: footer }),
2374
- action ? /* @__PURE__ */ jsx24("span", { className: "shrink-0 opacity-80", children: action }) : null
2877
+ /* @__PURE__ */ jsx25("span", { className: "min-w-0 truncate", children: footer }),
2878
+ action ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 opacity-90", children: action }) : null
2879
+ ] }) : null
2880
+ ] });
2881
+ if (onClick) {
2882
+ return /* @__PURE__ */ jsx25(
2883
+ "button",
2884
+ {
2885
+ type: "button",
2886
+ onClick,
2887
+ "aria-label": ariaLabel,
2888
+ className: cn(shellClass3, className),
2889
+ children: body
2890
+ }
2891
+ );
2892
+ }
2893
+ return /* @__PURE__ */ jsx25("article", { className: cn(shellClass3, className), children: body });
2894
+ };
2895
+
2896
+ // src/app/surfaces/AlertCard.tsx
2897
+ import { ChevronRight } from "lucide-react";
2898
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
2899
+ var alertCardShellClass = cn(
2900
+ "flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
2901
+ TIMBAL_V2_ELEVATED_GRADIENT
2902
+ );
2903
+ var alertCardInteractiveClass = cn(
2904
+ "flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card cursor-pointer",
2905
+ TIMBAL_V2_ELEVATED_GRADIENT,
2906
+ "transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
2907
+ "hover:border-foreground/20",
2908
+ "hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
2909
+ "active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
2910
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2911
+ );
2912
+ var AlertCard = ({
2913
+ title,
2914
+ description,
2915
+ category,
2916
+ categoryTone = "default",
2917
+ status,
2918
+ statusTone = "default",
2919
+ action,
2920
+ trailing,
2921
+ onClick,
2922
+ ariaLabel,
2923
+ className
2924
+ }) => {
2925
+ const showTags = Boolean(category || status);
2926
+ const showTrailing = Boolean(trailing || onClick);
2927
+ const bodyContent = /* @__PURE__ */ jsxs20("div", { className: "flex-1 min-w-0 flex flex-col h-full", children: [
2928
+ showTags ? /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap items-center gap-1.5 mb-2.5", children: [
2929
+ category ? /* @__PURE__ */ jsx26(StatusBadge, { tone: categoryTone, children: category }) : null,
2930
+ status ? /* @__PURE__ */ jsx26(StatusBadge, { tone: statusTone, children: status }) : null
2931
+ ] }) : null,
2932
+ /* @__PURE__ */ jsx26("h4", { className: "text-sm font-medium leading-snug text-foreground", children: title }),
2933
+ description ? /* @__PURE__ */ jsx26("p", { className: "mt-1.5 text-xs text-muted-foreground leading-normal", children: description }) : null,
2934
+ action ? /* @__PURE__ */ jsxs20("div", { className: "mt-auto pt-3 text-xs text-muted-foreground/90 leading-normal", children: [
2935
+ /* @__PURE__ */ jsx26("strong", { className: "font-semibold text-foreground/80", children: "Action: " }),
2936
+ action
2375
2937
  ] }) : null
2376
2938
  ] });
2939
+ const cardContent = /* @__PURE__ */ jsxs20("div", { className: "flex items-start justify-between gap-4 w-full h-full", children: [
2940
+ bodyContent,
2941
+ 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
2942
+ ] });
2943
+ if (onClick) {
2944
+ return /* @__PURE__ */ jsx26(
2945
+ "button",
2946
+ {
2947
+ type: "button",
2948
+ onClick,
2949
+ "aria-label": ariaLabel,
2950
+ className: cn(alertCardInteractiveClass, className),
2951
+ children: cardContent
2952
+ }
2953
+ );
2954
+ }
2955
+ return /* @__PURE__ */ jsx26("article", { className: cn(alertCardShellClass, className), children: cardContent });
2956
+ };
2957
+
2958
+ // src/app/surfaces/CatalogCard.tsx
2959
+ import { ExternalLink } from "lucide-react";
2960
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
2961
+ var catalogCardShellClass = cn(
2962
+ "flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal",
2963
+ TIMBAL_V2_ELEVATED_GRADIENT
2964
+ );
2965
+ var catalogCardInteractiveClass = cn(
2966
+ "flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal cursor-pointer",
2967
+ TIMBAL_V2_ELEVATED_GRADIENT,
2968
+ "transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
2969
+ "hover:border-foreground/20",
2970
+ "hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
2971
+ "active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
2972
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2973
+ );
2974
+ var CatalogCard = ({
2975
+ title,
2976
+ subtitle,
2977
+ logo,
2978
+ href,
2979
+ badge,
2980
+ description,
2981
+ tags,
2982
+ footerLinks,
2983
+ copyValue,
2984
+ copyLabel = "Copy ID",
2985
+ actions,
2986
+ onClick,
2987
+ ariaLabel,
2988
+ className
2989
+ }) => {
2990
+ const showHeaderTags = Boolean(subtitle || badge);
2991
+ const showFooter = Boolean(footerLinks && footerLinks.length > 0 || copyValue || actions);
2992
+ const mainContent = /* @__PURE__ */ jsxs21("div", { className: "flex-1 p-5 flex flex-col h-full min-h-0", children: [
2993
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-3", children: [
2994
+ logo ? /* @__PURE__ */ jsx27("div", { className: "size-8 shrink-0 flex items-center justify-center overflow-hidden rounded-lg bg-muted/40", children: logo }) : null,
2995
+ /* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1 pt-0.5", children: [
2996
+ /* @__PURE__ */ jsx27("div", { className: "flex items-center gap-1.5 flex-wrap", children: href ? /* @__PURE__ */ jsxs21(
2997
+ "a",
2998
+ {
2999
+ href,
3000
+ target: "_blank",
3001
+ rel: "noopener noreferrer",
3002
+ className: "inline-flex items-center gap-1 hover:underline text-sm font-medium leading-snug text-foreground focus-visible:outline-none focus-visible:underline",
3003
+ onClick: (e) => e.stopPropagation(),
3004
+ children: [
3005
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: title }),
3006
+ /* @__PURE__ */ jsx27(ExternalLink, { className: "size-3.5 shrink-0 text-muted-foreground/60" })
3007
+ ]
3008
+ }
3009
+ ) : /* @__PURE__ */ jsx27("h4", { className: "text-sm font-medium leading-snug text-foreground truncate", children: title }) }),
3010
+ showHeaderTags ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center gap-2 mt-1", children: [
3011
+ subtitle ? /* @__PURE__ */ jsx27("span", { className: "text-xs font-medium text-muted-foreground", children: subtitle }) : null,
3012
+ badge ? /* @__PURE__ */ jsx27("div", { className: "shrink-0 flex items-center", children: badge }) : null
3013
+ ] }) : null
3014
+ ] })
3015
+ ] }),
3016
+ description ? /* @__PURE__ */ jsx27("p", { className: "mt-3 text-xs leading-relaxed text-muted-foreground line-clamp-3", children: description }) : null,
3017
+ 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: [
3018
+ idx > 0 && /* @__PURE__ */ jsx27("span", { className: "mr-2 text-muted-foreground/30", children: "\u2022" }),
3019
+ tag
3020
+ ] }, idx)) }) : null
3021
+ ] });
3022
+ 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: [
3023
+ /* @__PURE__ */ jsx27("div", { className: "flex flex-wrap items-center gap-4", children: footerLinks && footerLinks.length > 0 ? footerLinks.map((link, idx) => /* @__PURE__ */ jsxs21(
3024
+ "a",
3025
+ {
3026
+ href: link.href,
3027
+ target: "_blank",
3028
+ rel: "noopener noreferrer",
3029
+ 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",
3030
+ onClick: (e) => e.stopPropagation(),
3031
+ children: [
3032
+ /* @__PURE__ */ jsx27("span", { children: link.label }),
3033
+ /* @__PURE__ */ jsx27(ExternalLink, { className: "size-2.5 shrink-0 text-muted-foreground/50" })
3034
+ ]
3035
+ },
3036
+ idx
3037
+ )) : null }),
3038
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", onClick: (e) => e.stopPropagation(), children: [
3039
+ actions ? actions : null,
3040
+ !actions && copyValue ? /* @__PURE__ */ jsx27(
3041
+ CopyButton,
3042
+ {
3043
+ value: copyValue,
3044
+ className: "h-7 text-xs px-2 hover:bg-muted/40 text-muted-foreground/80 hover:text-foreground",
3045
+ children: copyLabel
3046
+ }
3047
+ ) : null
3048
+ ] })
3049
+ ] }) : null;
2377
3050
  if (onClick) {
2378
- return /* @__PURE__ */ jsx24("button", { type: "button", onClick, "aria-label": ariaLabel, className: cn(resourceCardInteractiveClass, className), children: body });
3051
+ return /* @__PURE__ */ jsxs21(
3052
+ "button",
3053
+ {
3054
+ type: "button",
3055
+ onClick,
3056
+ "aria-label": ariaLabel,
3057
+ className: cn(catalogCardInteractiveClass, className),
3058
+ children: [
3059
+ mainContent,
3060
+ footerMarkup
3061
+ ]
3062
+ }
3063
+ );
2379
3064
  }
2380
- return /* @__PURE__ */ jsx24("article", { className: cn(resourceCardShellClass, className), children: body });
3065
+ return /* @__PURE__ */ jsxs21("article", { className: cn(catalogCardShellClass, className), children: [
3066
+ mainContent,
3067
+ footerMarkup
3068
+ ] });
2381
3069
  };
2382
3070
 
2383
3071
  // src/app/settings/SettingsSection.tsx
2384
- import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
3072
+ import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
2385
3073
  var SettingsSectionHeader = ({
2386
3074
  title,
2387
3075
  description,
2388
3076
  className
2389
- }) => /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col", className), children: [
2390
- /* @__PURE__ */ jsx25("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
2391
- description ? /* @__PURE__ */ jsx25("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
3077
+ }) => /* @__PURE__ */ jsxs22("div", { className: cn("flex flex-col", className), children: [
3078
+ /* @__PURE__ */ jsx28("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
3079
+ description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
2392
3080
  ] });
2393
3081
  var SettingsSection = ({
2394
3082
  title,
@@ -2397,7 +3085,7 @@ var SettingsSection = ({
2397
3085
  children,
2398
3086
  noBorderTop = false,
2399
3087
  className
2400
- }) => /* @__PURE__ */ jsxs20(
3088
+ }) => /* @__PURE__ */ jsxs22(
2401
3089
  "section",
2402
3090
  {
2403
3091
  className: cn(
@@ -2406,18 +3094,18 @@ var SettingsSection = ({
2406
3094
  className
2407
3095
  ),
2408
3096
  children: [
2409
- /* @__PURE__ */ jsxs20("div", { className: "min-w-0", children: [
2410
- /* @__PURE__ */ jsx25("h2", { className: "text-sm font-medium text-foreground", children: title }),
2411
- description ? /* @__PURE__ */ jsx25("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
2412
- descriptionFooter ? /* @__PURE__ */ jsx25("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
3097
+ /* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
3098
+ /* @__PURE__ */ jsx28("h2", { className: "text-sm font-medium text-foreground", children: title }),
3099
+ description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
3100
+ descriptionFooter ? /* @__PURE__ */ jsx28("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
2413
3101
  ] }),
2414
- /* @__PURE__ */ jsx25("div", { className: "min-w-0 space-y-3", children })
3102
+ /* @__PURE__ */ jsx28("div", { className: "min-w-0 space-y-3", children })
2415
3103
  ]
2416
3104
  }
2417
3105
  );
2418
3106
 
2419
3107
  // src/app/settings/FieldRow.tsx
2420
- import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
3108
+ import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
2421
3109
  var FieldRow = ({
2422
3110
  label,
2423
3111
  children,
@@ -2427,7 +3115,7 @@ var FieldRow = ({
2427
3115
  className
2428
3116
  }) => {
2429
3117
  if (inline) {
2430
- return /* @__PURE__ */ jsxs21(
3118
+ return /* @__PURE__ */ jsxs23(
2431
3119
  "div",
2432
3120
  {
2433
3121
  className: cn(
@@ -2435,8 +3123,8 @@ var FieldRow = ({
2435
3123
  className
2436
3124
  ),
2437
3125
  children: [
2438
- /* @__PURE__ */ jsxs21("div", { className: "min-w-0", children: [
2439
- /* @__PURE__ */ jsx26(
3126
+ /* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
3127
+ /* @__PURE__ */ jsx29(
2440
3128
  "label",
2441
3129
  {
2442
3130
  htmlFor,
@@ -2444,25 +3132,25 @@ var FieldRow = ({
2444
3132
  children: label
2445
3133
  }
2446
3134
  ),
2447
- description ? /* @__PURE__ */ jsx26("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
3135
+ description ? /* @__PURE__ */ jsx29("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
2448
3136
  ] }),
2449
- /* @__PURE__ */ jsx26("div", { className: "shrink-0", children })
3137
+ /* @__PURE__ */ jsx29("div", { className: "shrink-0", children })
2450
3138
  ]
2451
3139
  }
2452
3140
  );
2453
3141
  }
2454
- return /* @__PURE__ */ jsxs21("div", { className: cn("flex flex-col gap-1.5", className), children: [
2455
- /* @__PURE__ */ jsx26("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
3142
+ return /* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-1.5", className), children: [
3143
+ /* @__PURE__ */ jsx29("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
2456
3144
  children,
2457
- description ? /* @__PURE__ */ jsx26("p", { className: "text-xs text-muted-foreground", children: description }) : null
3145
+ description ? /* @__PURE__ */ jsx29("p", { className: "text-xs text-muted-foreground", children: description }) : null
2458
3146
  ] });
2459
3147
  };
2460
3148
 
2461
3149
  // src/app/settings/FloatingUnsavedChangesBar.tsx
2462
- import { useEffect, useState as useState3 } from "react";
3150
+ import { useEffect as useEffect2, useState as useState3 } from "react";
2463
3151
  import { createPortal } from "react-dom";
2464
3152
  import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
2465
- import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
3153
+ import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
2466
3154
  var FloatingUnsavedChangesBar = ({
2467
3155
  visible,
2468
3156
  message = "Unsaved changes",
@@ -2477,10 +3165,10 @@ var FloatingUnsavedChangesBar = ({
2477
3165
  }) => {
2478
3166
  const reduceMotion = useReducedMotion3();
2479
3167
  const [mounted, setMounted] = useState3(false);
2480
- useEffect(() => setMounted(true), []);
3168
+ useEffect2(() => setMounted(true), []);
2481
3169
  if (!mounted || typeof document === "undefined") return null;
2482
3170
  return createPortal(
2483
- /* @__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(
3171
+ /* @__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(
2484
3172
  motion3.div,
2485
3173
  {
2486
3174
  role: "region",
@@ -2494,10 +3182,10 @@ var FloatingUnsavedChangesBar = ({
2494
3182
  className
2495
3183
  ),
2496
3184
  children: [
2497
- /* @__PURE__ */ jsx27("span", { className: "text-sm text-muted-foreground", children: message }),
2498
- /* @__PURE__ */ jsxs22("span", { className: "flex items-center gap-1.5", children: [
2499
- /* @__PURE__ */ jsx27(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
2500
- /* @__PURE__ */ jsx27(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
3185
+ /* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: message }),
3186
+ /* @__PURE__ */ jsxs24("span", { className: "flex items-center gap-1.5", children: [
3187
+ /* @__PURE__ */ jsx30(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
3188
+ /* @__PURE__ */ jsx30(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
2501
3189
  ] })
2502
3190
  ]
2503
3191
  }
@@ -2507,13 +3195,13 @@ var FloatingUnsavedChangesBar = ({
2507
3195
  };
2508
3196
 
2509
3197
  // src/app/settings/DangerZone.tsx
2510
- import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
3198
+ import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
2511
3199
  var DangerZoneAction = ({
2512
3200
  title,
2513
3201
  description,
2514
3202
  action,
2515
3203
  className
2516
- }) => /* @__PURE__ */ jsxs23(
3204
+ }) => /* @__PURE__ */ jsxs25(
2517
3205
  "div",
2518
3206
  {
2519
3207
  className: cn(
@@ -2521,11 +3209,11 @@ var DangerZoneAction = ({
2521
3209
  className
2522
3210
  ),
2523
3211
  children: [
2524
- /* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
2525
- /* @__PURE__ */ jsx28("p", { className: "text-sm font-medium text-foreground", children: title }),
2526
- description ? /* @__PURE__ */ jsx28("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
3212
+ /* @__PURE__ */ jsxs25("div", { className: "min-w-0", children: [
3213
+ /* @__PURE__ */ jsx31("p", { className: "text-sm font-medium text-foreground", children: title }),
3214
+ description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2527
3215
  ] }),
2528
- /* @__PURE__ */ jsx28("div", { className: "shrink-0", children: action })
3216
+ /* @__PURE__ */ jsx31("div", { className: "shrink-0", children: action })
2529
3217
  ]
2530
3218
  }
2531
3219
  );
@@ -2534,7 +3222,7 @@ var DangerZone = ({
2534
3222
  description,
2535
3223
  children,
2536
3224
  className
2537
- }) => /* @__PURE__ */ jsxs23(
3225
+ }) => /* @__PURE__ */ jsxs25(
2538
3226
  "section",
2539
3227
  {
2540
3228
  className: cn(
@@ -2542,18 +3230,18 @@ var DangerZone = ({
2542
3230
  className
2543
3231
  ),
2544
3232
  children: [
2545
- (title || description) && /* @__PURE__ */ jsxs23("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
2546
- title ? /* @__PURE__ */ jsx28("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
2547
- description ? /* @__PURE__ */ jsx28("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
3233
+ (title || description) && /* @__PURE__ */ jsxs25("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
3234
+ title ? /* @__PURE__ */ jsx31("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
3235
+ description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
2548
3236
  ] }),
2549
- /* @__PURE__ */ jsx28("div", { className: "divide-y divide-border bg-card", children })
3237
+ /* @__PURE__ */ jsx31("div", { className: "divide-y divide-border bg-card", children })
2550
3238
  ]
2551
3239
  }
2552
3240
  );
2553
3241
 
2554
3242
  // src/app/integrations/IntegrationCard.tsx
2555
- import { useId as useId2 } from "react";
2556
- import { Fragment as Fragment4, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
3243
+ import { useId as useId3 } from "react";
3244
+ import { Fragment as Fragment6, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
2557
3245
  var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
2558
3246
  var statusLabel = {
2559
3247
  available: null,
@@ -2561,14 +3249,14 @@ var statusLabel = {
2561
3249
  disabled: "Disabled",
2562
3250
  locked: "Locked"
2563
3251
  };
2564
- var catalogCardShellClass = cn(
3252
+ var catalogCardShellClass2 = cn(
2565
3253
  "group relative box-border flex flex-col overflow-hidden rounded-2xl px-4 pb-4 pt-4 text-left font-normal",
2566
3254
  INTEGRATION_CATALOG_CARD_HEIGHT_CLASS,
2567
3255
  TIMBAL_V2_ELEVATED_SURFACE,
2568
3256
  "transition-opacity duration-200 ease-out"
2569
3257
  );
2570
- var catalogCardInteractiveClass = cn(
2571
- catalogCardShellClass,
3258
+ var catalogCardInteractiveClass2 = cn(
3259
+ catalogCardShellClass2,
2572
3260
  "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
2573
3261
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
2574
3262
  );
@@ -2591,15 +3279,15 @@ var IntegrationCard = ({
2591
3279
  ariaLabel,
2592
3280
  className
2593
3281
  }) => {
2594
- const titleId = useId2();
3282
+ const titleId = useId3();
2595
3283
  const locked = status === "locked";
2596
3284
  const dimmed = status === "disabled" || locked;
2597
- const body = /* @__PURE__ */ jsxs24("div", { className: "flex h-full min-h-0 flex-col", children: [
2598
- /* @__PURE__ */ jsxs24("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
2599
- logo ? /* @__PURE__ */ jsx29("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
2600
- /* @__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: [
2601
- /* @__PURE__ */ jsxs24("div", { className: "min-w-0", children: [
2602
- /* @__PURE__ */ jsx29(
3285
+ const body = /* @__PURE__ */ jsxs26("div", { className: "flex h-full min-h-0 flex-col", children: [
3286
+ /* @__PURE__ */ jsxs26("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
3287
+ logo ? /* @__PURE__ */ jsx32("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
3288
+ /* @__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: [
3289
+ /* @__PURE__ */ jsxs26("div", { className: "min-w-0", children: [
3290
+ /* @__PURE__ */ jsx32(
2603
3291
  "h4",
2604
3292
  {
2605
3293
  id: onClick && !action ? void 0 : titleId,
@@ -2607,12 +3295,12 @@ var IntegrationCard = ({
2607
3295
  children: name
2608
3296
  }
2609
3297
  ),
2610
- statusLabel[status] ? /* @__PURE__ */ jsx29("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
3298
+ statusLabel[status] ? /* @__PURE__ */ jsx32("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
2611
3299
  ] }),
2612
- badge ? /* @__PURE__ */ jsx29("span", { className: "shrink-0", children: badge }) : null
3300
+ badge ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: badge }) : null
2613
3301
  ] }) })
2614
3302
  ] }),
2615
- description ? /* @__PURE__ */ jsx29(
3303
+ description ? /* @__PURE__ */ jsx32(
2616
3304
  "p",
2617
3305
  {
2618
3306
  className: cn(
@@ -2622,19 +3310,19 @@ var IntegrationCard = ({
2622
3310
  children: description
2623
3311
  }
2624
3312
  ) : null,
2625
- action ? /* @__PURE__ */ jsxs24(Fragment4, { children: [
2626
- /* @__PURE__ */ jsx29("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
2627
- /* @__PURE__ */ jsx29("div", { className: "relative mt-3 shrink-0", children: action })
3313
+ action ? /* @__PURE__ */ jsxs26(Fragment6, { children: [
3314
+ /* @__PURE__ */ jsx32("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
3315
+ /* @__PURE__ */ jsx32("div", { className: "relative mt-3 shrink-0", children: action })
2628
3316
  ] }) : null
2629
3317
  ] });
2630
3318
  const shellClass3 = cn(
2631
- catalogCardShellClass,
3319
+ catalogCardShellClass2,
2632
3320
  dimmed && catalogCardMutedClass,
2633
3321
  locked && "cursor-default opacity-75",
2634
3322
  className
2635
3323
  );
2636
3324
  if (onClick && !action) {
2637
- return /* @__PURE__ */ jsx29(
3325
+ return /* @__PURE__ */ jsx32(
2638
3326
  "button",
2639
3327
  {
2640
3328
  type: "button",
@@ -2642,7 +3330,7 @@ var IntegrationCard = ({
2642
3330
  disabled: locked,
2643
3331
  "aria-label": ariaLabel,
2644
3332
  className: cn(
2645
- catalogCardInteractiveClass,
3333
+ catalogCardInteractiveClass2,
2646
3334
  dimmed && catalogCardMutedClass,
2647
3335
  locked && "cursor-default opacity-75",
2648
3336
  className
@@ -2651,12 +3339,12 @@ var IntegrationCard = ({
2651
3339
  }
2652
3340
  );
2653
3341
  }
2654
- return /* @__PURE__ */ jsx29("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
3342
+ return /* @__PURE__ */ jsx32("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
2655
3343
  };
2656
3344
 
2657
3345
  // src/app/integrations/IntegrationsEmptyState.tsx
2658
- import { useId as useId3 } from "react";
2659
- import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
3346
+ import { useId as useId4 } from "react";
3347
+ import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
2660
3348
  var IntegrationsEmptyState = ({
2661
3349
  title = "No integrations yet",
2662
3350
  description = "Connect a provider to start syncing data and powering your workforce.",
@@ -2664,8 +3352,8 @@ var IntegrationsEmptyState = ({
2664
3352
  action,
2665
3353
  className
2666
3354
  }) => {
2667
- const titleId = useId3();
2668
- return /* @__PURE__ */ jsxs25(
3355
+ const titleId = useId4();
3356
+ return /* @__PURE__ */ jsxs27(
2669
3357
  "section",
2670
3358
  {
2671
3359
  className: cn(
@@ -2675,7 +3363,7 @@ var IntegrationsEmptyState = ({
2675
3363
  ),
2676
3364
  "aria-labelledby": titleId,
2677
3365
  children: [
2678
- icon ? /* @__PURE__ */ jsx30(
3366
+ icon ? /* @__PURE__ */ jsx33(
2679
3367
  "span",
2680
3368
  {
2681
3369
  className: cn(
@@ -2687,21 +3375,21 @@ var IntegrationsEmptyState = ({
2687
3375
  children: icon
2688
3376
  }
2689
3377
  ) : null,
2690
- /* @__PURE__ */ jsx30("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
2691
- description ? /* @__PURE__ */ jsx30("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
2692
- action ? /* @__PURE__ */ jsx30("div", { className: "mt-1", children: action }) : null
3378
+ /* @__PURE__ */ jsx33("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
3379
+ description ? /* @__PURE__ */ jsx33("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
3380
+ action ? /* @__PURE__ */ jsx33("div", { className: "mt-1", children: action }) : null
2693
3381
  ]
2694
3382
  }
2695
3383
  );
2696
3384
  };
2697
3385
 
2698
3386
  // src/app/integrations/PlanBadge.tsx
2699
- import { jsx as jsx31 } from "react/jsx-runtime";
3387
+ import { jsx as jsx34 } from "react/jsx-runtime";
2700
3388
  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";
2701
- var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx31("span", { className: cn(planBadgeClass, className), children });
3389
+ var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx34("span", { className: cn(planBadgeClass, className), children });
2702
3390
 
2703
3391
  // src/app/integrations/ConnectionRow.tsx
2704
- import { Fragment as Fragment5, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
3392
+ import { Fragment as Fragment7, jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
2705
3393
  var logoShellClass2 = cn(
2706
3394
  "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
2707
3395
  TIMBAL_V2_LOGO_TILE
@@ -2716,14 +3404,14 @@ var ConnectionRow = ({
2716
3404
  ariaLabel,
2717
3405
  className
2718
3406
  }) => {
2719
- const inner = /* @__PURE__ */ jsxs26(Fragment5, { children: [
2720
- logo ? /* @__PURE__ */ jsx32("span", { className: logoShellClass2, children: logo }) : null,
2721
- /* @__PURE__ */ jsxs26("div", { className: "min-w-0 flex-1", children: [
2722
- /* @__PURE__ */ jsx32("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
2723
- meta ? /* @__PURE__ */ jsx32("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
3407
+ const inner = /* @__PURE__ */ jsxs28(Fragment7, { children: [
3408
+ logo ? /* @__PURE__ */ jsx35("span", { className: logoShellClass2, children: logo }) : null,
3409
+ /* @__PURE__ */ jsxs28("div", { className: "min-w-0 flex-1", children: [
3410
+ /* @__PURE__ */ jsx35("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
3411
+ meta ? /* @__PURE__ */ jsx35("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
2724
3412
  ] }),
2725
- badge ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: badge }) : null,
2726
- action ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: action }) : null
3413
+ badge ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: badge }) : null,
3414
+ action ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: action }) : null
2727
3415
  ] });
2728
3416
  const rowClass2 = cn(
2729
3417
  "flex w-full items-center gap-3 px-4 py-3 text-left",
@@ -2731,7 +3419,7 @@ var ConnectionRow = ({
2731
3419
  className
2732
3420
  );
2733
3421
  if (onClick) {
2734
- return /* @__PURE__ */ jsx32(
3422
+ return /* @__PURE__ */ jsx35(
2735
3423
  "button",
2736
3424
  {
2737
3425
  type: "button",
@@ -2743,7 +3431,7 @@ var ConnectionRow = ({
2743
3431
  }
2744
3432
  );
2745
3433
  }
2746
- return /* @__PURE__ */ jsx32("div", { role: "listitem", className: rowClass2, children: inner });
3434
+ return /* @__PURE__ */ jsx35("div", { role: "listitem", className: rowClass2, children: inner });
2747
3435
  };
2748
3436
  var connectionRowListClass = cn(
2749
3437
  "overflow-hidden rounded-2xl",
@@ -2751,12 +3439,12 @@ var connectionRowListClass = cn(
2751
3439
  );
2752
3440
 
2753
3441
  // src/app/integrations/ConnectionRowList.tsx
2754
- import { jsx as jsx33 } from "react/jsx-runtime";
3442
+ import { jsx as jsx36 } from "react/jsx-runtime";
2755
3443
  var ConnectionRowList = ({
2756
3444
  children,
2757
3445
  "aria-label": ariaLabel = "Connected integrations",
2758
3446
  className
2759
- }) => /* @__PURE__ */ jsx33(
3447
+ }) => /* @__PURE__ */ jsx36(
2760
3448
  "div",
2761
3449
  {
2762
3450
  role: "list",
@@ -2767,7 +3455,7 @@ var ConnectionRowList = ({
2767
3455
  );
2768
3456
 
2769
3457
  // src/app/navigation/SubNav.tsx
2770
- import { jsx as jsx34 } from "react/jsx-runtime";
3458
+ import { jsx as jsx37 } from "react/jsx-runtime";
2771
3459
  var SubNav = ({
2772
3460
  items,
2773
3461
  activeId,
@@ -2776,7 +3464,7 @@ var SubNav = ({
2776
3464
  "aria-label": ariaLabel = "Section navigation",
2777
3465
  layoutId
2778
3466
  }) => {
2779
- return /* @__PURE__ */ jsx34("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx34(
3467
+ return /* @__PURE__ */ jsx37("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx37(
2780
3468
  PillSegmentedTabs,
2781
3469
  {
2782
3470
  value: activeId,
@@ -2790,13 +3478,13 @@ var SubNav = ({
2790
3478
  };
2791
3479
 
2792
3480
  // src/app/navigation/Breadcrumbs.tsx
2793
- import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
3481
+ import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
2794
3482
  var Breadcrumbs = ({ items, className }) => {
2795
- 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) => {
3483
+ 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) => {
2796
3484
  const isLast = index === items.length - 1;
2797
- return /* @__PURE__ */ jsxs27("li", { className: "inline-flex items-center gap-1.5", children: [
2798
- index > 0 ? /* @__PURE__ */ jsx35("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
2799
- 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(
3485
+ return /* @__PURE__ */ jsxs29("li", { className: "inline-flex items-center gap-1.5", children: [
3486
+ index > 0 ? /* @__PURE__ */ jsx38("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
3487
+ 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(
2800
3488
  "button",
2801
3489
  {
2802
3490
  type: "button",
@@ -2810,7 +3498,8 @@ var Breadcrumbs = ({ items, className }) => {
2810
3498
  };
2811
3499
 
2812
3500
  // src/app/forms/Field.tsx
2813
- import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
3501
+ import { useId as useId5 } from "react";
3502
+ import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
2814
3503
  var Field = ({
2815
3504
  label,
2816
3505
  hint,
@@ -2819,11 +3508,11 @@ var Field = ({
2819
3508
  className,
2820
3509
  htmlFor
2821
3510
  }) => {
2822
- return /* @__PURE__ */ jsxs28("div", { className: cn("aui-app-field", appFieldClass, className), children: [
2823
- /* @__PURE__ */ jsx36("label", { className: appFieldLabelClass, htmlFor, children: label }),
3511
+ return /* @__PURE__ */ jsxs30("div", { className: cn("aui-app-field", appFieldClass, className), children: [
3512
+ /* @__PURE__ */ jsx39("label", { className: appFieldLabelClass, htmlFor, children: label }),
2824
3513
  children,
2825
- hint && !error ? /* @__PURE__ */ jsx36("p", { className: appFieldHintClass, children: hint }) : null,
2826
- error ? /* @__PURE__ */ jsx36("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
3514
+ hint && !error ? /* @__PURE__ */ jsx39("p", { className: appFieldHintClass, children: hint }) : null,
3515
+ error ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
2827
3516
  ] });
2828
3517
  };
2829
3518
  var FieldInput = ({
@@ -2835,8 +3524,9 @@ var FieldInput = ({
2835
3524
  id,
2836
3525
  ...inputProps
2837
3526
  }) => {
2838
- const inputId = id ?? inputProps.name;
2839
- return /* @__PURE__ */ jsx36(
3527
+ const autoId = useId5();
3528
+ const inputId = id ?? inputProps.name ?? autoId;
3529
+ return /* @__PURE__ */ jsx39(
2840
3530
  Field,
2841
3531
  {
2842
3532
  label,
@@ -2844,7 +3534,7 @@ var FieldInput = ({
2844
3534
  error,
2845
3535
  htmlFor: inputId,
2846
3536
  className: fieldClassName,
2847
- children: /* @__PURE__ */ jsx36(
3537
+ children: /* @__PURE__ */ jsx39(
2848
3538
  "input",
2849
3539
  {
2850
3540
  id: inputId,
@@ -2858,7 +3548,8 @@ var FieldInput = ({
2858
3548
  };
2859
3549
 
2860
3550
  // src/app/forms/FieldTextarea.tsx
2861
- import { jsx as jsx37 } from "react/jsx-runtime";
3551
+ import { useId as useId6 } from "react";
3552
+ import { jsx as jsx40 } from "react/jsx-runtime";
2862
3553
  var textareaClass = cn(
2863
3554
  appInputClass,
2864
3555
  "min-h-[5.5rem] resize-y py-2.5 leading-relaxed"
@@ -2872,8 +3563,9 @@ var FieldTextarea = ({
2872
3563
  id,
2873
3564
  ...props
2874
3565
  }) => {
2875
- const textareaId = id ?? props.name;
2876
- return /* @__PURE__ */ jsx37(
3566
+ const autoId = useId6();
3567
+ const textareaId = id ?? props.name ?? autoId;
3568
+ return /* @__PURE__ */ jsx40(
2877
3569
  Field,
2878
3570
  {
2879
3571
  label,
@@ -2881,7 +3573,7 @@ var FieldTextarea = ({
2881
3573
  error,
2882
3574
  htmlFor: textareaId,
2883
3575
  className: fieldClassName,
2884
- children: /* @__PURE__ */ jsx37(
3576
+ children: /* @__PURE__ */ jsx40(
2885
3577
  "textarea",
2886
3578
  {
2887
3579
  id: textareaId,
@@ -2895,8 +3587,9 @@ var FieldTextarea = ({
2895
3587
  };
2896
3588
 
2897
3589
  // src/app/forms/FieldSelect.tsx
3590
+ import { useId as useId7 } from "react";
2898
3591
  import { ChevronDownIcon } from "lucide-react";
2899
- import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
3592
+ import { jsx as jsx41, jsxs as jsxs31 } from "react/jsx-runtime";
2900
3593
  var selectWrapClass = "relative";
2901
3594
  var selectClass = cn(
2902
3595
  appInputClass,
@@ -2912,8 +3605,9 @@ var FieldSelect = ({
2912
3605
  id,
2913
3606
  ...props
2914
3607
  }) => {
2915
- const selectId = id ?? props.name;
2916
- return /* @__PURE__ */ jsx38(
3608
+ const autoId = useId7();
3609
+ const selectId = id ?? props.name ?? autoId;
3610
+ return /* @__PURE__ */ jsx41(
2917
3611
  Field,
2918
3612
  {
2919
3613
  label,
@@ -2921,8 +3615,8 @@ var FieldSelect = ({
2921
3615
  error,
2922
3616
  htmlFor: selectId,
2923
3617
  className: fieldClassName,
2924
- children: /* @__PURE__ */ jsxs29("div", { className: selectWrapClass, children: [
2925
- /* @__PURE__ */ jsx38(
3618
+ children: /* @__PURE__ */ jsxs31("div", { className: selectWrapClass, children: [
3619
+ /* @__PURE__ */ jsx41(
2926
3620
  "select",
2927
3621
  {
2928
3622
  id: selectId,
@@ -2932,7 +3626,7 @@ var FieldSelect = ({
2932
3626
  children
2933
3627
  }
2934
3628
  ),
2935
- /* @__PURE__ */ jsx38(
3629
+ /* @__PURE__ */ jsx41(
2936
3630
  ChevronDownIcon,
2937
3631
  {
2938
3632
  className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground",
@@ -2945,7 +3639,8 @@ var FieldSelect = ({
2945
3639
  };
2946
3640
 
2947
3641
  // src/app/forms/FieldSwitch.tsx
2948
- import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
3642
+ import { useId as useId8 } from "react";
3643
+ import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
2949
3644
  var trackClass = cn(
2950
3645
  "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
2951
3646
  "peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
@@ -2965,8 +3660,9 @@ var FieldSwitch = ({
2965
3660
  id,
2966
3661
  ...props
2967
3662
  }) => {
2968
- const inputId = id ?? props.name ?? "switch";
2969
- return /* @__PURE__ */ jsxs30(
3663
+ const autoId = useId8();
3664
+ const inputId = id ?? props.name ?? autoId;
3665
+ return /* @__PURE__ */ jsxs32(
2970
3666
  "label",
2971
3667
  {
2972
3668
  className: cn(
@@ -2975,8 +3671,8 @@ var FieldSwitch = ({
2975
3671
  ),
2976
3672
  htmlFor: inputId,
2977
3673
  children: [
2978
- /* @__PURE__ */ jsxs30("span", { className: "relative mt-0.5", children: [
2979
- /* @__PURE__ */ jsx39(
3674
+ /* @__PURE__ */ jsxs32("span", { className: "relative mt-0.5", children: [
3675
+ /* @__PURE__ */ jsx42(
2980
3676
  "input",
2981
3677
  {
2982
3678
  id: inputId,
@@ -2986,11 +3682,11 @@ var FieldSwitch = ({
2986
3682
  ...props
2987
3683
  }
2988
3684
  ),
2989
- /* @__PURE__ */ jsx39("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx39("span", { className: thumbClass }) })
3685
+ /* @__PURE__ */ jsx42("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx42("span", { className: thumbClass }) })
2990
3686
  ] }),
2991
- /* @__PURE__ */ jsxs30("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
2992
- /* @__PURE__ */ jsx39("span", { className: "text-sm font-medium text-foreground", children: label }),
2993
- description ? /* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: description }) : null
3687
+ /* @__PURE__ */ jsxs32("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3688
+ /* @__PURE__ */ jsx42("span", { className: "text-sm font-medium text-foreground", children: label }),
3689
+ description ? /* @__PURE__ */ jsx42("span", { className: "text-xs text-muted-foreground", children: description }) : null
2994
3690
  ] })
2995
3691
  ]
2996
3692
  }
@@ -2999,13 +3695,13 @@ var FieldSwitch = ({
2999
3695
 
3000
3696
  // src/app/forms/SearchInput.tsx
3001
3697
  import { SearchIcon } from "lucide-react";
3002
- import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
3698
+ import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
3003
3699
  var SearchInput = ({
3004
3700
  className,
3005
3701
  placeholder = "Search\u2026",
3006
3702
  ...props
3007
3703
  }) => {
3008
- return /* @__PURE__ */ jsxs31(
3704
+ return /* @__PURE__ */ jsxs33(
3009
3705
  "label",
3010
3706
  {
3011
3707
  className: cn(
@@ -3014,8 +3710,8 @@ var SearchInput = ({
3014
3710
  className
3015
3711
  ),
3016
3712
  children: [
3017
- /* @__PURE__ */ jsx40(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
3018
- /* @__PURE__ */ jsx40(
3713
+ /* @__PURE__ */ jsx43(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
3714
+ /* @__PURE__ */ jsx43(
3019
3715
  "input",
3020
3716
  {
3021
3717
  type: "search",
@@ -3030,26 +3726,26 @@ var SearchInput = ({
3030
3726
  };
3031
3727
 
3032
3728
  // src/app/forms/FormSection.tsx
3033
- import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
3729
+ import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
3034
3730
  var FormSection = ({ title, children, className }) => {
3035
3731
  const density = useAppDensity();
3036
3732
  const sectionClass = useAppDensityClass("section");
3037
- return /* @__PURE__ */ jsxs32(
3733
+ return /* @__PURE__ */ jsxs34(
3038
3734
  "fieldset",
3039
3735
  {
3040
3736
  className: cn("aui-app-form-section", sectionClass, "border-0 p-0", className),
3041
3737
  children: [
3042
- title ? /* @__PURE__ */ jsx41("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
3043
- /* @__PURE__ */ jsx41("div", { className: cn("flex flex-col", density === "compact" ? "gap-2" : "gap-4"), children })
3738
+ title ? /* @__PURE__ */ jsx44("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
3739
+ /* @__PURE__ */ jsx44("div", { className: cn("flex flex-col", density === "compact" ? "gap-2" : "gap-4"), children })
3044
3740
  ]
3045
3741
  }
3046
3742
  );
3047
3743
  };
3048
3744
 
3049
3745
  // src/app/data/FilterBar.tsx
3050
- import { jsx as jsx42 } from "react/jsx-runtime";
3746
+ import { jsx as jsx45 } from "react/jsx-runtime";
3051
3747
  var FilterBar = ({ children, className }) => {
3052
- return /* @__PURE__ */ jsx42(
3748
+ return /* @__PURE__ */ jsx45(
3053
3749
  "div",
3054
3750
  {
3055
3751
  className: cn("aui-app-filter-bar", appFilterBarClass, className),
@@ -3061,41 +3757,539 @@ var FilterBar = ({ children, className }) => {
3061
3757
  };
3062
3758
 
3063
3759
  // src/app/data/FilterField.tsx
3064
- import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
3760
+ import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
3065
3761
  var FilterField = ({
3066
3762
  label,
3067
3763
  children,
3068
3764
  className
3069
3765
  }) => {
3070
- return /* @__PURE__ */ jsxs33("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
3071
- label ? /* @__PURE__ */ jsx43("span", { className: appFieldLabelClass, children: label }) : null,
3766
+ return /* @__PURE__ */ jsxs35("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
3767
+ label ? /* @__PURE__ */ jsx46("span", { className: appFieldLabelClass, children: label }) : null,
3072
3768
  children
3073
3769
  ] });
3074
3770
  };
3075
3771
 
3772
+ // src/app/data/FilterDropdown.tsx
3773
+ import { useState as useState4, useMemo as useMemo2, useEffect as useEffect3 } from "react";
3774
+ import {
3775
+ CalendarIcon,
3776
+ ChevronDownIcon as ChevronDownIcon2,
3777
+ ChevronRightIcon,
3778
+ CircleDollarSignIcon,
3779
+ ListFilterIcon,
3780
+ SearchIcon as SearchIcon2,
3781
+ TrendingUpIcon,
3782
+ UserIcon,
3783
+ WalletIcon
3784
+ } from "lucide-react";
3785
+ import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
3786
+ var DEFAULT_CONTACTS = [
3787
+ { id: "1", name: "Pedro Olivares Sanchez", email: "polivares@timbal.ai", initials: "PE" },
3788
+ { id: "2", name: "John Doe", email: "john@example.com", initials: "JD" },
3789
+ { id: "3", name: "Sarah Smith", email: "sarah@example.com", initials: "SS" }
3790
+ ];
3791
+ var OPERATORS = [
3792
+ { id: "greater_than", label: "Greater than..." },
3793
+ { id: "less_than", label: "Less than..." },
3794
+ { id: "equals", label: "Equals..." }
3795
+ ];
3796
+ function FilterDropdown({
3797
+ filters,
3798
+ onFiltersChange,
3799
+ initialFilters,
3800
+ contacts = DEFAULT_CONTACTS,
3801
+ className
3802
+ }) {
3803
+ const [isOpen, setIsOpen] = useState4(false);
3804
+ const [activeMenu, setActiveMenu] = useState4("contact");
3805
+ const [isMobile, setIsMobile] = useState4(false);
3806
+ useEffect3(() => {
3807
+ const checkMobile = () => setIsMobile(window.innerWidth < 768);
3808
+ checkMobile();
3809
+ window.addEventListener("resize", checkMobile);
3810
+ return () => window.removeEventListener("resize", checkMobile);
3811
+ }, []);
3812
+ const [selectedContacts, setSelectedContacts] = useState4(
3813
+ filters?.contacts ?? initialFilters?.contacts ?? []
3814
+ );
3815
+ const [walletInput, setWalletInput] = useState4(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
3816
+ const [appliedWallet, setAppliedWallet] = useState4(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
3817
+ const [selectedDatePreset, setSelectedDatePreset] = useState4(
3818
+ filters?.lastInvoiceDate ?? initialFilters?.lastInvoiceDate ?? null
3819
+ );
3820
+ const [customDateFrom, setCustomDateFrom] = useState4(
3821
+ filters?.customDateRange?.from ?? initialFilters?.customDateRange?.from ?? ""
3822
+ );
3823
+ const [customDateTo, setCustomDateTo] = useState4(
3824
+ filters?.customDateRange?.to ?? initialFilters?.customDateRange?.to ?? ""
3825
+ );
3826
+ const [ltvOperator, setLtvOperator] = useState4(
3827
+ filters?.lifetimeValue?.operator ?? initialFilters?.lifetimeValue?.operator ?? "greater_than"
3828
+ );
3829
+ const [ltvValue, setLtvValue] = useState4(filters?.lifetimeValue?.value ?? initialFilters?.lifetimeValue?.value ?? "");
3830
+ const [isLtvOperatorOpen, setLtvOperatorOpen] = useState4(false);
3831
+ const [outstandingOperator, setOutstandingOperator] = useState4(
3832
+ filters?.outstanding?.operator ?? initialFilters?.outstanding?.operator ?? "greater_than"
3833
+ );
3834
+ const [outstandingValue, setOutstandingValue] = useState4(filters?.outstanding?.value ?? initialFilters?.outstanding?.value ?? "");
3835
+ const [isOutstandingOperatorOpen, setOutstandingOperatorOpen] = useState4(false);
3836
+ useEffect3(() => {
3837
+ if (filters) {
3838
+ setSelectedContacts(filters.contacts ?? []);
3839
+ setWalletInput(filters.walletAddress ?? "");
3840
+ setAppliedWallet(filters.walletAddress ?? "");
3841
+ setSelectedDatePreset(filters.lastInvoiceDate ?? null);
3842
+ setCustomDateFrom(filters.customDateRange?.from ?? "");
3843
+ setCustomDateTo(filters.customDateRange?.to ?? "");
3844
+ setLtvOperator(filters.lifetimeValue?.operator ?? "greater_than");
3845
+ setLtvValue(filters.lifetimeValue?.value ?? "");
3846
+ setOutstandingOperator(filters.outstanding?.operator ?? "greater_than");
3847
+ setOutstandingValue(filters.outstanding?.value ?? "");
3848
+ }
3849
+ }, [filters]);
3850
+ const [contactSearch, setContactSearch] = useState4("");
3851
+ const [walletSearch, setWalletSearch] = useState4("");
3852
+ const refDate = useMemo2(() => new Date(2026, 5, 26), []);
3853
+ const presets = useMemo2(() => {
3854
+ const year = refDate.getFullYear();
3855
+ const month = refDate.getMonth();
3856
+ const lastMonthDate = new Date(year, month - 1, 1);
3857
+ const lastMonthLabel = lastMonthDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
3858
+ const thisMonthLabel = refDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
3859
+ const thisQuarter = Math.floor(month / 3) + 1;
3860
+ const thisQuarterLabel = `Q${thisQuarter} ${year}`;
3861
+ const lastQuarter = thisQuarter === 1 ? 4 : thisQuarter - 1;
3862
+ const lastQuarterYear = thisQuarter === 1 ? year - 1 : year;
3863
+ const lastQuarterLabel = `Q${lastQuarter} ${lastQuarterYear}`;
3864
+ const thisYearLabel = `${year}`;
3865
+ const last30 = new Date(refDate);
3866
+ last30.setDate(refDate.getDate() - 30);
3867
+ const formatDate = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
3868
+ const last30Label = `${formatDate(last30)} - ${formatDate(refDate)}`;
3869
+ const last90 = new Date(refDate);
3870
+ last90.setDate(refDate.getDate() - 90);
3871
+ const last90Label = `${formatDate(last90)} - ${formatDate(refDate)}`;
3872
+ return [
3873
+ { id: "last_month", label: "Last month", date: lastMonthLabel },
3874
+ { id: "this_month", label: "This month", date: thisMonthLabel },
3875
+ { id: "this_quarter", label: "This quarter", date: thisQuarterLabel },
3876
+ { id: "last_quarter", label: "Last quarter", date: lastQuarterLabel },
3877
+ { id: "this_year", label: "This year", date: thisYearLabel },
3878
+ { id: "last_30_days", label: "Last 30 days", date: last30Label },
3879
+ { id: "last_90_days", label: "Last 90 days", date: last90Label },
3880
+ { id: "custom", label: "Custom range", date: "" }
3881
+ ];
3882
+ }, [refDate]);
3883
+ const filteredContacts = useMemo2(() => {
3884
+ if (!contactSearch) return contacts;
3885
+ const query = contactSearch.toLowerCase();
3886
+ return contacts.filter(
3887
+ (c) => c.name.toLowerCase().includes(query) || c.email.toLowerCase().includes(query)
3888
+ );
3889
+ }, [contacts, contactSearch]);
3890
+ const handleContactToggle = (contactName) => {
3891
+ const next = selectedContacts.includes(contactName) ? selectedContacts.filter((name) => name !== contactName) : [...selectedContacts, contactName];
3892
+ setSelectedContacts(next);
3893
+ notifyChanges({ contacts: next });
3894
+ setIsOpen(false);
3895
+ };
3896
+ const handleWalletApply = () => {
3897
+ setAppliedWallet(walletInput);
3898
+ notifyChanges({ walletAddress: walletInput });
3899
+ setIsOpen(false);
3900
+ };
3901
+ const handleWalletClear = () => {
3902
+ setWalletInput("");
3903
+ setAppliedWallet("");
3904
+ notifyChanges({ walletAddress: "" });
3905
+ setIsOpen(false);
3906
+ };
3907
+ const handleDatePresetSelect = (presetId) => {
3908
+ setSelectedDatePreset(presetId);
3909
+ if (presetId !== "custom") {
3910
+ notifyChanges({ lastInvoiceDate: presetId, customDateRange: void 0 });
3911
+ setIsOpen(false);
3912
+ }
3913
+ };
3914
+ const handleCustomDateApply = () => {
3915
+ notifyChanges({
3916
+ lastInvoiceDate: "custom",
3917
+ customDateRange: { from: customDateFrom, to: customDateTo }
3918
+ });
3919
+ setIsOpen(false);
3920
+ };
3921
+ const handleLtvApply = () => {
3922
+ notifyChanges({
3923
+ lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null
3924
+ });
3925
+ setIsOpen(false);
3926
+ };
3927
+ const handleLtvClear = () => {
3928
+ setLtvValue("");
3929
+ notifyChanges({ lifetimeValue: null });
3930
+ setIsOpen(false);
3931
+ };
3932
+ const handleOutstandingApply = () => {
3933
+ notifyChanges({
3934
+ outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null
3935
+ });
3936
+ setIsOpen(false);
3937
+ };
3938
+ const handleOutstandingClear = () => {
3939
+ setOutstandingValue("");
3940
+ notifyChanges({ outstanding: null });
3941
+ setIsOpen(false);
3942
+ };
3943
+ const notifyChanges = (overrides) => {
3944
+ const current = {
3945
+ contacts: selectedContacts,
3946
+ walletAddress: appliedWallet,
3947
+ lastInvoiceDate: selectedDatePreset,
3948
+ customDateRange: customDateFrom || customDateTo ? { from: customDateFrom, to: customDateTo } : void 0,
3949
+ lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null,
3950
+ outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null,
3951
+ ...overrides
3952
+ };
3953
+ onFiltersChange?.(current);
3954
+ };
3955
+ const menuItems = [
3956
+ { id: "contact", label: "Contact", icon: /* @__PURE__ */ jsx47(UserIcon, { className: "size-4" }) },
3957
+ { id: "wallet", label: "Wallet address", icon: /* @__PURE__ */ jsx47(WalletIcon, { className: "size-4" }) },
3958
+ { id: "date", label: "Last invoice date", icon: /* @__PURE__ */ jsx47(CalendarIcon, { className: "size-4" }) },
3959
+ { id: "ltv", label: "Lifetime value", icon: /* @__PURE__ */ jsx47(TrendingUpIcon, { className: "size-4" }) },
3960
+ { id: "outstanding", label: "Outstanding", icon: /* @__PURE__ */ jsx47(CircleDollarSignIcon, { className: "size-4" }) }
3961
+ ];
3962
+ const activeIdx = menuItems.findIndex((item) => item.id === activeMenu);
3963
+ return /* @__PURE__ */ jsx47("div", { className: cn("inline-block", className), children: /* @__PURE__ */ jsxs36(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
3964
+ /* @__PURE__ */ jsx47(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx47(
3965
+ Button,
3966
+ {
3967
+ variant: "outline",
3968
+ size: "sm",
3969
+ shape: "pill",
3970
+ className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
3971
+ iconLeading: /* @__PURE__ */ jsx47(ListFilterIcon, { className: "size-4" }),
3972
+ children: "Filter"
3973
+ }
3974
+ ) }),
3975
+ /* @__PURE__ */ jsx47(
3976
+ PopoverContent,
3977
+ {
3978
+ variant: "list",
3979
+ align: "start",
3980
+ className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
3981
+ 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: [
3982
+ /* @__PURE__ */ jsx47("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: menuItems.map((item) => {
3983
+ const isActive = activeMenu === item.id;
3984
+ return /* @__PURE__ */ jsxs36(
3985
+ "button",
3986
+ {
3987
+ type: "button",
3988
+ className: cn(
3989
+ "flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
3990
+ isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
3991
+ ),
3992
+ onMouseEnter: () => !isMobile && setActiveMenu(item.id),
3993
+ onClick: () => setActiveMenu(item.id),
3994
+ children: [
3995
+ /* @__PURE__ */ jsxs36("span", { className: "flex items-center gap-2", children: [
3996
+ item.icon,
3997
+ /* @__PURE__ */ jsx47("span", { children: item.label })
3998
+ ] }),
3999
+ /* @__PURE__ */ jsx47(ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
4000
+ ]
4001
+ },
4002
+ item.id
4003
+ );
4004
+ }) }),
4005
+ activeMenu && /* @__PURE__ */ jsxs36(
4006
+ "div",
4007
+ {
4008
+ 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",
4009
+ style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
4010
+ children: [
4011
+ activeMenu === "contact" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4012
+ /* @__PURE__ */ jsxs36("div", { className: "relative flex items-center", children: [
4013
+ /* @__PURE__ */ jsx47(SearchIcon2, { className: "absolute left-2.5 size-4 text-muted-foreground/60" }),
4014
+ /* @__PURE__ */ jsx47(
4015
+ "input",
4016
+ {
4017
+ type: "text",
4018
+ placeholder: "Search by name or email...",
4019
+ value: contactSearch,
4020
+ onChange: (e) => setContactSearch(e.target.value),
4021
+ className: "w-full rounded-lg border border-border bg-background py-1.5 pl-8 pr-3 text-sm outline-none placeholder:text-muted-foreground/60 focus:border-border"
4022
+ }
4023
+ )
4024
+ ] }),
4025
+ /* @__PURE__ */ jsx47("div", { className: "max-h-48 overflow-y-auto flex flex-col gap-1 pr-1", children: filteredContacts.length === 0 ? /* @__PURE__ */ jsx47("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No contacts found" }) : filteredContacts.map((contact) => {
4026
+ const isChecked = selectedContacts.includes(contact.name);
4027
+ return /* @__PURE__ */ jsxs36(
4028
+ "label",
4029
+ {
4030
+ className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 hover:bg-muted/50 transition-colors",
4031
+ children: [
4032
+ /* @__PURE__ */ jsx47(
4033
+ Checkbox,
4034
+ {
4035
+ checked: isChecked,
4036
+ onCheckedChange: () => handleContactToggle(contact.name)
4037
+ }
4038
+ ),
4039
+ /* @__PURE__ */ jsx47(Avatar, { variant: "secondary", children: /* @__PURE__ */ jsx47(AvatarFallback, { children: contact.initials }) }),
4040
+ /* @__PURE__ */ jsx47("span", { className: "text-sm font-medium text-foreground", children: contact.name })
4041
+ ]
4042
+ },
4043
+ contact.id
4044
+ );
4045
+ }) })
4046
+ ] }),
4047
+ activeMenu === "wallet" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4048
+ /* @__PURE__ */ jsx47("div", { className: "relative flex items-center", children: /* @__PURE__ */ jsx47(
4049
+ "input",
4050
+ {
4051
+ type: "text",
4052
+ placeholder: "Search by wallet...",
4053
+ value: walletInput,
4054
+ onChange: (e) => setWalletInput(e.target.value),
4055
+ className: "w-full rounded-lg border border-border bg-background px-3 py-1.5 text-sm outline-none placeholder:text-muted-foreground/60"
4056
+ }
4057
+ ) }),
4058
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
4059
+ /* @__PURE__ */ jsx47(
4060
+ Button,
4061
+ {
4062
+ variant: "ghost",
4063
+ size: "sm",
4064
+ onClick: handleWalletClear,
4065
+ className: "text-muted-foreground hover:text-foreground h-8 px-3",
4066
+ children: "Clear"
4067
+ }
4068
+ ),
4069
+ /* @__PURE__ */ jsx47(
4070
+ Button,
4071
+ {
4072
+ color: "primary",
4073
+ size: "sm",
4074
+ onClick: handleWalletApply,
4075
+ className: "h-8 px-3",
4076
+ children: "Apply"
4077
+ }
4078
+ )
4079
+ ] })
4080
+ ] }),
4081
+ activeMenu === "date" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-1", children: [
4082
+ presets.map((preset) => {
4083
+ const isSelected = selectedDatePreset === preset.id;
4084
+ return /* @__PURE__ */ jsxs36(
4085
+ "button",
4086
+ {
4087
+ type: "button",
4088
+ onClick: () => handleDatePresetSelect(preset.id),
4089
+ className: cn(
4090
+ "flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-sm text-left transition-colors outline-none",
4091
+ isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
4092
+ ),
4093
+ children: [
4094
+ /* @__PURE__ */ jsx47("span", { children: preset.label }),
4095
+ preset.date && /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground/70", children: preset.date })
4096
+ ]
4097
+ },
4098
+ preset.id
4099
+ );
4100
+ }),
4101
+ selectedDatePreset === "custom" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2 mt-2 pt-2 border-t border-border/40", children: [
4102
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
4103
+ /* @__PURE__ */ jsx47(
4104
+ "input",
4105
+ {
4106
+ type: "date",
4107
+ value: customDateFrom,
4108
+ onChange: (e) => setCustomDateFrom(e.target.value),
4109
+ className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
4110
+ }
4111
+ ),
4112
+ /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: "to" }),
4113
+ /* @__PURE__ */ jsx47(
4114
+ "input",
4115
+ {
4116
+ type: "date",
4117
+ value: customDateTo,
4118
+ onChange: (e) => setCustomDateTo(e.target.value),
4119
+ className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
4120
+ }
4121
+ )
4122
+ ] }),
4123
+ /* @__PURE__ */ jsx47("div", { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsx47(
4124
+ Button,
4125
+ {
4126
+ color: "primary",
4127
+ size: "sm",
4128
+ onClick: handleCustomDateApply,
4129
+ className: "h-7 text-xs px-2.5",
4130
+ children: "Apply"
4131
+ }
4132
+ ) })
4133
+ ] })
4134
+ ] }),
4135
+ activeMenu === "ltv" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4136
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
4137
+ /* @__PURE__ */ jsxs36("div", { className: "relative shrink-0", children: [
4138
+ /* @__PURE__ */ jsxs36(
4139
+ "button",
4140
+ {
4141
+ type: "button",
4142
+ onClick: () => setLtvOperatorOpen(!isLtvOperatorOpen),
4143
+ className: "flex h-9 items-center gap-1 rounded-lg border border-border bg-background px-2.5 text-xs font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground outline-none whitespace-nowrap",
4144
+ children: [
4145
+ /* @__PURE__ */ jsx47("span", { children: OPERATORS.find((op) => op.id === ltvOperator)?.label.replace("...", "") }),
4146
+ /* @__PURE__ */ jsx47(ChevronDownIcon2, { className: "size-3" })
4147
+ ]
4148
+ }
4149
+ ),
4150
+ isLtvOperatorOpen && /* @__PURE__ */ jsx47("div", { className: "absolute left-0 top-full z-50 mt-1 w-32 rounded-lg border border-border bg-popover p-1 shadow-md", children: OPERATORS.map((op) => /* @__PURE__ */ jsx47(
4151
+ "button",
4152
+ {
4153
+ type: "button",
4154
+ onClick: () => {
4155
+ setLtvOperator(op.id);
4156
+ setLtvOperatorOpen(false);
4157
+ },
4158
+ className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
4159
+ children: op.label
4160
+ },
4161
+ op.id
4162
+ )) })
4163
+ ] }),
4164
+ /* @__PURE__ */ jsx47(
4165
+ "input",
4166
+ {
4167
+ type: "text",
4168
+ placeholder: "0.00",
4169
+ value: ltvValue,
4170
+ onChange: (e) => setLtvValue(e.target.value),
4171
+ className: "h-9 flex-1 min-w-0 rounded-lg border border-border bg-background px-3 py-1 text-sm outline-none placeholder:text-muted-foreground/60"
4172
+ }
4173
+ )
4174
+ ] }),
4175
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
4176
+ /* @__PURE__ */ jsx47(
4177
+ Button,
4178
+ {
4179
+ variant: "ghost",
4180
+ size: "sm",
4181
+ onClick: handleLtvClear,
4182
+ className: "text-muted-foreground hover:text-foreground h-8 px-3",
4183
+ children: "Clear"
4184
+ }
4185
+ ),
4186
+ /* @__PURE__ */ jsx47(
4187
+ Button,
4188
+ {
4189
+ color: "primary",
4190
+ size: "sm",
4191
+ onClick: handleLtvApply,
4192
+ className: "h-8 px-3",
4193
+ children: "Apply"
4194
+ }
4195
+ )
4196
+ ] })
4197
+ ] }),
4198
+ activeMenu === "outstanding" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
4199
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
4200
+ /* @__PURE__ */ jsxs36("div", { className: "relative shrink-0", children: [
4201
+ /* @__PURE__ */ jsxs36(
4202
+ "button",
4203
+ {
4204
+ type: "button",
4205
+ onClick: () => setOutstandingOperatorOpen(!isOutstandingOperatorOpen),
4206
+ className: "flex h-9 items-center gap-1 rounded-lg border border-border bg-background px-2.5 text-xs font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground outline-none whitespace-nowrap",
4207
+ children: [
4208
+ /* @__PURE__ */ jsx47("span", { children: OPERATORS.find((op) => op.id === outstandingOperator)?.label.replace("...", "") }),
4209
+ /* @__PURE__ */ jsx47(ChevronDownIcon2, { className: "size-3" })
4210
+ ]
4211
+ }
4212
+ ),
4213
+ isOutstandingOperatorOpen && /* @__PURE__ */ jsx47("div", { className: "absolute left-0 top-full z-50 mt-1 w-32 rounded-lg border border-border bg-popover p-1 shadow-md", children: OPERATORS.map((op) => /* @__PURE__ */ jsx47(
4214
+ "button",
4215
+ {
4216
+ type: "button",
4217
+ onClick: () => {
4218
+ setOutstandingOperator(op.id);
4219
+ setOutstandingOperatorOpen(false);
4220
+ },
4221
+ className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
4222
+ children: op.label
4223
+ },
4224
+ op.id
4225
+ )) })
4226
+ ] }),
4227
+ /* @__PURE__ */ jsx47(
4228
+ "input",
4229
+ {
4230
+ type: "text",
4231
+ placeholder: "0.00",
4232
+ value: outstandingValue,
4233
+ onChange: (e) => setOutstandingValue(e.target.value),
4234
+ className: "h-9 flex-1 min-w-0 rounded-lg border border-border bg-background px-3 py-1 text-sm outline-none placeholder:text-muted-foreground/60"
4235
+ }
4236
+ )
4237
+ ] }),
4238
+ /* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
4239
+ /* @__PURE__ */ jsx47(
4240
+ Button,
4241
+ {
4242
+ variant: "ghost",
4243
+ size: "sm",
4244
+ onClick: handleOutstandingClear,
4245
+ className: "text-muted-foreground hover:text-foreground h-8 px-3",
4246
+ children: "Clear"
4247
+ }
4248
+ ),
4249
+ /* @__PURE__ */ jsx47(
4250
+ Button,
4251
+ {
4252
+ color: "primary",
4253
+ size: "sm",
4254
+ onClick: handleOutstandingApply,
4255
+ className: "h-8 px-3",
4256
+ children: "Apply"
4257
+ }
4258
+ )
4259
+ ] })
4260
+ ] })
4261
+ ]
4262
+ }
4263
+ )
4264
+ ] })
4265
+ }
4266
+ )
4267
+ ] }) });
4268
+ }
4269
+
3076
4270
  // src/app/data/DataTable.tsx
3077
- import { useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
4271
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState5 } from "react";
3078
4272
  import {
3079
4273
  ArrowDownIcon,
3080
4274
  ArrowUpDownIcon,
3081
4275
  ArrowUpIcon,
3082
4276
  ChevronLeftIcon,
3083
- ChevronRightIcon
4277
+ ChevronRightIcon as ChevronRightIcon2
3084
4278
  } from "lucide-react";
3085
- import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
3086
- var shellClass2 = "overflow-hidden rounded-xl border border-border bg-gradient-to-b from-elevated-from to-elevated-to shadow-card";
3087
- var tableClass = "w-full border-collapse bg-transparent text-sm";
3088
- var headRowClass = "bg-muted/40";
3089
- 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";
3090
- var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-2.5 text-foreground";
3091
- var rowClass = "bg-transparent transition-colors hover:bg-muted/40 data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.06]";
3092
- var footCellClass = "border-t border-border/60 bg-muted/20 px-4 py-2.5 text-xs text-muted-foreground";
4279
+ import { jsx as jsx48, jsxs as jsxs37 } from "react/jsx-runtime";
4280
+ var shellClass2 = "w-full";
4281
+ var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
4282
+ var headRowClass = "";
4283
+ 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";
4284
+ var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-4 text-foreground";
4285
+ var rowClass = "bg-transparent transition-colors hover:bg-muted/20 data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.04]";
4286
+ var footCellClass = "border-t border-border/40 bg-transparent px-4 py-3 text-xs text-muted-foreground";
3093
4287
  var footInnerClass = "flex flex-wrap items-center gap-2";
3094
4288
  var emptyCellClass = "bg-transparent px-4 py-10 text-center text-sm text-muted-foreground";
3095
- 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";
3096
- var stickyHeadClass = "sticky top-0 z-[1] bg-muted/95 shadow-[0_1px_0_0_hsl(var(--border)/0.6)] backdrop-blur-sm";
3097
- var selectCellClass = "w-10 px-4 py-2.5 align-middle";
3098
- 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";
4289
+ 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";
4290
+ var stickyHeadClass = "sticky top-0 z-[1] bg-background/95 shadow-[0_1px_0_0_hsl(var(--border)/0.4)] backdrop-blur-sm";
4291
+ var selectCellClass = "w-10 px-4 py-4 align-middle";
4292
+ 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";
3099
4293
  var alignClass = {
3100
4294
  left: "text-left",
3101
4295
  center: "text-center",
@@ -3125,12 +4319,12 @@ function SortIndicator({
3125
4319
  }) {
3126
4320
  const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
3127
4321
  if (!active) {
3128
- return /* @__PURE__ */ jsx44(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
4322
+ return /* @__PURE__ */ jsx48(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
3129
4323
  }
3130
4324
  if (direction === "desc") {
3131
- return /* @__PURE__ */ jsx44(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
4325
+ return /* @__PURE__ */ jsx48(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
3132
4326
  }
3133
- return /* @__PURE__ */ jsx44(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
4327
+ return /* @__PURE__ */ jsx48(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
3134
4328
  }
3135
4329
  function DataTable({
3136
4330
  columns,
@@ -3162,7 +4356,7 @@ function DataTable({
3162
4356
  defaultPageIndex = 0,
3163
4357
  onPageChange
3164
4358
  }) {
3165
- const [uncontrolledSort, setUncontrolledSort] = useState4(
4359
+ const [uncontrolledSort, setUncontrolledSort] = useState5(
3166
4360
  defaultSort
3167
4361
  );
3168
4362
  const isSortControlled = sortProp !== void 0;
@@ -3173,19 +4367,19 @@ function DataTable({
3173
4367
  }
3174
4368
  onSortChange?.(next);
3175
4369
  };
3176
- const [uncontrolledSelected, setUncontrolledSelected] = useState4(
4370
+ const [uncontrolledSelected, setUncontrolledSelected] = useState5(
3177
4371
  defaultSelectedKeys ?? []
3178
4372
  );
3179
4373
  const isSelectionControlled = selectedKeysProp !== void 0;
3180
4374
  const selectedKeys = isSelectionControlled ? selectedKeysProp : uncontrolledSelected;
3181
- const selectedSet = useMemo2(() => new Set(selectedKeys), [selectedKeys]);
4375
+ const selectedSet = useMemo3(() => new Set(selectedKeys), [selectedKeys]);
3182
4376
  const setSelected = (next) => {
3183
4377
  if (!isSelectionControlled) {
3184
4378
  setUncontrolledSelected(next);
3185
4379
  }
3186
4380
  onSelectionChange?.(next);
3187
4381
  };
3188
- const [uncontrolledPage, setUncontrolledPage] = useState4(defaultPageIndex);
4382
+ const [uncontrolledPage, setUncontrolledPage] = useState5(defaultPageIndex);
3189
4383
  const isPageControlled = pageIndexProp !== void 0;
3190
4384
  const rawPageIndex = isPageControlled ? pageIndexProp : uncontrolledPage;
3191
4385
  const setPage = (next) => {
@@ -3194,7 +4388,7 @@ function DataTable({
3194
4388
  }
3195
4389
  onPageChange?.(next);
3196
4390
  };
3197
- const sortedRows = useMemo2(() => {
4391
+ const sortedRows = useMemo3(() => {
3198
4392
  if (!sort) return rows;
3199
4393
  const column = columns.find((col) => col.id === sort.columnId);
3200
4394
  if (!column?.sortable) return rows;
@@ -3213,13 +4407,13 @@ function DataTable({
3213
4407
  const paginated = typeof pageSize === "number" && pageSize > 0;
3214
4408
  const pageCount = paginated ? Math.max(1, Math.ceil(sortedRows.length / pageSize)) : 1;
3215
4409
  const pageIndex = Math.min(Math.max(0, rawPageIndex), pageCount - 1);
3216
- useEffect2(() => {
4410
+ useEffect4(() => {
3217
4411
  if (!paginated || isPageControlled) return;
3218
4412
  if (uncontrolledPage > pageCount - 1) {
3219
4413
  setUncontrolledPage(pageCount - 1);
3220
4414
  }
3221
4415
  }, [paginated, isPageControlled, uncontrolledPage, pageCount]);
3222
- const visibleRows = useMemo2(() => {
4416
+ const visibleRows = useMemo3(() => {
3223
4417
  if (!paginated) return sortedRows;
3224
4418
  const start = pageIndex * pageSize;
3225
4419
  return sortedRows.slice(start, start + pageSize);
@@ -3228,7 +4422,7 @@ function DataTable({
3228
4422
  const headPad = dense ? "px-3 py-2" : void 0;
3229
4423
  const colSpan = columns.length + (selectable ? 1 : 0);
3230
4424
  if (!loading && rows.length === 0 && emptyMode === "replace") {
3231
- return /* @__PURE__ */ jsx44(EmptyState, { title: emptyTitle, description: emptyDescription, className });
4425
+ return /* @__PURE__ */ jsx48(EmptyState, { title: emptyTitle, description: emptyDescription, className });
3232
4426
  }
3233
4427
  const allKeys = sortedRows.map(getRowKey);
3234
4428
  const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
@@ -3253,10 +4447,10 @@ function DataTable({
3253
4447
  const hasPager = paginated && !loading && sortedRows.length > 0;
3254
4448
  const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
3255
4449
  const skeletonCount = loadingRows ?? pageSize ?? 5;
3256
- 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: [
3257
- caption ? /* @__PURE__ */ jsx44("caption", { className: "sr-only", children: caption }) : null,
3258
- /* @__PURE__ */ jsx44("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs34("tr", { children: [
3259
- selectable ? /* @__PURE__ */ jsx44("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx44(
4450
+ 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: [
4451
+ caption ? /* @__PURE__ */ jsx48("caption", { className: "sr-only", children: caption }) : null,
4452
+ /* @__PURE__ */ jsx48("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs37("tr", { children: [
4453
+ selectable ? /* @__PURE__ */ jsx48("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx48(
3260
4454
  Checkbox,
3261
4455
  {
3262
4456
  checked: headerCheckedState,
@@ -3268,19 +4462,19 @@ function DataTable({
3268
4462
  columns.map((col) => {
3269
4463
  const isSorted = sort?.columnId === col.id;
3270
4464
  const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
3271
- const headerContent = col.sortable ? /* @__PURE__ */ jsxs34(
4465
+ const headerContent = col.sortable ? /* @__PURE__ */ jsxs37(
3272
4466
  "button",
3273
4467
  {
3274
4468
  type: "button",
3275
4469
  className: sortButtonClass,
3276
4470
  onClick: () => setSort(nextSort(sort, col.id)),
3277
4471
  children: [
3278
- /* @__PURE__ */ jsx44("span", { className: "truncate", children: col.header }),
3279
- /* @__PURE__ */ jsx44(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
4472
+ /* @__PURE__ */ jsx48("span", { className: "truncate", children: col.header }),
4473
+ /* @__PURE__ */ jsx48(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
3280
4474
  ]
3281
4475
  }
3282
4476
  ) : col.header;
3283
- return /* @__PURE__ */ jsx44(
4477
+ return /* @__PURE__ */ jsx48(
3284
4478
  "th",
3285
4479
  {
3286
4480
  scope: "col",
@@ -3297,9 +4491,9 @@ function DataTable({
3297
4491
  );
3298
4492
  })
3299
4493
  ] }) }),
3300
- /* @__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: [
3301
- selectable ? /* @__PURE__ */ jsx44("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx44(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
3302
- columns.map((col) => /* @__PURE__ */ jsx44(
4494
+ /* @__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: [
4495
+ selectable ? /* @__PURE__ */ jsx48("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx48(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
4496
+ columns.map((col) => /* @__PURE__ */ jsx48(
3303
4497
  "td",
3304
4498
  {
3305
4499
  className: cn(
@@ -3308,17 +4502,17 @@ function DataTable({
3308
4502
  col.align && alignClass[col.align],
3309
4503
  col.className
3310
4504
  ),
3311
- children: /* @__PURE__ */ jsx44(Skeleton, { className: "h-4 w-[60%]" })
4505
+ children: /* @__PURE__ */ jsx48(Skeleton, { className: "h-4 w-[60%]" })
3312
4506
  },
3313
4507
  col.id
3314
4508
  ))
3315
- ] }, `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: [
3316
- /* @__PURE__ */ jsx44("p", { className: "font-medium text-foreground", children: emptyTitle }),
3317
- emptyDescription ? /* @__PURE__ */ jsx44("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
4509
+ ] }, `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: [
4510
+ /* @__PURE__ */ jsx48("p", { className: "font-medium text-foreground", children: emptyTitle }),
4511
+ emptyDescription ? /* @__PURE__ */ jsx48("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
3318
4512
  ] }) }) }) : visibleRows.map((row) => {
3319
4513
  const key = getRowKey(row);
3320
4514
  const isSelected = selectedSet.has(key);
3321
- return /* @__PURE__ */ jsxs34(
4515
+ return /* @__PURE__ */ jsxs37(
3322
4516
  "tr",
3323
4517
  {
3324
4518
  className: rowClass,
@@ -3334,12 +4528,12 @@ function DataTable({
3334
4528
  tabIndex: onRowClick ? 0 : void 0,
3335
4529
  role: onRowClick ? "button" : void 0,
3336
4530
  children: [
3337
- selectable ? /* @__PURE__ */ jsx44(
4531
+ selectable ? /* @__PURE__ */ jsx48(
3338
4532
  "td",
3339
4533
  {
3340
4534
  className: cn(selectCellClass, cellPad),
3341
4535
  onClick: (event) => event.stopPropagation(),
3342
- children: /* @__PURE__ */ jsx44(
4536
+ children: /* @__PURE__ */ jsx48(
3343
4537
  Checkbox,
3344
4538
  {
3345
4539
  checked: isSelected,
@@ -3349,7 +4543,7 @@ function DataTable({
3349
4543
  )
3350
4544
  }
3351
4545
  ) : null,
3352
- columns.map((col) => /* @__PURE__ */ jsx44(
4546
+ columns.map((col) => /* @__PURE__ */ jsx48(
3353
4547
  "td",
3354
4548
  {
3355
4549
  className: cn(
@@ -3359,7 +4553,7 @@ function DataTable({
3359
4553
  col.align && alignClass[col.align],
3360
4554
  col.className
3361
4555
  ),
3362
- children: col.truncate ? /* @__PURE__ */ jsx44("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
4556
+ children: col.truncate ? /* @__PURE__ */ jsx48("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
3363
4557
  },
3364
4558
  col.id
3365
4559
  ))
@@ -3368,7 +4562,7 @@ function DataTable({
3368
4562
  key
3369
4563
  );
3370
4564
  }) }),
3371
- hasFoot ? /* @__PURE__ */ jsx44("tfoot", { children: /* @__PURE__ */ jsx44("tr", { children: /* @__PURE__ */ jsx44("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs34(
4565
+ hasFoot ? /* @__PURE__ */ jsx48("tfoot", { children: /* @__PURE__ */ jsx48("tr", { children: /* @__PURE__ */ jsx48("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs37(
3372
4566
  "div",
3373
4567
  {
3374
4568
  className: cn(
@@ -3376,18 +4570,18 @@ function DataTable({
3376
4570
  (showRowCount || footer || hasPager) && "justify-between"
3377
4571
  ),
3378
4572
  children: [
3379
- /* @__PURE__ */ jsxs34("div", { className: footInnerClass, children: [
3380
- showRowCount ? /* @__PURE__ */ jsxs34("span", { children: [
4573
+ /* @__PURE__ */ jsxs37("div", { className: footInnerClass, children: [
4574
+ showRowCount ? /* @__PURE__ */ jsxs37("span", { children: [
3381
4575
  rowCountText,
3382
4576
  selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
3383
- ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs34("span", { children: [
4577
+ ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs37("span", { children: [
3384
4578
  selectedSet.size,
3385
4579
  " selected"
3386
4580
  ] }) : null,
3387
4581
  footer
3388
4582
  ] }),
3389
- hasPager ? /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2", children: [
3390
- /* @__PURE__ */ jsxs34("span", { className: "tabular-nums", children: [
4583
+ hasPager ? /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2", children: [
4584
+ /* @__PURE__ */ jsxs37("span", { className: "tabular-nums", children: [
3391
4585
  pageIndex * pageSize + 1,
3392
4586
  "\u2013",
3393
4587
  Math.min(
@@ -3398,8 +4592,8 @@ function DataTable({
3398
4592
  "of ",
3399
4593
  sortedRows.length
3400
4594
  ] }),
3401
- /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-1", children: [
3402
- /* @__PURE__ */ jsx44(
4595
+ /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1", children: [
4596
+ /* @__PURE__ */ jsx48(
3403
4597
  "button",
3404
4598
  {
3405
4599
  type: "button",
@@ -3407,10 +4601,10 @@ function DataTable({
3407
4601
  onClick: () => setPage(pageIndex - 1),
3408
4602
  disabled: pageIndex <= 0,
3409
4603
  "aria-label": "Previous page",
3410
- children: /* @__PURE__ */ jsx44(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
4604
+ children: /* @__PURE__ */ jsx48(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
3411
4605
  }
3412
4606
  ),
3413
- /* @__PURE__ */ jsx44(
4607
+ /* @__PURE__ */ jsx48(
3414
4608
  "button",
3415
4609
  {
3416
4610
  type: "button",
@@ -3418,7 +4612,7 @@ function DataTable({
3418
4612
  onClick: () => setPage(pageIndex + 1),
3419
4613
  disabled: pageIndex >= pageCount - 1,
3420
4614
  "aria-label": "Next page",
3421
- children: /* @__PURE__ */ jsx44(ChevronRightIcon, { className: "size-4", "aria-hidden": true })
4615
+ children: /* @__PURE__ */ jsx48(ChevronRightIcon2, { className: "size-4", "aria-hidden": true })
3422
4616
  }
3423
4617
  )
3424
4618
  ] })
@@ -3430,8 +4624,8 @@ function DataTable({
3430
4624
  }
3431
4625
 
3432
4626
  // src/app/data/ChartPanel.tsx
3433
- import { useId as useId4 } from "react";
3434
- import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
4627
+ import { useId as useId9 } from "react";
4628
+ import { jsx as jsx49, jsxs as jsxs38 } from "react/jsx-runtime";
3435
4629
  var ChartPanel = ({
3436
4630
  title,
3437
4631
  description,
@@ -3446,17 +4640,17 @@ var ChartPanel = ({
3446
4640
  const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
3447
4641
  const metricChartPlotRegionClass = useAppDensityClass("metricChartPlotRegion");
3448
4642
  const chartPanelBodyClass = useAppDensityClass("chartPanelBody");
3449
- const titleId = useId4();
4643
+ const titleId = useId9();
3450
4644
  const resolvedTitle = title ?? artifact?.title;
3451
4645
  const hasHeader = Boolean(resolvedTitle || description || actions);
3452
- 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);
3453
- return /* @__PURE__ */ jsxs35(
4646
+ 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);
4647
+ return /* @__PURE__ */ jsxs38(
3454
4648
  "section",
3455
4649
  {
3456
4650
  className: cn(metricCardShellClass, "aui-app-chart-panel", className),
3457
4651
  "aria-labelledby": resolvedTitle ? titleId : void 0,
3458
4652
  children: [
3459
- /* @__PURE__ */ jsx45(
4653
+ /* @__PURE__ */ jsx49(
3460
4654
  MetricCardHeader,
3461
4655
  {
3462
4656
  title: resolvedTitle,
@@ -3465,14 +4659,14 @@ var ChartPanel = ({
3465
4659
  actions
3466
4660
  }
3467
4661
  ),
3468
- /* @__PURE__ */ jsx45(
4662
+ /* @__PURE__ */ jsx49(
3469
4663
  "div",
3470
4664
  {
3471
4665
  className: cn(
3472
4666
  "relative min-h-0 w-full",
3473
4667
  hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
3474
4668
  ),
3475
- children: body ?? /* @__PURE__ */ jsx45(
4669
+ children: body ?? /* @__PURE__ */ jsx49(
3476
4670
  "div",
3477
4671
  {
3478
4672
  className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
@@ -3489,10 +4683,11 @@ var ChartPanel = ({
3489
4683
  };
3490
4684
 
3491
4685
  // src/app/data/MetricRow.tsx
3492
- import { useId as useId5, useState as useState5 } from "react";
3493
- import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
4686
+ import { useId as useId10, useState as useState6 } from "react";
4687
+ import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
3494
4688
  var MetricRow = ({
3495
4689
  title,
4690
+ titleTag,
3496
4691
  description,
3497
4692
  actions,
3498
4693
  metrics,
@@ -3504,9 +4699,9 @@ var MetricRow = ({
3504
4699
  className
3505
4700
  }) => {
3506
4701
  const metricTileClass = useAppDensityClass("metricTile");
3507
- const titleId = useId5();
4702
+ const titleId = useId10();
3508
4703
  const selectable = onMetricChange != null || activeMetricId != null;
3509
- const [internalId, setInternalId] = useState5(
4704
+ const [internalId, setInternalId] = useState6(
3510
4705
  defaultActiveMetricId ?? metrics[0]?.id
3511
4706
  );
3512
4707
  const activeId = activeMetricId ?? internalId;
@@ -3514,22 +4709,24 @@ var MetricRow = ({
3514
4709
  if (activeMetricId == null) setInternalId(id);
3515
4710
  onMetricChange?.(id);
3516
4711
  };
3517
- return /* @__PURE__ */ jsxs36(
4712
+ const hasHeader = Boolean(title || titleTag || description || actions);
4713
+ return /* @__PURE__ */ jsxs39(
3518
4714
  "section",
3519
4715
  {
3520
4716
  className: cn(metricCardShellClass, className),
3521
4717
  "aria-labelledby": title ? titleId : void 0,
3522
4718
  children: [
3523
- /* @__PURE__ */ jsx46(
4719
+ /* @__PURE__ */ jsx50(
3524
4720
  MetricCardHeader,
3525
4721
  {
3526
4722
  title,
4723
+ titleTag,
3527
4724
  titleId,
3528
4725
  description,
3529
4726
  actions
3530
4727
  }
3531
4728
  ),
3532
- /* @__PURE__ */ jsx46(
4729
+ /* @__PURE__ */ jsx50(
3533
4730
  "div",
3534
4731
  {
3535
4732
  role: selectable ? "group" : void 0,
@@ -3538,20 +4735,20 @@ var MetricRow = ({
3538
4735
  className: cn(
3539
4736
  metricTilesRowClass,
3540
4737
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
3541
- (title || description || actions) && "mt-3"
4738
+ hasHeader && "mt-3.5 border-t border-border/40"
3542
4739
  ),
3543
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs36(
4740
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs39(
3544
4741
  "div",
3545
4742
  {
3546
4743
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
3547
4744
  "aria-hidden": true,
3548
4745
  children: [
3549
- /* @__PURE__ */ jsx46(Skeleton, { className: "h-3 w-20" }),
3550
- /* @__PURE__ */ jsx46(Skeleton, { className: "h-7 w-24" })
4746
+ /* @__PURE__ */ jsx50(Skeleton, { className: "h-3 w-20" }),
4747
+ /* @__PURE__ */ jsx50(Skeleton, { className: "h-7 w-24" })
3551
4748
  ]
3552
4749
  },
3553
4750
  `skeleton-${index}`
3554
- )) : metrics.map((m, index) => /* @__PURE__ */ jsx46(
4751
+ )) : metrics.map((m, index) => /* @__PURE__ */ jsx50(
3555
4752
  MetricTile,
3556
4753
  {
3557
4754
  label: m.label,
@@ -3559,6 +4756,11 @@ var MetricRow = ({
3559
4756
  unit: m.unit,
3560
4757
  trend: m.trend,
3561
4758
  trendTone: m.trendTone,
4759
+ trendVariant: m.trendVariant,
4760
+ activeTone: m.activeTone,
4761
+ sparklineData: m.sparklineData,
4762
+ sparklineConfig: m.sparklineConfig,
4763
+ sparkline: m.sparkline,
3562
4764
  active: selectable && m.id === activeId,
3563
4765
  showDivider: index < metrics.length - 1,
3564
4766
  onSelect: selectable ? () => select(m.id) : void 0
@@ -3573,10 +4775,11 @@ var MetricRow = ({
3573
4775
  };
3574
4776
 
3575
4777
  // src/app/data/MetricChartCard.tsx
3576
- import { useId as useId6, useState as useState6 } from "react";
3577
- import { jsx as jsx47, jsxs as jsxs37 } from "react/jsx-runtime";
4778
+ import { useId as useId11, useState as useState7 } from "react";
4779
+ import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
3578
4780
  var MetricChartCard = ({
3579
4781
  title,
4782
+ titleTag,
3580
4783
  description,
3581
4784
  actions,
3582
4785
  metrics,
@@ -3597,8 +4800,8 @@ var MetricChartCard = ({
3597
4800
  const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
3598
4801
  const metricChartRegionClass = useAppDensityClass("metricChartRegion");
3599
4802
  const metricTileClass = useAppDensityClass("metricTile");
3600
- const titleId = useId6();
3601
- const [internalId, setInternalId] = useState6(
4803
+ const titleId = useId11();
4804
+ const [internalId, setInternalId] = useState7(
3602
4805
  defaultActiveMetricId ?? metrics[0]?.id
3603
4806
  );
3604
4807
  const activeId = activeMetricId ?? internalId;
@@ -3607,24 +4810,25 @@ var MetricChartCard = ({
3607
4810
  if (activeMetricId == null) setInternalId(id);
3608
4811
  onMetricChange?.(id);
3609
4812
  };
3610
- const hasHeader = Boolean(title || description || actions);
4813
+ const hasHeader = Boolean(title || titleTag || description || actions);
3611
4814
  const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
3612
- return /* @__PURE__ */ jsxs37(
4815
+ return /* @__PURE__ */ jsxs40(
3613
4816
  "section",
3614
4817
  {
3615
4818
  className: cn(metricCardShellClass, className),
3616
4819
  "aria-labelledby": title ? titleId : void 0,
3617
4820
  children: [
3618
- /* @__PURE__ */ jsx47(
4821
+ /* @__PURE__ */ jsx51(
3619
4822
  MetricCardHeader,
3620
4823
  {
3621
4824
  title,
3622
- titleId,
4825
+ titleTag,
3623
4826
  description,
3624
- actions
4827
+ actions,
4828
+ titleId
3625
4829
  }
3626
4830
  ),
3627
- /* @__PURE__ */ jsx47(
4831
+ /* @__PURE__ */ jsx51(
3628
4832
  "div",
3629
4833
  {
3630
4834
  role: "group",
@@ -3633,20 +4837,20 @@ var MetricChartCard = ({
3633
4837
  className: cn(
3634
4838
  metricTilesRowClass,
3635
4839
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
3636
- hasHeader && "mt-3"
4840
+ hasHeader && "mt-3.5 border-t border-border/40"
3637
4841
  ),
3638
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs37(
4842
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs40(
3639
4843
  "div",
3640
4844
  {
3641
4845
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
3642
4846
  "aria-hidden": true,
3643
4847
  children: [
3644
- /* @__PURE__ */ jsx47(Skeleton, { className: "h-3 w-20" }),
3645
- /* @__PURE__ */ jsx47(Skeleton, { className: "h-7 w-24" })
4848
+ /* @__PURE__ */ jsx51(Skeleton, { className: "h-3 w-20" }),
4849
+ /* @__PURE__ */ jsx51(Skeleton, { className: "h-7 w-24" })
3646
4850
  ]
3647
4851
  },
3648
4852
  `skeleton-${index}`
3649
- )) : metrics.map((m, index) => /* @__PURE__ */ jsx47(
4853
+ )) : metrics.map((m, index) => /* @__PURE__ */ jsx51(
3650
4854
  MetricTile,
3651
4855
  {
3652
4856
  label: m.label,
@@ -3654,6 +4858,11 @@ var MetricChartCard = ({
3654
4858
  unit: m.unit,
3655
4859
  trend: m.trend,
3656
4860
  trendTone: m.trendTone,
4861
+ trendVariant: m.trendVariant,
4862
+ activeTone: m.activeTone,
4863
+ sparklineData: m.sparklineData,
4864
+ sparklineConfig: m.sparklineConfig,
4865
+ sparkline: m.sparkline,
3657
4866
  active: m.id === active?.id,
3658
4867
  showDivider: index < metrics.length - 1,
3659
4868
  onSelect: () => select(m.id)
@@ -3662,14 +4871,14 @@ var MetricChartCard = ({
3662
4871
  ))
3663
4872
  }
3664
4873
  ),
3665
- /* @__PURE__ */ jsx47("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx47(
4874
+ /* @__PURE__ */ jsx51("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx51(
3666
4875
  Skeleton,
3667
4876
  {
3668
4877
  className: "w-full rounded-lg",
3669
4878
  style: { height },
3670
4879
  "aria-hidden": true
3671
4880
  }
3672
- ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx47(
4881
+ ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx51(
3673
4882
  LineAreaChart,
3674
4883
  {
3675
4884
  data: active.data,
@@ -3689,7 +4898,7 @@ var MetricChartCard = ({
3689
4898
  ariaLabel: chartAriaLabel
3690
4899
  },
3691
4900
  active.id
3692
- ) : /* @__PURE__ */ jsx47(
4901
+ ) : /* @__PURE__ */ jsx51(
3693
4902
  "div",
3694
4903
  {
3695
4904
  className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
@@ -3704,13 +4913,13 @@ var MetricChartCard = ({
3704
4913
  };
3705
4914
 
3706
4915
  // src/hooks/use-live-query.ts
3707
- import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState7 } from "react";
4916
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef, useState as useState8 } from "react";
3708
4917
  function useInterval(callback, delayMs) {
3709
4918
  const saved = useRef(callback);
3710
- useEffect3(() => {
4919
+ useEffect5(() => {
3711
4920
  saved.current = callback;
3712
4921
  }, [callback]);
3713
- useEffect3(() => {
4922
+ useEffect5(() => {
3714
4923
  if (delayMs === null) return;
3715
4924
  const id = setInterval(() => saved.current(), delayMs);
3716
4925
  return () => clearInterval(id);
@@ -3723,19 +4932,19 @@ function useLiveQuery(fetcher, options = {}) {
3723
4932
  immediate = true,
3724
4933
  refetchOnFocus = true
3725
4934
  } = options;
3726
- const [data, setData] = useState7(void 0);
3727
- const [error, setError] = useState7(void 0);
3728
- const [loading, setLoading] = useState7(enabled);
3729
- const [refreshing, setRefreshing] = useState7(false);
3730
- const [lastUpdated, setLastUpdated] = useState7(null);
4935
+ const [data, setData] = useState8(void 0);
4936
+ const [error, setError] = useState8(void 0);
4937
+ const [loading, setLoading] = useState8(enabled);
4938
+ const [refreshing, setRefreshing] = useState8(false);
4939
+ const [lastUpdated, setLastUpdated] = useState8(null);
3731
4940
  const fetcherRef = useRef(fetcher);
3732
- useEffect3(() => {
4941
+ useEffect5(() => {
3733
4942
  fetcherRef.current = fetcher;
3734
4943
  }, [fetcher]);
3735
4944
  const mounted = useRef(true);
3736
4945
  const requestId = useRef(0);
3737
4946
  const hasData = useRef(false);
3738
- useEffect3(() => {
4947
+ useEffect5(() => {
3739
4948
  mounted.current = true;
3740
4949
  return () => {
3741
4950
  mounted.current = false;
@@ -3767,11 +4976,11 @@ function useLiveQuery(fetcher, options = {}) {
3767
4976
  if (!enabled) return;
3768
4977
  run();
3769
4978
  }, [enabled, run]);
3770
- useEffect3(() => {
4979
+ useEffect5(() => {
3771
4980
  if (!enabled) return;
3772
4981
  if (immediate) run();
3773
4982
  }, [enabled, immediate, run]);
3774
- useEffect3(() => {
4983
+ useEffect5(() => {
3775
4984
  if (!enabled || intervalMs === null) return;
3776
4985
  const tick = () => {
3777
4986
  if (refetchOnFocus && typeof document !== "undefined" && document.visibilityState === "hidden") {
@@ -3782,7 +4991,7 @@ function useLiveQuery(fetcher, options = {}) {
3782
4991
  const handle = setInterval(tick, intervalMs);
3783
4992
  return () => clearInterval(handle);
3784
4993
  }, [enabled, intervalMs, refetchOnFocus, run]);
3785
- useEffect3(() => {
4994
+ useEffect5(() => {
3786
4995
  if (!enabled || !refetchOnFocus || typeof document === "undefined") return;
3787
4996
  const onVisible = () => {
3788
4997
  if (document.visibilityState === "visible") run();
@@ -3793,69 +5002,6 @@ function useLiveQuery(fetcher, options = {}) {
3793
5002
  return { data, error, loading, refreshing, lastUpdated, refetch };
3794
5003
  }
3795
5004
 
3796
- // src/charts/sparkline.tsx
3797
- import { useId as useId7 } from "react";
3798
- import { Fragment as Fragment6, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
3799
- var Sparkline = ({
3800
- data,
3801
- dataKey = "value",
3802
- color = "var(--primary, #6366f1)",
3803
- area = true,
3804
- width = 96,
3805
- height = 28,
3806
- strokeWidth = 1.5,
3807
- className,
3808
- ariaLabel = "Trend"
3809
- }) => {
3810
- const uid = useId7();
3811
- const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
3812
- if (values.length === 0) {
3813
- return /* @__PURE__ */ jsx48("span", { className: cn("inline-block", className), style: { width, height } });
3814
- }
3815
- const pad = strokeWidth + 1;
3816
- const min = Math.min(...values);
3817
- const max = Math.max(...values);
3818
- const range = max - min || 1;
3819
- const innerW = width - pad * 2;
3820
- const innerH = height - pad * 2;
3821
- const points = values.map((v, i) => ({
3822
- x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
3823
- y: pad + innerH - (v - min) / range * innerH
3824
- }));
3825
- return /* @__PURE__ */ jsxs38(
3826
- "svg",
3827
- {
3828
- width,
3829
- height,
3830
- viewBox: `0 0 ${width} ${height}`,
3831
- className: cn("block", className),
3832
- role: "img",
3833
- "aria-label": ariaLabel,
3834
- preserveAspectRatio: "none",
3835
- children: [
3836
- area && /* @__PURE__ */ jsxs38(Fragment6, { children: [
3837
- /* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs38("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
3838
- /* @__PURE__ */ jsx48("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
3839
- /* @__PURE__ */ jsx48("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
3840
- ] }) }),
3841
- /* @__PURE__ */ jsx48("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
3842
- ] }),
3843
- /* @__PURE__ */ jsx48(
3844
- "path",
3845
- {
3846
- d: monotoneLinePath(points),
3847
- fill: "none",
3848
- stroke: color,
3849
- strokeWidth,
3850
- strokeLinecap: "round",
3851
- strokeLinejoin: "round"
3852
- }
3853
- )
3854
- ]
3855
- }
3856
- );
3857
- };
3858
-
3859
5005
  export {
3860
5006
  SEMANTIC_COLOR_TOKENS,
3861
5007
  RESERVED_GRADIENT_TOKENS,
@@ -3892,16 +5038,17 @@ export {
3892
5038
  AppDensityProvider,
3893
5039
  useAppDensity,
3894
5040
  useAppDensityClass,
5041
+ Sparkline,
3895
5042
  MetricTile,
3896
5043
  useAppShellChat,
3897
5044
  useAppShellNav,
3898
5045
  AppShell,
3899
- AppShellTopbar,
3900
5046
  AppShellChatTrigger,
3901
5047
  AppShellSidebarTrigger,
3902
5048
  PageHeader,
3903
5049
  Page,
3904
5050
  Section,
5051
+ Stack,
3905
5052
  AppCopilotProvider,
3906
5053
  useAppCopilotContext,
3907
5054
  AppChatPanel,
@@ -3915,6 +5062,8 @@ export {
3915
5062
  DescriptionList,
3916
5063
  ExpandableSection,
3917
5064
  ResourceCard,
5065
+ AlertCard,
5066
+ CatalogCard,
3918
5067
  SettingsSectionHeader,
3919
5068
  SettingsSection,
3920
5069
  FieldRow,
@@ -3939,11 +5088,11 @@ export {
3939
5088
  FormSection,
3940
5089
  FilterBar,
3941
5090
  FilterField,
5091
+ FilterDropdown,
3942
5092
  DataTable,
3943
5093
  ChartPanel,
3944
5094
  MetricRow,
3945
5095
  MetricChartCard,
3946
5096
  useInterval,
3947
- useLiveQuery,
3948
- Sparkline
5097
+ useLiveQuery
3949
5098
  };