@timbal-ai/timbal-react 1.5.0 → 1.6.1

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 -1
  2. package/README.md +33 -0
  3. package/dist/app.cjs +884 -642
  4. package/dist/app.d.cts +4 -4
  5. package/dist/app.d.ts +4 -4
  6. package/dist/app.esm.js +6 -6
  7. package/dist/{chart-artifact-2OTDTRwM.d.ts → chart-artifact-BYl5C-dk.d.ts} +90 -29
  8. package/dist/{chart-artifact-CS3qyGIY.d.cts → chart-artifact-Dpt4t5sf.d.cts} +90 -29
  9. package/dist/{chat-ClmzWzCX.d.cts → chat-DDsp-Vzz.d.cts} +1 -1
  10. package/dist/{chat-ClmzWzCX.d.ts → chat-DDsp-Vzz.d.ts} +1 -1
  11. package/dist/chat.cjs +26 -26
  12. package/dist/chat.d.cts +3 -3
  13. package/dist/chat.d.ts +3 -3
  14. package/dist/chat.esm.js +3 -3
  15. package/dist/{chunk-TZI3ID3C.esm.js → chunk-24B4I4XC.esm.js} +3 -3
  16. package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
  17. package/dist/{chunk-WMKPT5BV.esm.js → chunk-HSL36SJ4.esm.js} +6 -6
  18. package/dist/chunk-JJOO4PR5.esm.js +391 -0
  19. package/dist/{chunk-AZL2WANO.esm.js → chunk-MBS7XHV2.esm.js} +28 -28
  20. package/dist/{chunk-5ECRZ5O7.esm.js → chunk-NO5AWNWT.esm.js} +224 -57
  21. package/dist/{chunk-ZNYAETFD.esm.js → chunk-R4RQT2XQ.esm.js} +2 -2
  22. package/dist/{chunk-JYDJRGDE.esm.js → chunk-TMP7RIA7.esm.js} +2 -2
  23. package/dist/{chunk-SZDYIRMB.esm.js → chunk-UVPXH4MB.esm.js} +647 -532
  24. package/dist/{chunk-IGHBLJV3.esm.js → chunk-WQIQW7EM.esm.js} +3 -2
  25. package/dist/{chunk-B4XAC4G7.esm.js → chunk-YYEI6XME.esm.js} +361 -527
  26. package/dist/{circular-progress-CDsJwIPF.d.cts → circular-progress-B9nnwzCu.d.cts} +1 -1
  27. package/dist/{circular-progress-CDsJwIPF.d.ts → circular-progress-B9nnwzCu.d.ts} +1 -1
  28. package/dist/cli/timbal-ui-lint.mjs +503 -0
  29. package/dist/index.cjs +1358 -856
  30. package/dist/index.d.cts +9 -8
  31. package/dist/index.d.ts +9 -8
  32. package/dist/index.esm.js +40 -20
  33. package/dist/{kanban-U5xNe9py.d.cts → kanban-FFBeaZPS.d.cts} +4 -4
  34. package/dist/{kanban-U5xNe9py.d.ts → kanban-FFBeaZPS.d.ts} +4 -4
  35. package/dist/{layout-B8r6Jbat.d.ts → layout-CuKeSY74.d.ts} +1 -1
  36. package/dist/{layout-Cu7Ijn04.d.cts → layout-PzVwkJyL.d.cts} +1 -1
  37. package/dist/site.cjs +71 -0
  38. package/dist/site.d.cts +15 -1
  39. package/dist/site.d.ts +15 -1
  40. package/dist/site.esm.js +12 -311
  41. package/dist/studio.cjs +31 -31
  42. package/dist/studio.d.cts +2 -2
  43. package/dist/studio.d.ts +2 -2
  44. package/dist/studio.esm.js +7 -7
  45. package/dist/{timbal-v2-button-B7vPs7gg.d.ts → timbal-v2-button-DCAZNyUx.d.cts} +1 -1
  46. package/dist/{timbal-v2-button-B7vPs7gg.d.cts → timbal-v2-button-DCAZNyUx.d.ts} +1 -1
  47. package/dist/ui.cjs +77 -77
  48. package/dist/ui.d.cts +3 -3
  49. package/dist/ui.d.ts +3 -3
  50. package/dist/ui.esm.js +15 -15
  51. package/dist/{welcome-NXZlcihe.d.cts → welcome-B00oH5Io.d.cts} +1 -1
  52. package/dist/{welcome-DduQAC4K.d.ts → welcome-DU-4NTjZ.d.ts} +1 -1
  53. package/package.json +13 -3
package/dist/index.cjs CHANGED
@@ -38,7 +38,7 @@ __export(index_exports, {
38
38
  AccordionContent: () => AccordionContent,
39
39
  AccordionItem: () => AccordionItem,
40
40
  AccordionTrigger: () => AccordionTrigger,
41
- ActionBarPrimitive: () => import_react79.ActionBarPrimitive,
41
+ ActionBarPrimitive: () => import_react84.ActionBarPrimitive,
42
42
  Alert: () => Alert,
43
43
  AlertDescription: () => AlertDescription,
44
44
  AlertDialog: () => AlertDialog,
@@ -63,8 +63,8 @@ __export(index_exports, {
63
63
  ArtifactRegistryProvider: () => ArtifactRegistryProvider,
64
64
  ArtifactView: () => ArtifactView,
65
65
  AspectRatio: () => AspectRatio,
66
- AssistantRuntimeProvider: () => import_react79.AssistantRuntimeProvider,
67
- AuiIf: () => import_react79.AuiIf,
66
+ AssistantRuntimeProvider: () => import_react84.AssistantRuntimeProvider,
67
+ AuiIf: () => import_react84.AuiIf,
68
68
  AuthGuard: () => AuthGuard,
69
69
  Avatar: () => Avatar,
70
70
  AvatarFallback: () => AvatarFallback,
@@ -121,7 +121,7 @@ __export(index_exports, {
121
121
  CommandSeparator: () => CommandSeparator,
122
122
  CommandShortcut: () => CommandShortcut,
123
123
  Composer: () => Composer,
124
- ComposerPrimitive: () => import_react79.ComposerPrimitive,
124
+ ComposerPrimitive: () => import_react84.ComposerPrimitive,
125
125
  ConnectionRow: () => ConnectionRow,
126
126
  ConnectionRowList: () => ConnectionRowList,
127
127
  ContextMenu: () => ContextMenu,
@@ -140,6 +140,7 @@ __export(index_exports, {
140
140
  ContextMenuTrigger: () => ContextMenuTrigger,
141
141
  CopyButton: () => CopyButton,
142
142
  DEFAULT_UPLOAD_ACCEPT: () => DEFAULT_UPLOAD_ACCEPT,
143
+ DURATION: () => DURATION,
143
144
  DangerZone: () => DangerZone,
144
145
  DangerZoneAction: () => DangerZoneAction,
145
146
  DataTable: () => DataTable,
@@ -173,6 +174,7 @@ __export(index_exports, {
173
174
  DropdownMenuSubContent: () => DropdownMenuSubContent,
174
175
  DropdownMenuSubTrigger: () => DropdownMenuSubTrigger,
175
176
  DropdownMenuTrigger: () => DropdownMenuTrigger,
177
+ EASE: () => EASE,
176
178
  EmptyState: () => EmptyState,
177
179
  ExpandableSection: () => ExpandableSection,
178
180
  Field: () => Field,
@@ -218,7 +220,9 @@ __export(index_exports, {
218
220
  KbdGroup: () => KbdGroup,
219
221
  Label: () => Label3,
220
222
  LineAreaChart: () => LineAreaChart,
223
+ Magnetic: () => Magnetic,
221
224
  MarkdownText: () => MarkdownText,
225
+ Marquee: () => Marquee,
222
226
  MemoPillSegmentedTabs: () => MemoPillSegmentedTabs,
223
227
  Menubar: () => Menubar,
224
228
  MenubarCheckboxItem: () => MenubarCheckboxItem,
@@ -234,7 +238,7 @@ __export(index_exports, {
234
238
  MenubarSubContent: () => MenubarSubContent,
235
239
  MenubarSubTrigger: () => MenubarSubTrigger,
236
240
  MenubarTrigger: () => MenubarTrigger,
237
- MessagePrimitive: () => import_react79.MessagePrimitive,
241
+ MessagePrimitive: () => import_react84.MessagePrimitive,
238
242
  MetricChartCard: () => MetricChartCard,
239
243
  MetricRow: () => MetricRow,
240
244
  MetricTile: () => MetricTile,
@@ -257,6 +261,7 @@ __export(index_exports, {
257
261
  PaginationLink: () => PaginationLink,
258
262
  PaginationNext: () => PaginationNext,
259
263
  PaginationPrevious: () => PaginationPrevious,
264
+ Parallax: () => Parallax,
260
265
  PieChart: () => PieChart,
261
266
  PillSegmentedTabs: () => PillSegmentedTabs,
262
267
  PlanBadge: () => PlanBadge,
@@ -273,8 +278,11 @@ __export(index_exports, {
273
278
  RadioGroupItem: () => RadioGroupItem,
274
279
  Rating: () => Rating,
275
280
  ResourceCard: () => ResourceCard,
281
+ Reveal: () => Reveal,
276
282
  SEMANTIC_COLOR_TOKENS: () => SEMANTIC_COLOR_TOKENS,
283
+ SITE_AGENT_INSTRUCTIONS: () => SITE_AGENT_INSTRUCTIONS,
277
284
  SLOP_BUDGETS: () => SLOP_BUDGETS,
285
+ SPRING: () => SPRING,
278
286
  STUDIO_NAV_MODE: () => STUDIO_NAV_MODE,
279
287
  ScrollArea: () => ScrollArea,
280
288
  ScrollBar: () => ScrollBar,
@@ -334,9 +342,10 @@ __export(index_exports, {
334
342
  TableHeader: () => TableHeader,
335
343
  TableRow: () => TableRow,
336
344
  TagInput: () => TagInput,
345
+ TextReveal: () => TextReveal,
337
346
  Textarea: () => Textarea,
338
347
  Thread: () => Thread,
339
- ThreadPrimitive: () => import_react79.ThreadPrimitive,
348
+ ThreadPrimitive: () => import_react84.ThreadPrimitive,
340
349
  TimbalChat: () => TimbalChat,
341
350
  TimbalChatShell: () => TimbalChatShell,
342
351
  TimbalMark: () => TimbalMark,
@@ -435,15 +444,15 @@ __export(index_exports, {
435
444
  useAppShellChat: () => useAppShellChat,
436
445
  useAppShellNav: () => useAppShellNav,
437
446
  useArtifactRegistry: () => useArtifactRegistry,
438
- useComposerRuntime: () => import_react79.useComposerRuntime,
447
+ useComposerRuntime: () => import_react84.useComposerRuntime,
439
448
  useInterval: () => useInterval,
440
449
  useLiveQuery: () => useLiveQuery,
441
- useMessageRuntime: () => import_react79.useMessageRuntime,
450
+ useMessageRuntime: () => import_react84.useMessageRuntime,
442
451
  useOptionalSession: () => useOptionalSession,
443
452
  useResolvedSuggestions: () => useResolvedSuggestions,
444
453
  useSession: () => useSession,
445
- useThread: () => import_react79.useThread,
446
- useThreadRuntime: () => import_react79.useThreadRuntime,
454
+ useThread: () => import_react84.useThread,
455
+ useThreadRuntime: () => import_react84.useThreadRuntime,
447
456
  useTimbalRuntime: () => useTimbalRuntime,
448
457
  useTimbalStream: () => useTimbalStream,
449
458
  useToast: () => useToast,
@@ -1413,22 +1422,22 @@ var TIMBAL_V2_MODAL_SURFACE = cn(
1413
1422
  );
1414
1423
  var TIMBAL_V2_PRIMARY_GRADIENT = "bg-gradient-to-b from-primary-fill-from to-primary-fill-to";
1415
1424
  var TIMBAL_V2_SIZE_HEIGHT = {
1416
- xs: "min-h-8 h-8",
1417
- sm: "min-h-9 h-9",
1418
- md: "min-h-10 h-10",
1419
- lg: "min-h-11 h-11"
1425
+ xs: "min-h-7 h-7",
1426
+ sm: "min-h-8 h-8",
1427
+ md: "min-h-9 h-9",
1428
+ lg: "min-h-10 h-10"
1420
1429
  };
1421
1430
  var TIMBAL_V2_SIZE_ICON = {
1422
- xs: "min-h-8 min-w-8 size-8",
1431
+ xs: "min-h-7 min-w-7 size-7",
1423
1432
  sm: "min-h-8 min-w-8 size-8",
1424
- md: "min-h-10 min-w-10 size-10",
1425
- lg: "min-h-11 min-w-11 size-11"
1433
+ md: "min-h-9 min-w-9 size-9",
1434
+ lg: "min-h-10 min-w-10 size-10"
1426
1435
  };
1427
1436
  var TIMBAL_V2_SIZE_LABEL_PX = {
1428
- xs: "px-3",
1429
- sm: "px-4",
1430
- md: "px-5",
1431
- lg: "px-6"
1437
+ xs: "px-2.5",
1438
+ sm: "px-3",
1439
+ md: "px-3.5",
1440
+ lg: "px-4.5"
1432
1441
  };
1433
1442
  var TIMBAL_V2_FILL = {
1434
1443
  primary: [
@@ -1595,7 +1604,7 @@ function DialogContent({
1595
1604
  "data-slot": "dialog-content",
1596
1605
  className: cn(
1597
1606
  TIMBAL_V2_MODAL_SURFACE,
1598
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-[70] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl p-6 duration-200 outline-none sm:max-w-lg",
1607
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-[70] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl p-5 duration-200 outline-none sm:max-w-lg",
1599
1608
  className
1600
1609
  ),
1601
1610
  ...props,
@@ -1692,9 +1701,9 @@ function avatarChartVariantClass(_seed) {
1692
1701
  return AVATAR_PRIMARY_FALLBACK_CLASS;
1693
1702
  }
1694
1703
  var AVATAR_SIZE_CLASS = {
1695
- default: "size-8",
1696
- sm: "size-6",
1697
- lg: "size-10"
1704
+ default: "size-7",
1705
+ sm: "size-5",
1706
+ lg: "size-9"
1698
1707
  };
1699
1708
  function Avatar({
1700
1709
  className,
@@ -3875,16 +3884,16 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
3875
3884
  )
3876
3885
  },
3877
3886
  size: {
3878
- xs: "h-8 gap-1 rounded-md px-2.5 text-xs",
3879
- sm: "h-9 gap-1.5 rounded-lg px-3 text-sm",
3880
- md: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
3881
- lg: "h-11 gap-2 rounded-lg px-4 text-base",
3882
- xl: "h-12 gap-2 rounded-lg px-5 text-base",
3883
- default: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
3884
- icon: "h-10 w-10 rounded-lg",
3885
- "icon-xs": "h-8 w-8 rounded-md",
3886
- "icon-sm": "h-9 w-9 rounded-lg",
3887
- "icon-lg": "h-11 w-11 rounded-lg"
3887
+ xs: "h-7 gap-1 rounded-md px-2 text-xs",
3888
+ sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
3889
+ md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
3890
+ lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
3891
+ xl: "h-11 gap-2 rounded-lg px-4 text-base",
3892
+ default: "h-9 gap-1.5 rounded-lg px-3 text-sm",
3893
+ icon: "h-9 w-9 rounded-lg",
3894
+ "icon-xs": "h-7 w-7 rounded-md",
3895
+ "icon-sm": "h-8 w-8 rounded-md",
3896
+ "icon-lg": "h-10 w-10 rounded-lg"
3888
3897
  },
3889
3898
  shape: {
3890
3899
  pill: "rounded-full!",
@@ -4141,7 +4150,7 @@ var BadgeNode = ({ node }) => {
4141
4150
  "span",
4142
4151
  {
4143
4152
  className: cn(
4144
- "aui-ui-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
4153
+ "aui-ui-badge inline-flex w-fit shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium",
4145
4154
  BADGE_TONE[node.tone ?? "default"],
4146
4155
  node.className
4147
4156
  ),
@@ -5790,8 +5799,8 @@ var import_lucide_react9 = require("lucide-react");
5790
5799
 
5791
5800
  // src/design/control-surface.ts
5792
5801
  var CONTROL_SIZE = {
5793
- sm: "h-9 px-3",
5794
- default: "h-10 px-3"
5802
+ sm: "h-8 px-2.5",
5803
+ default: "h-9 px-3"
5795
5804
  };
5796
5805
  var CONTROL_SHAPE = {
5797
5806
  field: "rounded-lg",
@@ -5819,7 +5828,7 @@ var overlayListPanelClass = cn(
5819
5828
  overlaySurfaceClass,
5820
5829
  "overflow-hidden rounded-lg p-0 outline-hidden"
5821
5830
  );
5822
- var overlayItemClass = "relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground";
5831
+ var overlayItemClass = "relative flex cursor-default items-center gap-2 rounded-md px-2 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground";
5823
5832
 
5824
5833
  // src/chat/workforce-selector.tsx
5825
5834
  var import_jsx_runtime38 = require("react/jsx-runtime");
@@ -5944,8 +5953,8 @@ var SIDEBAR_MOBILE_PX = 272;
5944
5953
  var SIDEBAR_GAP_PX = 12;
5945
5954
  var SIDEBAR_CONTENT_GAP_PX = 8;
5946
5955
  var TOPBAR_GAP_PX = 8;
5947
- var TOPBAR_HEIGHT_PX = 48;
5948
- var PILL_HEIGHT_PX = 40;
5956
+ var TOPBAR_HEIGHT_PX = 44;
5957
+ var PILL_HEIGHT_PX = 36;
5949
5958
  var SIDEBAR_INSET_PX_EXPANDED = SIDEBAR_GAP_PX + SIDEBAR_WIDTH_PX + SIDEBAR_CONTENT_GAP_PX;
5950
5959
  var SIDEBAR_INSET_PX_COLLAPSED = SIDEBAR_GAP_PX + SIDEBAR_WIDTH_COLLAPSED_PX + SIDEBAR_CONTENT_GAP_PX;
5951
5960
  var px = (n) => `${n / 16}rem`;
@@ -6526,7 +6535,7 @@ function DropdownMenuCheckboxItem({
6526
6535
  import_radix_ui6.DropdownMenu.CheckboxItem,
6527
6536
  {
6528
6537
  "data-slot": "dropdown-menu-checkbox-item",
6529
- className: cn(overlayItemClass, "py-1.5 pr-2 pl-8", className),
6538
+ className: cn(overlayItemClass, "py-1 pr-2 pl-8", className),
6530
6539
  checked,
6531
6540
  ...props,
6532
6541
  children: [
@@ -6556,7 +6565,7 @@ function DropdownMenuRadioItem({
6556
6565
  import_radix_ui6.DropdownMenu.RadioItem,
6557
6566
  {
6558
6567
  "data-slot": "dropdown-menu-radio-item",
6559
- className: cn(overlayItemClass, "py-1.5 pr-2 pl-8", className),
6568
+ className: cn(overlayItemClass, "py-1 pr-2 pl-8", className),
6560
6569
  ...props,
6561
6570
  children: [
6562
6571
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_radix_ui6.DropdownMenu.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react10.CircleIcon, { className: "size-2 fill-current" }) }) }),
@@ -7810,10 +7819,10 @@ var pillSegmentedTrackFlushClass = cn(
7810
7819
  "h-[var(--studio-chrome-pill-height)] items-center gap-0.5 overflow-visible p-0.5"
7811
7820
  );
7812
7821
  var pillSegmentedSegmentClass = cn(
7813
- "relative flex items-center gap-1.5 rounded-full px-4 py-1.5 text-xs font-normal transition-colors"
7822
+ "relative flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-normal transition-colors"
7814
7823
  );
7815
7824
  var pillSegmentedFlushSegmentClass = cn(
7816
- "relative box-border inline-flex h-9 min-h-9 items-center justify-center gap-1.5 rounded-full px-3.5 py-0",
7825
+ "relative box-border inline-flex h-8 min-h-8 items-center justify-center gap-1.5 rounded-full px-3 py-0",
7817
7826
  "text-sm font-normal leading-tight transition-colors"
7818
7827
  );
7819
7828
  var pillSegmentedActiveIndicatorClass = cn(
@@ -8123,7 +8132,11 @@ var HOUSE_RULES = [
8123
8132
  {
8124
8133
  id: "compose-from-blocks",
8125
8134
  rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
8126
- why: "Slop appears the moment generation falls below the curated block layer."
8135
+ why: "Slop appears the moment generation falls below the curated block layer.",
8136
+ // "Should have used a block" is a judgement about absence, not a textual
8137
+ // pattern — no high-precision deterministic check exists, so this stays
8138
+ // prompt-only rather than risk false-positives blocking valid UIs.
8139
+ enforcement: "prompt-only"
8127
8140
  },
8128
8141
  {
8129
8142
  id: "use-kit-controls",
@@ -8204,7 +8217,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
8204
8217
  - 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\`.
8205
8218
 
8206
8219
  \`\`\`tsx
8207
- <AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
8220
+ <AppShell contentFill> {/* no global topbar / theme switch */}
8208
8221
  <Page fill> {/* headerless: omit title */}
8209
8222
  <TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
8210
8223
  </Page>
@@ -8224,7 +8237,7 @@ Presentational groups \u2014 import from the package root, not from these paths:
8224
8237
 
8225
8238
  | Folder | Components |
8226
8239
  |--------|------------|
8227
- | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
8240
+ | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`FilterDropdown\`, \`ChartPanel\` |
8228
8241
  | \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
8229
8242
  | \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
8230
8243
  | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
@@ -8293,6 +8306,7 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
8293
8306
  | \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
8294
8307
  | \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
8295
8308
  | \`FilterField\` | Optional label wrapper for a filter control inside \`FilterBar\` (severity, status, \u2026). Omit \`label\` for search-only fields. |
8309
+ | \`FilterDropdown\` | Single-button **multi-facet** filter popover for dense list/table views \u2014 **data-driven**: pass \`fields\` describing your **actual columns** (each \`{ id, label, type }\` where \`type\` is \`multiselect\` \\| \`text\` \\| \`daterange\` \\| \`numeric\`; \`multiselect\` takes \`options: [{ value, label, hint?, icon? }]\`). State is keyed by field \`id\` \u2014 controlled (\`value\` + \`onChange\`) or uncontrolled (\`defaultValue\`). Renders **removable active-filter pills** next to the trigger by default (\`showActiveChips\`); wire \`onChange\` to actually filter your rows. **Always derive \`fields\` from the table's columns/data; never ship the default example facets.** Use when one \`FilterBar\` row isn't enough. |
8296
8310
  | \`SearchInput\` | Filter field with consistent app styling. |
8297
8311
  | \`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\`). |
8298
8312
  | \`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\`. |
@@ -8307,6 +8321,8 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
8307
8321
 
8308
8322
  Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipContent\` chrome (see \`src/ui/chart.tsx\`). Series colors default to \`--chart-1..6\`; override those CSS tokens to rebrand every chart.
8309
8323
 
8324
+ > **React 19 requirement \u2014 do not hand-roll SVG charts to work around this.** recharts under React 19 crashes (\`Cannot assign to read only property 'lanes'\`, blank route) when \`immer\` resolves to **11.0.0**. The fix is a dependency override in the app's \`package.json\` \u2014 \`"overrides": { "immer": ">=11.0.1" }\` (Yarn: \`"resolutions"\`) \u2014 **not** a code change. Always keep using \`LineAreaChart\` / \`PieChart\` / \`ChartPanel\`; never replace them with raw SVG/CSS charts.
8325
+
8310
8326
  | Component | Use for |
8311
8327
  |-----------|---------|
8312
8328
  | \`LineAreaChart\` | Cartesian engine (shadcn-style chrome). Bar fills use theme gradients automatically. Props: \`data\`, \`xKey\`, \`series: [{ dataKey, label?, color? }]\`, \`variant\` (\`area\`\\|\`line\`\\|\`bar\`), \`orientation\` (\`horizontal\` for horizontal bars), \`stacked\`, \`curve\`, \`dots\`, \`gridLines\`, \`tooltipIndicator\`, \`layout\` (\`flush\` \u2014 hides axes by default; category + values on hover tooltip), \`showXAxis\` / \`showYAxis\` to opt back in, \`clipTicks\` (truncates long axis labels when axes are on), \`height\`, \`showLegend\`, \`formatX\`, \`formatValue\`, \`ariaLabel\`. |
@@ -8351,6 +8367,21 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
8351
8367
  | \`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. |
8352
8368
  | \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
8353
8369
 
8370
+ #### More \`/ui\` primitives (import from \`/ui\` or the package root)
8371
+
8372
+ These ship in the same design system but aren't re-exported from \`/app\`. Reach for them before hand-rolling \u2014 they're all dependency-free and on the shared tokens / control surface.
8373
+
8374
+ | Component | Use for |
8375
+ |-----------|---------|
8376
+ | \`Stepper\` | Ordered step indicator for wizards / onboarding (horizontal or vertical; complete / active / upcoming states). |
8377
+ | \`Rating\` | Star rating \u2014 interactive (keyboard + hover preview) or \`readOnly\`; controlled or uncontrolled. |
8378
+ | \`NumberField\` | Numeric input with \u2212/+ steppers on the control surface; clamps to \`min\`/\`max\`, steps by \`step\`. |
8379
+ | \`TagInput\` | Chips / token input; commits on Enter/comma, removes on Backspace, optional \`dedupe\`/\`max\`. |
8380
+ | \`AvatarGroup\` | Overlapping avatar stack with an optional \`+N\` overflow chip (\`max\`, \`spacing\`). |
8381
+ | \`CircularProgress\` | Lightweight SVG progress ring \u2014 determinate (optional center label) or indeterminate. |
8382
+ | \`CopyButton\` | Click-to-copy with a transient check confirmation; icon-only or with a label. |
8383
+ | \`Snippet\` | Single-line code / command on the elevated surface with a built-in copy button. |
8384
+
8354
8385
  Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
8355
8386
 
8356
8387
  ### Block recipes \u2014 compose these (don't clone wholesale)
@@ -8417,6 +8448,7 @@ import {
8417
8448
  DataTable,
8418
8449
  FilterBar,
8419
8450
  FilterField,
8451
+ FilterDropdown,
8420
8452
  AlertCard,
8421
8453
  CatalogCard,
8422
8454
  } from "@timbal-ai/timbal-react/app";
@@ -8471,6 +8503,8 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
8471
8503
  var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
8472
8504
  var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
8473
8505
  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/;
8506
+ var TREND_CONTEXT_RE = /\b(?:trend|delta|TrendingUp|TrendingDown|ArrowUp|ArrowDown|ArrowUpRight|ArrowDownRight|MoveUp|MoveDown)\b|[+\-]\d+(?:\.\d+)?\s*%/;
8507
+ var TREND_COLOR_RE = /\b(?:text|bg|border)-(?:success|destructive|emerald|green|lime|teal|red|rose|orange|amber)(?:-\d{2,3})?(?:\/\d{1,3})?\b/;
8474
8508
  var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
8475
8509
  function stripVariants(util) {
8476
8510
  return util.replace(/^(?:[a-z-]+:)*/, "");
@@ -8514,6 +8548,16 @@ function lintGeneratedUi(source, options = {}) {
8514
8548
  if (cardMatch) {
8515
8549
  const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
8516
8550
  if (!isSelfClosing) {
8551
+ if (openCards.length > 0) {
8552
+ const parentCard = openCards[openCards.length - 1];
8553
+ findings.push({
8554
+ rule: "no-card-in-card",
8555
+ severity: "warn",
8556
+ line: lineNo,
8557
+ message: `Card inside card. A <${cardMatch[1]}> is nested inside the <${parentCard.type}> opened on L${parentCard.line}. Double borders/shadows add no information \u2014 group with spacing or a <Section> instead.`,
8558
+ snippet: line.trim().slice(0, 120)
8559
+ });
8560
+ }
8517
8561
  openCards.push({ type: cardMatch[1], line: lineNo });
8518
8562
  }
8519
8563
  }
@@ -8586,6 +8630,15 @@ function lintGeneratedUi(source, options = {}) {
8586
8630
  snippet: line.trim().slice(0, 120)
8587
8631
  });
8588
8632
  }
8633
+ if (TREND_CONTEXT_RE.test(line) && TREND_COLOR_RE.test(line)) {
8634
+ findings.push({
8635
+ rule: "neutral-trend",
8636
+ severity: "warn",
8637
+ line: lineNo,
8638
+ message: "Colored trend indicator. House style: don't tint deltas green/red on every metric \u2014 show a trend only when the change is the point, and keep it muted (text-muted-foreground).",
8639
+ snippet: line.trim().slice(0, 120)
8640
+ });
8641
+ }
8589
8642
  if (BOLD_VALUE_RE.test(line)) {
8590
8643
  findings.push({
8591
8644
  rule: "bold-metric",
@@ -9612,30 +9665,36 @@ var Sparkline = ({
9612
9665
  height = 28,
9613
9666
  strokeWidth = 1.5,
9614
9667
  className,
9615
- ariaLabel = "Trend"
9668
+ ariaLabel = "Trend",
9669
+ interactive = false,
9670
+ labels,
9671
+ formatValue
9616
9672
  }) => {
9617
9673
  const uid = (0, import_react57.useId)();
9674
+ const containerRef = (0, import_react57.useRef)(null);
9675
+ const [activeIndex, setActiveIndex] = (0, import_react57.useState)(null);
9618
9676
  const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
9619
9677
  if (values.length === 0) {
9620
9678
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: cn("inline-block", className), style: { width, height } });
9621
9679
  }
9622
- const pad = strokeWidth + 1;
9680
+ const padX = 0;
9681
+ const padY = strokeWidth + 1;
9623
9682
  const min = Math.min(...values);
9624
9683
  const max = Math.max(...values);
9625
9684
  const range = max - min || 1;
9626
- const innerW = width - pad * 2;
9627
- const innerH = height - pad * 2;
9685
+ const innerW = width - padX * 2;
9686
+ const innerH = height - padY * 2;
9628
9687
  const points = values.map((v, i) => ({
9629
- x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
9630
- y: pad + innerH - (v - min) / range * innerH
9688
+ x: padX + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
9689
+ y: padY + innerH - (v - min) / range * innerH
9631
9690
  }));
9632
- return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
9691
+ const svg = /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
9633
9692
  "svg",
9634
9693
  {
9635
9694
  width,
9636
9695
  height,
9637
9696
  viewBox: `0 0 ${width} ${height}`,
9638
- className: cn("block", className),
9697
+ className: cn("block", interactive ? "h-full w-full" : className),
9639
9698
  role: "img",
9640
9699
  "aria-label": ariaLabel,
9641
9700
  preserveAspectRatio: "none",
@@ -9645,7 +9704,7 @@ var Sparkline = ({
9645
9704
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
9646
9705
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
9647
9706
  ] }) }),
9648
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
9707
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("path", { d: monotoneAreaPath(points, height - padY), fill: `url(#${uid}-spark)` })
9649
9708
  ] }),
9650
9709
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
9651
9710
  "path",
@@ -9657,7 +9716,81 @@ var Sparkline = ({
9657
9716
  strokeLinecap: "round",
9658
9717
  strokeLinejoin: "round"
9659
9718
  }
9660
- )
9719
+ ),
9720
+ interactive && activeIndex != null && points[activeIndex] ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
9721
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
9722
+ "line",
9723
+ {
9724
+ x1: points[activeIndex].x,
9725
+ x2: points[activeIndex].x,
9726
+ y1: 0,
9727
+ y2: height,
9728
+ stroke: color,
9729
+ strokeWidth: 1,
9730
+ strokeOpacity: 0.3,
9731
+ vectorEffect: "non-scaling-stroke"
9732
+ }
9733
+ ),
9734
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
9735
+ "circle",
9736
+ {
9737
+ cx: points[activeIndex].x,
9738
+ cy: points[activeIndex].y,
9739
+ r: 2.75,
9740
+ fill: color,
9741
+ stroke: "var(--background, #fff)",
9742
+ strokeWidth: 1.5,
9743
+ vectorEffect: "non-scaling-stroke"
9744
+ }
9745
+ )
9746
+ ] }) : null
9747
+ ]
9748
+ }
9749
+ );
9750
+ if (!interactive) return svg;
9751
+ const onMove = (e) => {
9752
+ const rect = e.currentTarget.getBoundingClientRect();
9753
+ if (rect.width === 0) return;
9754
+ const fraction = (e.clientX - rect.left) / rect.width;
9755
+ const index = Math.max(
9756
+ 0,
9757
+ Math.min(values.length - 1, Math.round(fraction * (values.length - 1)))
9758
+ );
9759
+ setActiveIndex(index);
9760
+ };
9761
+ const active = activeIndex != null ? points[activeIndex] : null;
9762
+ const formattedValue = activeIndex != null ? formatValue ? formatValue(values[activeIndex], activeIndex) : formatCompact(values[activeIndex]) : null;
9763
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
9764
+ "span",
9765
+ {
9766
+ ref: containerRef,
9767
+ className: cn("relative block touch-none", className),
9768
+ style: { width: "100%", height: "100%" },
9769
+ onPointerMove: onMove,
9770
+ onPointerLeave: () => setActiveIndex(null),
9771
+ children: [
9772
+ svg,
9773
+ active ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
9774
+ "span",
9775
+ {
9776
+ "aria-hidden": true,
9777
+ className: cn(
9778
+ "pointer-events-none absolute z-30 -translate-x-1/2 -translate-y-full whitespace-nowrap",
9779
+ "rounded-xl border px-3 py-2 text-[11px] font-medium leading-none tabular-nums shadow-[0_12px_40px_-10px_rgba(0,0,0,0.55)]",
9780
+ "border-white/10 bg-gradient-to-b from-neutral-800 to-neutral-950 text-white",
9781
+ "dark:border-black/10 dark:from-white dark:to-neutral-100 dark:text-neutral-900"
9782
+ ),
9783
+ style: {
9784
+ left: `${active.x / width * 100}%`,
9785
+ top: `${active.y / height * 100}%`,
9786
+ marginTop: -8
9787
+ },
9788
+ children: [
9789
+ labels?.[activeIndex] != null ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "mr-1.5 text-neutral-300 dark:text-neutral-500", children: labels[activeIndex] }) : null,
9790
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: formattedValue })
9791
+ ]
9792
+ }
9793
+ ) : null
9661
9794
  ]
9662
9795
  }
9663
9796
  );
@@ -9675,6 +9808,15 @@ var inlineTrendToneClass = {
9675
9808
  down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
9676
9809
  neutral: "text-muted-foreground/80"
9677
9810
  };
9811
+ var sparklineToneColor = {
9812
+ up: "var(--primary, #3b82f6)",
9813
+ down: "var(--destructive, #f43f5e)",
9814
+ neutral: "var(--muted-foreground, #64748b)"
9815
+ };
9816
+ var sparklineBandBleed = {
9817
+ default: "-mx-4 -mb-3 h-10",
9818
+ compact: "-mx-3 -mb-2 h-8"
9819
+ };
9678
9820
  var activeToneClass = {
9679
9821
  default: "bg-foreground dark:bg-white",
9680
9822
  primary: "bg-primary",
@@ -9704,8 +9846,10 @@ var MetricTile = ({
9704
9846
  ariaLabel,
9705
9847
  className
9706
9848
  }) => {
9849
+ const density = useAppDensity();
9707
9850
  const metricTileBaseClass = useAppDensityClass("metricTile");
9708
9851
  const hasSparkline = Boolean(sparkline || sparklineData);
9852
+ const bandBleed = sparklineBandBleed[density === "compact" ? "compact" : "default"];
9709
9853
  const content = /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_jsx_runtime61.Fragment, { children: [
9710
9854
  active ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
9711
9855
  "span",
@@ -9717,17 +9861,6 @@ var MetricTile = ({
9717
9861
  )
9718
9862
  }
9719
9863
  ) : null,
9720
- hasSparkline ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("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__ */ (0, import_jsx_runtime61.jsx)(
9721
- Sparkline,
9722
- {
9723
- data: sparklineData,
9724
- width: 160,
9725
- height: 36,
9726
- className: "w-full h-full",
9727
- color: trendTone === "up" ? "var(--primary, #3b82f6)" : trendTone === "down" ? "var(--destructive, #f43f5e)" : "var(--muted-foreground, #64748b)",
9728
- ...sparklineConfig
9729
- }
9730
- ) }) : null,
9731
9864
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
9732
9865
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
9733
9866
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "flex items-center gap-2", children: [
@@ -9746,7 +9879,28 @@ var MetricTile = ({
9746
9879
  }
9747
9880
  ) : null
9748
9881
  ] })
9749
- ] })
9882
+ ] }),
9883
+ hasSparkline ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
9884
+ "div",
9885
+ {
9886
+ className: cn(
9887
+ "relative z-10 mt-2",
9888
+ bandBleed
9889
+ ),
9890
+ children: sparkline ?? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
9891
+ Sparkline,
9892
+ {
9893
+ data: sparklineData,
9894
+ width: 160,
9895
+ height: 40,
9896
+ interactive: true,
9897
+ className: "h-full w-full opacity-90",
9898
+ color: sparklineToneColor[trendTone],
9899
+ ...sparklineConfig
9900
+ }
9901
+ )
9902
+ }
9903
+ ) : null
9750
9904
  ] });
9751
9905
  const divider = showDivider ? metricCellDividerClass : void 0;
9752
9906
  if (onSelect) {
@@ -10447,7 +10601,7 @@ var StatusBadge = ({
10447
10601
  "span",
10448
10602
  {
10449
10603
  className: cn(
10450
- "aui-app-status-badge inline-flex items-center gap-1.5 rounded-full px-2 py-0.5",
10604
+ "aui-app-status-badge inline-flex w-fit shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5",
10451
10605
  "text-xs font-medium leading-none ring-1 ring-inset",
10452
10606
  statusBadgeToneClass[tone],
10453
10607
  className
@@ -10861,7 +11015,7 @@ function CopyButton({
10861
11015
  "inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium text-muted-foreground transition-colors",
10862
11016
  "hover:bg-accent hover:text-foreground data-[copied=true]:text-emerald-600 dark:data-[copied=true]:text-emerald-400",
10863
11017
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
10864
- children ? "h-8 px-2" : "size-8",
11018
+ children ? "h-7 px-1.5" : "size-7",
10865
11019
  className
10866
11020
  ),
10867
11021
  ...props,
@@ -11592,7 +11746,7 @@ var FilterField = ({
11592
11746
 
11593
11747
  // src/app/data/FilterDropdown.tsx
11594
11748
  var import_react73 = require("react");
11595
- var import_lucide_react24 = require("lucide-react");
11749
+ var import_lucide_react25 = require("lucide-react");
11596
11750
 
11597
11751
  // src/ui/checkbox.tsx
11598
11752
  var import_radix_ui7 = require("radix-ui");
@@ -11625,23 +11779,178 @@ function Checkbox({
11625
11779
  );
11626
11780
  }
11627
11781
 
11628
- // src/ui/popover.tsx
11782
+ // src/ui/select.tsx
11629
11783
  var import_radix_ui8 = require("radix-ui");
11784
+ var import_lucide_react24 = require("lucide-react");
11630
11785
  var import_jsx_runtime105 = require("react/jsx-runtime");
11786
+ function Select({
11787
+ ...props
11788
+ }) {
11789
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.Root, { "data-slot": "select", ...props });
11790
+ }
11791
+ function SelectGroup({
11792
+ ...props
11793
+ }) {
11794
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.Group, { "data-slot": "select-group", ...props });
11795
+ }
11796
+ function SelectValue({
11797
+ ...props
11798
+ }) {
11799
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.Value, { "data-slot": "select-value", ...props });
11800
+ }
11801
+ function SelectTrigger({
11802
+ className,
11803
+ size = "default",
11804
+ children,
11805
+ ...props
11806
+ }) {
11807
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(
11808
+ import_radix_ui8.Select.Trigger,
11809
+ {
11810
+ "data-slot": "select-trigger",
11811
+ "data-size": size,
11812
+ className: cn(
11813
+ controlClass({ size }),
11814
+ "flex w-fit items-center justify-between gap-2 whitespace-nowrap *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
11815
+ className
11816
+ ),
11817
+ ...props,
11818
+ children: [
11819
+ children,
11820
+ /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_lucide_react24.ChevronDownIcon, { className: "size-4 opacity-50" }) })
11821
+ ]
11822
+ }
11823
+ );
11824
+ }
11825
+ function SelectContent({
11826
+ className,
11827
+ children,
11828
+ position = "popper",
11829
+ ...props
11830
+ }) {
11831
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(
11832
+ import_radix_ui8.Select.Content,
11833
+ {
11834
+ "data-slot": "select-content",
11835
+ className: cn(
11836
+ overlayListPanelClass,
11837
+ "relative max-h-[var(--radix-select-content-available-height)] min-w-[8rem] origin-[var(--radix-select-content-transform-origin)] overflow-x-hidden overflow-y-auto",
11838
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
11839
+ className
11840
+ ),
11841
+ position,
11842
+ ...props,
11843
+ children: [
11844
+ /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(SelectScrollUpButton, {}),
11845
+ /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
11846
+ import_radix_ui8.Select.Viewport,
11847
+ {
11848
+ className: cn(
11849
+ "p-1",
11850
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
11851
+ ),
11852
+ children
11853
+ }
11854
+ ),
11855
+ /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(SelectScrollDownButton, {})
11856
+ ]
11857
+ }
11858
+ ) });
11859
+ }
11860
+ function SelectLabel({
11861
+ className,
11862
+ ...props
11863
+ }) {
11864
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
11865
+ import_radix_ui8.Select.Label,
11866
+ {
11867
+ "data-slot": "select-label",
11868
+ className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", className),
11869
+ ...props
11870
+ }
11871
+ );
11872
+ }
11873
+ function SelectItem({
11874
+ className,
11875
+ children,
11876
+ ...props
11877
+ }) {
11878
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(
11879
+ import_radix_ui8.Select.Item,
11880
+ {
11881
+ "data-slot": "select-item",
11882
+ className: cn(
11883
+ overlayItemClass,
11884
+ "w-full py-1 pr-8 pl-2 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
11885
+ className
11886
+ ),
11887
+ ...props,
11888
+ children: [
11889
+ /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_lucide_react24.CheckIcon, { className: "size-4" }) }) }),
11890
+ /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Select.ItemText, { children })
11891
+ ]
11892
+ }
11893
+ );
11894
+ }
11895
+ function SelectSeparator({
11896
+ className,
11897
+ ...props
11898
+ }) {
11899
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
11900
+ import_radix_ui8.Select.Separator,
11901
+ {
11902
+ "data-slot": "select-separator",
11903
+ className: cn("-mx-1 my-1 h-px bg-border", className),
11904
+ ...props
11905
+ }
11906
+ );
11907
+ }
11908
+ function SelectScrollUpButton({
11909
+ className,
11910
+ ...props
11911
+ }) {
11912
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
11913
+ import_radix_ui8.Select.ScrollUpButton,
11914
+ {
11915
+ "data-slot": "select-scroll-up-button",
11916
+ className: cn("flex cursor-default items-center justify-center py-1", className),
11917
+ ...props,
11918
+ children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_lucide_react24.ChevronUpIcon, { className: "size-4" })
11919
+ }
11920
+ );
11921
+ }
11922
+ function SelectScrollDownButton({
11923
+ className,
11924
+ ...props
11925
+ }) {
11926
+ return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
11927
+ import_radix_ui8.Select.ScrollDownButton,
11928
+ {
11929
+ "data-slot": "select-scroll-down-button",
11930
+ className: cn("flex cursor-default items-center justify-center py-1", className),
11931
+ ...props,
11932
+ children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_lucide_react24.ChevronDownIcon, { className: "size-4" })
11933
+ }
11934
+ );
11935
+ }
11936
+
11937
+ // src/ui/popover.tsx
11938
+ var import_radix_ui9 = require("radix-ui");
11939
+ var import_jsx_runtime106 = require("react/jsx-runtime");
11631
11940
  function Popover({
11632
11941
  ...props
11633
11942
  }) {
11634
- return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Popover.Root, { "data-slot": "popover", ...props });
11943
+ return /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_radix_ui9.Popover.Root, { "data-slot": "popover", ...props });
11635
11944
  }
11636
11945
  function PopoverTrigger({
11637
11946
  ...props
11638
11947
  }) {
11639
- return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
11948
+ return /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_radix_ui9.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
11640
11949
  }
11641
11950
  function PopoverAnchor({
11642
11951
  ...props
11643
11952
  }) {
11644
- return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Popover.Anchor, { "data-slot": "popover-anchor", ...props });
11953
+ return /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_radix_ui9.Popover.Anchor, { "data-slot": "popover-anchor", ...props });
11645
11954
  }
11646
11955
  function PopoverContent({
11647
11956
  className,
@@ -11650,8 +11959,8 @@ function PopoverContent({
11650
11959
  variant = "default",
11651
11960
  ...props
11652
11961
  }) {
11653
- return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_radix_ui8.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
11654
- import_radix_ui8.Popover.Content,
11962
+ return /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_radix_ui9.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11963
+ import_radix_ui9.Popover.Content,
11655
11964
  {
11656
11965
  "data-slot": "popover-content",
11657
11966
  "data-variant": variant,
@@ -11663,7 +11972,7 @@ function PopoverContent({
11663
11972
  "min-w-[8rem] origin-[var(--radix-popover-content-transform-origin)]"
11664
11973
  ) : cn(
11665
11974
  overlaySurfaceClass,
11666
- "w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-4 outline-hidden"
11975
+ "w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-3 outline-hidden"
11667
11976
  ),
11668
11977
  className
11669
11978
  ),
@@ -11673,499 +11982,471 @@ function PopoverContent({
11673
11982
  }
11674
11983
 
11675
11984
  // src/app/data/FilterDropdown.tsx
11676
- var import_jsx_runtime106 = require("react/jsx-runtime");
11677
- var DEFAULT_CONTACTS = [
11678
- { id: "1", name: "Pedro Olivares Sanchez", email: "polivares@timbal.ai", initials: "PE" },
11679
- { id: "2", name: "John Doe", email: "john@example.com", initials: "JD" },
11680
- { id: "3", name: "Sarah Smith", email: "sarah@example.com", initials: "SS" }
11985
+ var import_jsx_runtime107 = require("react/jsx-runtime");
11986
+ var DEFAULT_PRESETS = [
11987
+ { id: "last_7_days", label: "Last 7 days" },
11988
+ { id: "last_30_days", label: "Last 30 days" },
11989
+ { id: "last_90_days", label: "Last 90 days" },
11990
+ { id: "this_month", label: "This month" },
11991
+ { id: "this_year", label: "This year" },
11992
+ { id: "custom", label: "Custom range" }
11681
11993
  ];
11682
- var OPERATORS = [
11683
- { id: "greater_than", label: "Greater than..." },
11684
- { id: "less_than", label: "Less than..." },
11685
- { id: "equals", label: "Equals..." }
11994
+ var DEFAULT_OPERATORS = [
11995
+ { id: "gt", label: "Greater than" },
11996
+ { id: "lt", label: "Less than" },
11997
+ { id: "eq", label: "Equals" }
11686
11998
  ];
11999
+ function asArray(v) {
12000
+ return Array.isArray(v) ? v : [];
12001
+ }
12002
+ function asText(v) {
12003
+ return typeof v === "string" ? v : "";
12004
+ }
12005
+ function asDate(v) {
12006
+ return v && !Array.isArray(v) && typeof v === "object" && "preset" in v ? v : { preset: null };
12007
+ }
12008
+ function asNumeric(v) {
12009
+ return v && !Array.isArray(v) && typeof v === "object" && "operator" in v ? v : { operator: "gt", value: "" };
12010
+ }
12011
+ var OPERATOR_SYMBOL = {
12012
+ gt: ">",
12013
+ lt: "<",
12014
+ eq: "="
12015
+ };
11687
12016
  function FilterDropdown({
11688
- filters,
11689
- onFiltersChange,
11690
- initialFilters,
11691
- contacts = DEFAULT_CONTACTS,
12017
+ fields,
12018
+ value,
12019
+ defaultValue,
12020
+ onChange,
12021
+ label = "Filter",
12022
+ align = "start",
12023
+ showActiveChips = true,
11692
12024
  className
11693
12025
  }) {
11694
12026
  const [isOpen, setIsOpen] = (0, import_react73.useState)(false);
11695
- const [activeMenu, setActiveMenu] = (0, import_react73.useState)("contact");
12027
+ const [activeId, setActiveId] = (0, import_react73.useState)(fields[0]?.id ?? null);
11696
12028
  const [isMobile, setIsMobile] = (0, import_react73.useState)(false);
12029
+ const isControlled = value !== void 0;
12030
+ const [internal, setInternal] = (0, import_react73.useState)(defaultValue ?? {});
12031
+ const values = isControlled ? value : internal;
11697
12032
  (0, import_react73.useEffect)(() => {
11698
12033
  const checkMobile = () => setIsMobile(window.innerWidth < 768);
11699
12034
  checkMobile();
11700
12035
  window.addEventListener("resize", checkMobile);
11701
12036
  return () => window.removeEventListener("resize", checkMobile);
11702
12037
  }, []);
11703
- const [selectedContacts, setSelectedContacts] = (0, import_react73.useState)(
11704
- filters?.contacts ?? initialFilters?.contacts ?? []
11705
- );
11706
- const [walletInput, setWalletInput] = (0, import_react73.useState)(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
11707
- const [appliedWallet, setAppliedWallet] = (0, import_react73.useState)(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
11708
- const [selectedDatePreset, setSelectedDatePreset] = (0, import_react73.useState)(
11709
- filters?.lastInvoiceDate ?? initialFilters?.lastInvoiceDate ?? null
11710
- );
11711
- const [customDateFrom, setCustomDateFrom] = (0, import_react73.useState)(
11712
- filters?.customDateRange?.from ?? initialFilters?.customDateRange?.from ?? ""
11713
- );
11714
- const [customDateTo, setCustomDateTo] = (0, import_react73.useState)(
11715
- filters?.customDateRange?.to ?? initialFilters?.customDateRange?.to ?? ""
11716
- );
11717
- const [ltvOperator, setLtvOperator] = (0, import_react73.useState)(
11718
- filters?.lifetimeValue?.operator ?? initialFilters?.lifetimeValue?.operator ?? "greater_than"
11719
- );
11720
- const [ltvValue, setLtvValue] = (0, import_react73.useState)(filters?.lifetimeValue?.value ?? initialFilters?.lifetimeValue?.value ?? "");
11721
- const [isLtvOperatorOpen, setLtvOperatorOpen] = (0, import_react73.useState)(false);
11722
- const [outstandingOperator, setOutstandingOperator] = (0, import_react73.useState)(
11723
- filters?.outstanding?.operator ?? initialFilters?.outstanding?.operator ?? "greater_than"
11724
- );
11725
- const [outstandingValue, setOutstandingValue] = (0, import_react73.useState)(filters?.outstanding?.value ?? initialFilters?.outstanding?.value ?? "");
11726
- const [isOutstandingOperatorOpen, setOutstandingOperatorOpen] = (0, import_react73.useState)(false);
11727
12038
  (0, import_react73.useEffect)(() => {
11728
- if (filters) {
11729
- setSelectedContacts(filters.contacts ?? []);
11730
- setWalletInput(filters.walletAddress ?? "");
11731
- setAppliedWallet(filters.walletAddress ?? "");
11732
- setSelectedDatePreset(filters.lastInvoiceDate ?? null);
11733
- setCustomDateFrom(filters.customDateRange?.from ?? "");
11734
- setCustomDateTo(filters.customDateRange?.to ?? "");
11735
- setLtvOperator(filters.lifetimeValue?.operator ?? "greater_than");
11736
- setLtvValue(filters.lifetimeValue?.value ?? "");
11737
- setOutstandingOperator(filters.outstanding?.operator ?? "greater_than");
11738
- setOutstandingValue(filters.outstanding?.value ?? "");
11739
- }
11740
- }, [filters]);
11741
- const [contactSearch, setContactSearch] = (0, import_react73.useState)("");
11742
- const [walletSearch, setWalletSearch] = (0, import_react73.useState)("");
11743
- const refDate = (0, import_react73.useMemo)(() => new Date(2026, 5, 26), []);
11744
- const presets = (0, import_react73.useMemo)(() => {
11745
- const year = refDate.getFullYear();
11746
- const month = refDate.getMonth();
11747
- const lastMonthDate = new Date(year, month - 1, 1);
11748
- const lastMonthLabel = lastMonthDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
11749
- const thisMonthLabel = refDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
11750
- const thisQuarter = Math.floor(month / 3) + 1;
11751
- const thisQuarterLabel = `Q${thisQuarter} ${year}`;
11752
- const lastQuarter = thisQuarter === 1 ? 4 : thisQuarter - 1;
11753
- const lastQuarterYear = thisQuarter === 1 ? year - 1 : year;
11754
- const lastQuarterLabel = `Q${lastQuarter} ${lastQuarterYear}`;
11755
- const thisYearLabel = `${year}`;
11756
- const last30 = new Date(refDate);
11757
- last30.setDate(refDate.getDate() - 30);
11758
- const formatDate = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
11759
- const last30Label = `${formatDate(last30)} - ${formatDate(refDate)}`;
11760
- const last90 = new Date(refDate);
11761
- last90.setDate(refDate.getDate() - 90);
11762
- const last90Label = `${formatDate(last90)} - ${formatDate(refDate)}`;
11763
- return [
11764
- { id: "last_month", label: "Last month", date: lastMonthLabel },
11765
- { id: "this_month", label: "This month", date: thisMonthLabel },
11766
- { id: "this_quarter", label: "This quarter", date: thisQuarterLabel },
11767
- { id: "last_quarter", label: "Last quarter", date: lastQuarterLabel },
11768
- { id: "this_year", label: "This year", date: thisYearLabel },
11769
- { id: "last_30_days", label: "Last 30 days", date: last30Label },
11770
- { id: "last_90_days", label: "Last 90 days", date: last90Label },
11771
- { id: "custom", label: "Custom range", date: "" }
11772
- ];
11773
- }, [refDate]);
11774
- const filteredContacts = (0, import_react73.useMemo)(() => {
11775
- if (!contactSearch) return contacts;
11776
- const query = contactSearch.toLowerCase();
11777
- return contacts.filter(
11778
- (c) => c.name.toLowerCase().includes(query) || c.email.toLowerCase().includes(query)
11779
- );
11780
- }, [contacts, contactSearch]);
11781
- const handleContactToggle = (contactName) => {
11782
- const next = selectedContacts.includes(contactName) ? selectedContacts.filter((name) => name !== contactName) : [...selectedContacts, contactName];
11783
- setSelectedContacts(next);
11784
- notifyChanges({ contacts: next });
11785
- setIsOpen(false);
11786
- };
11787
- const handleWalletApply = () => {
11788
- setAppliedWallet(walletInput);
11789
- notifyChanges({ walletAddress: walletInput });
11790
- setIsOpen(false);
11791
- };
11792
- const handleWalletClear = () => {
11793
- setWalletInput("");
11794
- setAppliedWallet("");
11795
- notifyChanges({ walletAddress: "" });
11796
- setIsOpen(false);
11797
- };
11798
- const handleDatePresetSelect = (presetId) => {
11799
- setSelectedDatePreset(presetId);
11800
- if (presetId !== "custom") {
11801
- notifyChanges({ lastInvoiceDate: presetId, customDateRange: void 0 });
11802
- setIsOpen(false);
11803
- }
11804
- };
11805
- const handleCustomDateApply = () => {
11806
- notifyChanges({
11807
- lastInvoiceDate: "custom",
11808
- customDateRange: { from: customDateFrom, to: customDateTo }
11809
- });
11810
- setIsOpen(false);
11811
- };
11812
- const handleLtvApply = () => {
11813
- notifyChanges({
11814
- lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null
11815
- });
11816
- setIsOpen(false);
11817
- };
11818
- const handleLtvClear = () => {
11819
- setLtvValue("");
11820
- notifyChanges({ lifetimeValue: null });
11821
- setIsOpen(false);
11822
- };
11823
- const handleOutstandingApply = () => {
11824
- notifyChanges({
11825
- outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null
11826
- });
11827
- setIsOpen(false);
12039
+ if (!fields.some((f) => f.id === activeId)) {
12040
+ setActiveId(fields[0]?.id ?? null);
12041
+ }
12042
+ }, [fields, activeId]);
12043
+ const commit = (id, next) => {
12044
+ const merged = { ...values, [id]: next };
12045
+ if (!isControlled) setInternal(merged);
12046
+ onChange?.(merged);
11828
12047
  };
11829
- const handleOutstandingClear = () => {
11830
- setOutstandingValue("");
11831
- notifyChanges({ outstanding: null });
11832
- setIsOpen(false);
12048
+ const clearAll = () => {
12049
+ if (!isControlled) setInternal({});
12050
+ onChange?.({});
11833
12051
  };
11834
- const notifyChanges = (overrides) => {
11835
- const current = {
11836
- contacts: selectedContacts,
11837
- walletAddress: appliedWallet,
11838
- lastInvoiceDate: selectedDatePreset,
11839
- customDateRange: customDateFrom || customDateTo ? { from: customDateFrom, to: customDateTo } : void 0,
11840
- lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null,
11841
- outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null,
11842
- ...overrides
11843
- };
11844
- onFiltersChange?.(current);
11845
- };
11846
- const menuItems = [
11847
- { id: "contact", label: "Contact", icon: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.UserIcon, { className: "size-4" }) },
11848
- { id: "wallet", label: "Wallet address", icon: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.WalletIcon, { className: "size-4" }) },
11849
- { id: "date", label: "Last invoice date", icon: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.CalendarIcon, { className: "size-4" }) },
11850
- { id: "ltv", label: "Lifetime value", icon: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.TrendingUpIcon, { className: "size-4" }) },
11851
- { id: "outstanding", label: "Outstanding", icon: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.CircleDollarSignIcon, { className: "size-4" }) }
11852
- ];
11853
- const activeIdx = menuItems.findIndex((item) => item.id === activeMenu);
11854
- return /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("div", { className: cn("inline-block", className), children: /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
11855
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11856
- Button,
11857
- {
11858
- variant: "outline",
11859
- size: "sm",
11860
- shape: "pill",
11861
- className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
11862
- iconLeading: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.ListFilterIcon, { className: "size-4" }),
11863
- children: "Filter"
12052
+ const activeIdx = fields.findIndex((f) => f.id === activeId);
12053
+ const activeField = activeIdx >= 0 ? fields[activeIdx] : void 0;
12054
+ const chips = [];
12055
+ for (const field of fields) {
12056
+ const v = values[field.id];
12057
+ if (field.type === "multiselect") {
12058
+ const selected = asArray(v);
12059
+ for (const optionValue of selected) {
12060
+ const opt = field.options?.find((o) => o.value === optionValue);
12061
+ chips.push({
12062
+ id: `${field.id}:${optionValue}`,
12063
+ label: `${field.label}: ${opt?.label ?? optionValue}`,
12064
+ remove: () => commit(field.id, selected.filter((x) => x !== optionValue))
12065
+ });
11864
12066
  }
11865
- ) }),
11866
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11867
- PopoverContent,
11868
- {
11869
- variant: "list",
11870
- align: "start",
11871
- className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
11872
- children: /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("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: [
11873
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: menuItems.map((item) => {
11874
- const isActive = activeMenu === item.id;
11875
- return /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
11876
- "button",
11877
- {
11878
- type: "button",
11879
- className: cn(
11880
- "flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
11881
- isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
11882
- ),
11883
- onMouseEnter: () => !isMobile && setActiveMenu(item.id),
11884
- onClick: () => setActiveMenu(item.id),
11885
- children: [
11886
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("span", { className: "flex items-center gap-2", children: [
11887
- item.icon,
11888
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { children: item.label })
11889
- ] }),
11890
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
11891
- ]
11892
- },
11893
- item.id
11894
- );
11895
- }) }),
11896
- activeMenu && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
11897
- "div",
11898
- {
11899
- 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",
11900
- style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
11901
- children: [
11902
- activeMenu === "contact" && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
11903
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "relative flex items-center", children: [
11904
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.SearchIcon, { className: "absolute left-2.5 size-4 text-muted-foreground/60" }),
11905
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11906
- "input",
11907
- {
11908
- type: "text",
11909
- placeholder: "Search by name or email...",
11910
- value: contactSearch,
11911
- onChange: (e) => setContactSearch(e.target.value),
11912
- 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"
11913
- }
11914
- )
11915
- ] }),
11916
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("div", { className: "max-h-48 overflow-y-auto flex flex-col gap-1 pr-1", children: filteredContacts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No contacts found" }) : filteredContacts.map((contact) => {
11917
- const isChecked = selectedContacts.includes(contact.name);
11918
- return /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
11919
- "label",
11920
- {
11921
- className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 hover:bg-muted/50 transition-colors",
11922
- children: [
11923
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11924
- Checkbox,
11925
- {
11926
- checked: isChecked,
11927
- onCheckedChange: () => handleContactToggle(contact.name)
11928
- }
11929
- ),
11930
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Avatar, { variant: "secondary", children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(AvatarFallback, { children: contact.initials }) }),
11931
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { className: "text-sm font-medium text-foreground", children: contact.name })
11932
- ]
11933
- },
11934
- contact.id
11935
- );
11936
- }) })
11937
- ] }),
11938
- activeMenu === "wallet" && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
11939
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("div", { className: "relative flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11940
- "input",
11941
- {
11942
- type: "text",
11943
- placeholder: "Search by wallet...",
11944
- value: walletInput,
11945
- onChange: (e) => setWalletInput(e.target.value),
11946
- className: "w-full rounded-lg border border-border bg-background px-3 py-1.5 text-sm outline-none placeholder:text-muted-foreground/60"
11947
- }
11948
- ) }),
11949
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
11950
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11951
- Button,
11952
- {
11953
- variant: "ghost",
11954
- size: "sm",
11955
- onClick: handleWalletClear,
11956
- className: "text-muted-foreground hover:text-foreground h-8 px-3",
11957
- children: "Clear"
11958
- }
11959
- ),
11960
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11961
- Button,
11962
- {
11963
- color: "primary",
11964
- size: "sm",
11965
- onClick: handleWalletApply,
11966
- className: "h-8 px-3",
11967
- children: "Apply"
11968
- }
11969
- )
11970
- ] })
11971
- ] }),
11972
- activeMenu === "date" && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex flex-col gap-1", children: [
11973
- presets.map((preset) => {
11974
- const isSelected = selectedDatePreset === preset.id;
11975
- return /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
11976
- "button",
11977
- {
11978
- type: "button",
11979
- onClick: () => handleDatePresetSelect(preset.id),
11980
- className: cn(
11981
- "flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-sm text-left transition-colors outline-none",
11982
- isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
11983
- ),
11984
- children: [
11985
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { children: preset.label }),
11986
- preset.date && /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { className: "text-xs text-muted-foreground/70", children: preset.date })
11987
- ]
11988
- },
11989
- preset.id
11990
- );
11991
- }),
11992
- selectedDatePreset === "custom" && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex flex-col gap-2 mt-2 pt-2 border-t border-border/40", children: [
11993
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex items-center gap-2", children: [
11994
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
11995
- "input",
11996
- {
11997
- type: "date",
11998
- value: customDateFrom,
11999
- onChange: (e) => setCustomDateFrom(e.target.value),
12000
- className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
12001
- }
12002
- ),
12003
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
12004
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12005
- "input",
12006
- {
12007
- type: "date",
12008
- value: customDateTo,
12009
- onChange: (e) => setCustomDateTo(e.target.value),
12010
- className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
12011
- }
12012
- )
12013
- ] }),
12014
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("div", { className: "flex justify-end gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12015
- Button,
12016
- {
12017
- color: "primary",
12018
- size: "sm",
12019
- onClick: handleCustomDateApply,
12020
- className: "h-7 text-xs px-2.5",
12021
- children: "Apply"
12022
- }
12023
- ) })
12024
- ] })
12025
- ] }),
12026
- activeMenu === "ltv" && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
12027
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex items-center gap-2", children: [
12028
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "relative shrink-0", children: [
12029
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
12030
- "button",
12031
- {
12032
- type: "button",
12033
- onClick: () => setLtvOperatorOpen(!isLtvOperatorOpen),
12034
- 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",
12035
- children: [
12036
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { children: OPERATORS.find((op) => op.id === ltvOperator)?.label.replace("...", "") }),
12037
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.ChevronDownIcon, { className: "size-3" })
12038
- ]
12039
- }
12040
- ),
12041
- isLtvOperatorOpen && /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("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__ */ (0, import_jsx_runtime106.jsx)(
12042
- "button",
12043
- {
12044
- type: "button",
12045
- onClick: () => {
12046
- setLtvOperator(op.id);
12047
- setLtvOperatorOpen(false);
12048
- },
12049
- className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
12050
- children: op.label
12051
- },
12052
- op.id
12053
- )) })
12054
- ] }),
12055
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12056
- "input",
12057
- {
12058
- type: "text",
12059
- placeholder: "0.00",
12060
- value: ltvValue,
12061
- onChange: (e) => setLtvValue(e.target.value),
12062
- 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"
12063
- }
12064
- )
12065
- ] }),
12066
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
12067
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12068
- Button,
12069
- {
12070
- variant: "ghost",
12071
- size: "sm",
12072
- onClick: handleLtvClear,
12073
- className: "text-muted-foreground hover:text-foreground h-8 px-3",
12074
- children: "Clear"
12075
- }
12076
- ),
12077
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12078
- Button,
12079
- {
12080
- color: "primary",
12081
- size: "sm",
12082
- onClick: handleLtvApply,
12083
- className: "h-8 px-3",
12084
- children: "Apply"
12085
- }
12086
- )
12087
- ] })
12088
- ] }),
12089
- activeMenu === "outstanding" && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
12090
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex items-center gap-2", children: [
12091
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "relative shrink-0", children: [
12092
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
12093
- "button",
12094
- {
12095
- type: "button",
12096
- onClick: () => setOutstandingOperatorOpen(!isOutstandingOperatorOpen),
12097
- 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",
12098
- children: [
12099
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { children: OPERATORS.find((op) => op.id === outstandingOperator)?.label.replace("...", "") }),
12100
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(import_lucide_react24.ChevronDownIcon, { className: "size-3" })
12101
- ]
12102
- }
12103
- ),
12104
- isOutstandingOperatorOpen && /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("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__ */ (0, import_jsx_runtime106.jsx)(
12105
- "button",
12106
- {
12107
- type: "button",
12108
- onClick: () => {
12109
- setOutstandingOperator(op.id);
12110
- setOutstandingOperatorOpen(false);
12111
- },
12112
- className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
12113
- children: op.label
12114
- },
12115
- op.id
12116
- )) })
12067
+ } else if (field.type === "text") {
12068
+ const text = asText(v);
12069
+ if (text) {
12070
+ chips.push({
12071
+ id: field.id,
12072
+ label: `${field.label}: ${text}`,
12073
+ remove: () => commit(field.id, "")
12074
+ });
12075
+ }
12076
+ } else if (field.type === "numeric") {
12077
+ const n = asNumeric(v);
12078
+ if (n.value) {
12079
+ chips.push({
12080
+ id: field.id,
12081
+ label: `${field.label} ${OPERATOR_SYMBOL[n.operator]} ${n.value}`,
12082
+ remove: () => commit(field.id, null)
12083
+ });
12084
+ }
12085
+ } else if (field.type === "daterange") {
12086
+ const d = asDate(v);
12087
+ if (d.preset) {
12088
+ const presetLabel = d.preset === "custom" ? `${d.from || "\u2026"} \u2013 ${d.to || "\u2026"}` : (field.presets ?? DEFAULT_PRESETS).find((p) => p.id === d.preset)?.label ?? d.preset;
12089
+ chips.push({
12090
+ id: field.id,
12091
+ label: `${field.label}: ${presetLabel}`,
12092
+ remove: () => commit(field.id, { preset: null })
12093
+ });
12094
+ }
12095
+ }
12096
+ }
12097
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [
12098
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
12099
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12100
+ Button,
12101
+ {
12102
+ variant: "outline",
12103
+ size: "sm",
12104
+ className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
12105
+ iconLeading: /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(import_lucide_react25.ListFilterIcon, { className: "size-4" }),
12106
+ children: label
12107
+ }
12108
+ ) }),
12109
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12110
+ PopoverContent,
12111
+ {
12112
+ variant: "list",
12113
+ align,
12114
+ className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
12115
+ children: /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("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: [
12116
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: fields.map((field) => {
12117
+ const isActive = activeId === field.id;
12118
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(
12119
+ "button",
12120
+ {
12121
+ type: "button",
12122
+ className: cn(
12123
+ "flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
12124
+ isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
12125
+ ),
12126
+ onMouseEnter: () => !isMobile && setActiveId(field.id),
12127
+ onClick: () => setActiveId(field.id),
12128
+ children: [
12129
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("span", { className: "flex items-center gap-2", children: [
12130
+ field.icon,
12131
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { children: field.label })
12117
12132
  ] }),
12118
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12119
- "input",
12120
- {
12121
- type: "text",
12122
- placeholder: "0.00",
12123
- value: outstandingValue,
12124
- onChange: (e) => setOutstandingValue(e.target.value),
12125
- 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"
12126
- }
12127
- )
12128
- ] }),
12129
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
12130
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12131
- Button,
12132
- {
12133
- variant: "ghost",
12134
- size: "sm",
12135
- onClick: handleOutstandingClear,
12136
- className: "text-muted-foreground hover:text-foreground h-8 px-3",
12137
- children: "Clear"
12138
- }
12139
- ),
12140
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
12141
- Button,
12142
- {
12143
- color: "primary",
12144
- size: "sm",
12145
- onClick: handleOutstandingApply,
12146
- className: "h-8 px-3",
12147
- children: "Apply"
12148
- }
12149
- )
12150
- ] })
12151
- ] })
12152
- ]
12133
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(import_lucide_react25.ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
12134
+ ]
12135
+ },
12136
+ field.id
12137
+ );
12138
+ }) }),
12139
+ activeField && /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12140
+ "div",
12141
+ {
12142
+ 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",
12143
+ style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
12144
+ children: /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12145
+ FilterFieldControl,
12146
+ {
12147
+ field: activeField,
12148
+ value: values[activeField.id],
12149
+ onChange: (next) => commit(activeField.id, next),
12150
+ onClose: () => setIsOpen(false)
12151
+ }
12152
+ )
12153
+ }
12154
+ )
12155
+ ] })
12156
+ }
12157
+ )
12158
+ ] }),
12159
+ showActiveChips && chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(FilterChip, { label: chip.label, onRemove: chip.remove }, chip.id)),
12160
+ showActiveChips && chips.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12161
+ "button",
12162
+ {
12163
+ type: "button",
12164
+ onClick: clearAll,
12165
+ className: "rounded-full px-3 py-1 text-sm font-medium text-muted-foreground outline-none transition-colors hover:text-foreground",
12166
+ children: "Clear all"
12167
+ }
12168
+ )
12169
+ ] });
12170
+ }
12171
+ function FilterChip({ label, onRemove }) {
12172
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("span", { className: "inline-flex h-9 items-center gap-1.5 rounded-full border border-border bg-muted/40 pl-3 pr-1.5 text-sm font-medium text-foreground", children: [
12173
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { className: "truncate", children: label }),
12174
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12175
+ "button",
12176
+ {
12177
+ type: "button",
12178
+ onClick: onRemove,
12179
+ "aria-label": `Remove ${label}`,
12180
+ className: "flex size-5 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground",
12181
+ children: /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(import_lucide_react25.XIcon, { className: "size-3.5" })
12182
+ }
12183
+ )
12184
+ ] });
12185
+ }
12186
+ function FilterFieldControl({
12187
+ field,
12188
+ value,
12189
+ onChange,
12190
+ onClose
12191
+ }) {
12192
+ switch (field.type) {
12193
+ case "multiselect":
12194
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(MultiSelectControl, { field, value: asArray(value), onChange });
12195
+ case "text":
12196
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(TextControl, { field, value: asText(value), onChange, onClose });
12197
+ case "daterange":
12198
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(DateRangeControl, { field, value: asDate(value), onChange, onClose });
12199
+ case "numeric":
12200
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(NumericControl, { field, value: asNumeric(value), onChange, onClose });
12201
+ default:
12202
+ return null;
12203
+ }
12204
+ }
12205
+ function MultiSelectControl({
12206
+ field,
12207
+ value,
12208
+ onChange
12209
+ }) {
12210
+ const options = field.options ?? [];
12211
+ const [search, setSearch] = (0, import_react73.useState)("");
12212
+ const searchable = field.searchable ?? options.length > 8;
12213
+ const filtered = (0, import_react73.useMemo)(() => {
12214
+ if (!search) return options;
12215
+ const q = search.toLowerCase();
12216
+ return options.filter(
12217
+ (o) => o.label.toLowerCase().includes(q) || o.hint?.toLowerCase().includes(q)
12218
+ );
12219
+ }, [options, search]);
12220
+ const toggle = (optionValue) => {
12221
+ onChange(
12222
+ value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
12223
+ );
12224
+ };
12225
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
12226
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12227
+ SearchInput,
12228
+ {
12229
+ placeholder: field.searchPlaceholder ?? "Search\u2026",
12230
+ value: search,
12231
+ onChange: (e) => setSearch(e.target.value),
12232
+ className: "w-full min-w-0"
12233
+ }
12234
+ ),
12235
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto pr-1", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No options found" }) : filtered.map((option) => /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(
12236
+ "label",
12237
+ {
12238
+ className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors hover:bg-muted/50",
12239
+ children: [
12240
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12241
+ Checkbox,
12242
+ {
12243
+ checked: value.includes(option.value),
12244
+ onCheckedChange: () => toggle(option.value)
12153
12245
  }
12154
- )
12155
- ] })
12246
+ ),
12247
+ option.icon,
12248
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("span", { className: "flex min-w-0 flex-col", children: [
12249
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { className: "truncate text-sm font-medium text-foreground", children: option.label }),
12250
+ option.hint && /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: option.hint })
12251
+ ] })
12252
+ ]
12253
+ },
12254
+ option.value
12255
+ )) })
12256
+ ] });
12257
+ }
12258
+ function TextControl({
12259
+ field,
12260
+ value,
12261
+ onChange,
12262
+ onClose
12263
+ }) {
12264
+ const [draft, setDraft] = (0, import_react73.useState)(value);
12265
+ (0, import_react73.useEffect)(() => setDraft(value), [value]);
12266
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
12267
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12268
+ "input",
12269
+ {
12270
+ type: "text",
12271
+ placeholder: field.placeholder ?? "Type a value\u2026",
12272
+ value: draft,
12273
+ onChange: (e) => setDraft(e.target.value),
12274
+ onKeyDown: (e) => {
12275
+ if (e.key === "Enter") {
12276
+ onChange(draft);
12277
+ onClose();
12278
+ }
12279
+ },
12280
+ className: controlClass({ size: "sm" }, "w-full")
12281
+ }
12282
+ ),
12283
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12284
+ ApplyClear,
12285
+ {
12286
+ onClear: () => {
12287
+ setDraft("");
12288
+ onChange("");
12289
+ onClose();
12290
+ },
12291
+ onApply: () => {
12292
+ onChange(draft);
12293
+ onClose();
12294
+ }
12156
12295
  }
12157
12296
  )
12158
- ] }) });
12297
+ ] });
12298
+ }
12299
+ function DateRangeControl({
12300
+ field,
12301
+ value,
12302
+ onChange,
12303
+ onClose
12304
+ }) {
12305
+ const presets = field.presets ?? DEFAULT_PRESETS;
12306
+ const [from, setFrom] = (0, import_react73.useState)(value.from ?? "");
12307
+ const [to, setTo] = (0, import_react73.useState)(value.to ?? "");
12308
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex flex-col gap-1", children: [
12309
+ presets.map((preset) => {
12310
+ const isSelected = value.preset === preset.id;
12311
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(
12312
+ "button",
12313
+ {
12314
+ type: "button",
12315
+ onClick: () => {
12316
+ if (preset.id === "custom") {
12317
+ onChange({ preset: "custom", from, to });
12318
+ } else {
12319
+ onChange({ preset: preset.id });
12320
+ onClose();
12321
+ }
12322
+ },
12323
+ className: cn(
12324
+ "flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-left text-sm transition-colors outline-none",
12325
+ isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
12326
+ ),
12327
+ children: [
12328
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { children: preset.label }),
12329
+ preset.hint && /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { className: "text-xs text-muted-foreground/70", children: preset.hint })
12330
+ ]
12331
+ },
12332
+ preset.id
12333
+ );
12334
+ }),
12335
+ value.preset === "custom" && /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "mt-2 flex flex-col gap-2 border-t border-border/40 pt-2", children: [
12336
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex items-center gap-2", children: [
12337
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12338
+ "input",
12339
+ {
12340
+ type: "date",
12341
+ value: from,
12342
+ onChange: (e) => setFrom(e.target.value),
12343
+ className: controlClass({ size: "sm" }, "w-full text-xs")
12344
+ }
12345
+ ),
12346
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
12347
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12348
+ "input",
12349
+ {
12350
+ type: "date",
12351
+ value: to,
12352
+ onChange: (e) => setTo(e.target.value),
12353
+ className: controlClass({ size: "sm" }, "w-full text-xs")
12354
+ }
12355
+ )
12356
+ ] }),
12357
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12358
+ Button,
12359
+ {
12360
+ color: "primary",
12361
+ size: "sm",
12362
+ className: "h-8 px-3",
12363
+ onClick: () => {
12364
+ onChange({ preset: "custom", from, to });
12365
+ onClose();
12366
+ },
12367
+ children: "Apply"
12368
+ }
12369
+ ) })
12370
+ ] })
12371
+ ] });
12372
+ }
12373
+ function NumericControl({
12374
+ field,
12375
+ value,
12376
+ onChange,
12377
+ onClose
12378
+ }) {
12379
+ const operators = field.operators ?? DEFAULT_OPERATORS;
12380
+ const [operator, setOperator] = (0, import_react73.useState)(value.operator);
12381
+ const [draft, setDraft] = (0, import_react73.useState)(value.value);
12382
+ (0, import_react73.useEffect)(() => {
12383
+ setOperator(value.operator);
12384
+ setDraft(value.value);
12385
+ }, [value.operator, value.value]);
12386
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
12387
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex items-center gap-2", children: [
12388
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(Select, { value: operator, onValueChange: (v) => setOperator(v), children: [
12389
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(SelectTrigger, { size: "sm", className: "shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(SelectValue, {}) }),
12390
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(SelectContent, { children: operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(SelectItem, { value: op.id, children: op.label }, op.id)) })
12391
+ ] }),
12392
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12393
+ "input",
12394
+ {
12395
+ type: "text",
12396
+ inputMode: "decimal",
12397
+ placeholder: field.placeholder ?? "0.00",
12398
+ value: draft,
12399
+ onChange: (e) => setDraft(e.target.value),
12400
+ onKeyDown: (e) => {
12401
+ if (e.key === "Enter") {
12402
+ onChange(draft ? { operator, value: draft } : null);
12403
+ onClose();
12404
+ }
12405
+ },
12406
+ className: controlClass({ size: "sm" }, "min-w-0 flex-1")
12407
+ }
12408
+ )
12409
+ ] }),
12410
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12411
+ ApplyClear,
12412
+ {
12413
+ onClear: () => {
12414
+ setDraft("");
12415
+ onChange(null);
12416
+ onClose();
12417
+ },
12418
+ onApply: () => {
12419
+ onChange(draft ? { operator, value: draft } : null);
12420
+ onClose();
12421
+ }
12422
+ }
12423
+ )
12424
+ ] });
12425
+ }
12426
+ function ApplyClear({ onClear, onApply }) {
12427
+ return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)("div", { className: "flex items-center justify-end gap-2 border-t border-border/40 pt-1", children: [
12428
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12429
+ Button,
12430
+ {
12431
+ variant: "ghost",
12432
+ size: "sm",
12433
+ onClick: onClear,
12434
+ className: "h-8 px-3 text-muted-foreground hover:text-foreground",
12435
+ children: "Clear"
12436
+ }
12437
+ ),
12438
+ /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(Button, { color: "primary", size: "sm", onClick: onApply, className: "h-8 px-3", children: "Apply" })
12439
+ ] });
12159
12440
  }
12160
12441
 
12161
12442
  // src/app/data/DataTable.tsx
12162
12443
  var import_react74 = require("react");
12163
- var import_lucide_react25 = require("lucide-react");
12444
+ var import_lucide_react26 = require("lucide-react");
12164
12445
 
12165
12446
  // src/ui/skeleton.tsx
12166
- var import_jsx_runtime107 = require("react/jsx-runtime");
12447
+ var import_jsx_runtime108 = require("react/jsx-runtime");
12167
12448
  function Skeleton({ className, ...props }) {
12168
- return /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
12449
+ return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12169
12450
  "div",
12170
12451
  {
12171
12452
  "data-slot": "skeleton",
@@ -12176,7 +12457,7 @@ function Skeleton({ className, ...props }) {
12176
12457
  }
12177
12458
 
12178
12459
  // src/app/data/DataTable.tsx
12179
- var import_jsx_runtime108 = require("react/jsx-runtime");
12460
+ var import_jsx_runtime109 = require("react/jsx-runtime");
12180
12461
  var shellClass2 = "w-full";
12181
12462
  var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
12182
12463
  var headRowClass = "";
@@ -12219,12 +12500,12 @@ function SortIndicator({
12219
12500
  }) {
12220
12501
  const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
12221
12502
  if (!active) {
12222
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(import_lucide_react25.ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
12503
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(import_lucide_react26.ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
12223
12504
  }
12224
12505
  if (direction === "desc") {
12225
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(import_lucide_react25.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
12506
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(import_lucide_react26.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
12226
12507
  }
12227
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(import_lucide_react25.ArrowUpIcon, { className: iconClass, "aria-hidden": true });
12508
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(import_lucide_react26.ArrowUpIcon, { className: iconClass, "aria-hidden": true });
12228
12509
  }
12229
12510
  function DataTable({
12230
12511
  columns,
@@ -12322,7 +12603,7 @@ function DataTable({
12322
12603
  const headPad = dense ? "px-3 py-2" : void 0;
12323
12604
  const colSpan = columns.length + (selectable ? 1 : 0);
12324
12605
  if (!loading && rows.length === 0 && emptyMode === "replace") {
12325
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, className });
12606
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, className });
12326
12607
  }
12327
12608
  const allKeys = sortedRows.map(getRowKey);
12328
12609
  const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
@@ -12347,10 +12628,10 @@ function DataTable({
12347
12628
  const hasPager = paginated && !loading && sortedRows.length > 0;
12348
12629
  const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
12349
12630
  const skeletonCount = loadingRows ?? pageSize ?? 5;
12350
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("table", { className: tableClass, children: [
12351
- caption ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("caption", { className: "sr-only", children: caption }) : null,
12352
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("tr", { children: [
12353
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12631
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("table", { className: tableClass, children: [
12632
+ caption ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("caption", { className: "sr-only", children: caption }) : null,
12633
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("tr", { children: [
12634
+ selectable ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12354
12635
  Checkbox,
12355
12636
  {
12356
12637
  checked: headerCheckedState,
@@ -12362,19 +12643,19 @@ function DataTable({
12362
12643
  columns.map((col) => {
12363
12644
  const isSorted = sort?.columnId === col.id;
12364
12645
  const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
12365
- const headerContent = col.sortable ? /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(
12646
+ const headerContent = col.sortable ? /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
12366
12647
  "button",
12367
12648
  {
12368
12649
  type: "button",
12369
12650
  className: sortButtonClass,
12370
12651
  onClick: () => setSort(nextSort(sort, col.id)),
12371
12652
  children: [
12372
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("span", { className: "truncate", children: col.header }),
12373
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
12653
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("span", { className: "truncate", children: col.header }),
12654
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
12374
12655
  ]
12375
12656
  }
12376
12657
  ) : col.header;
12377
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12658
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12378
12659
  "th",
12379
12660
  {
12380
12661
  scope: "col",
@@ -12391,9 +12672,9 @@ function DataTable({
12391
12672
  );
12392
12673
  })
12393
12674
  ] }) }),
12394
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("tr", { className: rowClass, "aria-hidden": true, children: [
12395
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
12396
- columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12675
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("tr", { className: rowClass, "aria-hidden": true, children: [
12676
+ selectable ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
12677
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12397
12678
  "td",
12398
12679
  {
12399
12680
  className: cn(
@@ -12402,17 +12683,17 @@ function DataTable({
12402
12683
  col.align && alignClass[col.align],
12403
12684
  col.className
12404
12685
  ),
12405
- children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Skeleton, { className: "h-4 w-[60%]" })
12686
+ children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Skeleton, { className: "h-4 w-[60%]" })
12406
12687
  },
12407
12688
  col.id
12408
12689
  ))
12409
- ] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
12410
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("p", { className: "font-medium text-foreground", children: emptyTitle }),
12411
- emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
12690
+ ] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
12691
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("p", { className: "font-medium text-foreground", children: emptyTitle }),
12692
+ emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
12412
12693
  ] }) }) }) : visibleRows.map((row) => {
12413
12694
  const key = getRowKey(row);
12414
12695
  const isSelected = selectedSet.has(key);
12415
- return /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(
12696
+ return /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
12416
12697
  "tr",
12417
12698
  {
12418
12699
  className: rowClass,
@@ -12428,12 +12709,12 @@ function DataTable({
12428
12709
  tabIndex: onRowClick ? 0 : void 0,
12429
12710
  role: onRowClick ? "button" : void 0,
12430
12711
  children: [
12431
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12712
+ selectable ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12432
12713
  "td",
12433
12714
  {
12434
12715
  className: cn(selectCellClass, cellPad),
12435
12716
  onClick: (event) => event.stopPropagation(),
12436
- children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12717
+ children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12437
12718
  Checkbox,
12438
12719
  {
12439
12720
  checked: isSelected,
@@ -12443,7 +12724,7 @@ function DataTable({
12443
12724
  )
12444
12725
  }
12445
12726
  ) : null,
12446
- columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12727
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12447
12728
  "td",
12448
12729
  {
12449
12730
  className: cn(
@@ -12453,7 +12734,7 @@ function DataTable({
12453
12734
  col.align && alignClass[col.align],
12454
12735
  col.className
12455
12736
  ),
12456
- children: col.truncate ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
12737
+ children: col.truncate ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
12457
12738
  },
12458
12739
  col.id
12459
12740
  ))
@@ -12462,7 +12743,7 @@ function DataTable({
12462
12743
  key
12463
12744
  );
12464
12745
  }) }),
12465
- hasFoot ? /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("tfoot", { children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(
12746
+ hasFoot ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("tfoot", { children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
12466
12747
  "div",
12467
12748
  {
12468
12749
  className: cn(
@@ -12470,18 +12751,18 @@ function DataTable({
12470
12751
  (showRowCount || footer || hasPager) && "justify-between"
12471
12752
  ),
12472
12753
  children: [
12473
- /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: footInnerClass, children: [
12474
- showRowCount ? /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("span", { children: [
12754
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { className: footInnerClass, children: [
12755
+ showRowCount ? /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("span", { children: [
12475
12756
  rowCountText,
12476
12757
  selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
12477
- ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("span", { children: [
12758
+ ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("span", { children: [
12478
12759
  selectedSet.size,
12479
12760
  " selected"
12480
12761
  ] }) : null,
12481
12762
  footer
12482
12763
  ] }),
12483
- hasPager ? /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: "flex items-center gap-2", children: [
12484
- /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("span", { className: "tabular-nums", children: [
12764
+ hasPager ? /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { className: "flex items-center gap-2", children: [
12765
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("span", { className: "tabular-nums", children: [
12485
12766
  pageIndex * pageSize + 1,
12486
12767
  "\u2013",
12487
12768
  Math.min(
@@ -12492,8 +12773,8 @@ function DataTable({
12492
12773
  "of ",
12493
12774
  sortedRows.length
12494
12775
  ] }),
12495
- /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: "flex items-center gap-1", children: [
12496
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12776
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { className: "flex items-center gap-1", children: [
12777
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12497
12778
  "button",
12498
12779
  {
12499
12780
  type: "button",
@@ -12501,10 +12782,10 @@ function DataTable({
12501
12782
  onClick: () => setPage(pageIndex - 1),
12502
12783
  disabled: pageIndex <= 0,
12503
12784
  "aria-label": "Previous page",
12504
- children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(import_lucide_react25.ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
12785
+ children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(import_lucide_react26.ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
12505
12786
  }
12506
12787
  ),
12507
- /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
12788
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12508
12789
  "button",
12509
12790
  {
12510
12791
  type: "button",
@@ -12512,7 +12793,7 @@ function DataTable({
12512
12793
  onClick: () => setPage(pageIndex + 1),
12513
12794
  disabled: pageIndex >= pageCount - 1,
12514
12795
  "aria-label": "Next page",
12515
- children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(import_lucide_react25.ChevronRightIcon, { className: "size-4", "aria-hidden": true })
12796
+ children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(import_lucide_react26.ChevronRightIcon, { className: "size-4", "aria-hidden": true })
12516
12797
  }
12517
12798
  )
12518
12799
  ] })
@@ -12525,7 +12806,7 @@ function DataTable({
12525
12806
 
12526
12807
  // src/app/data/ChartPanel.tsx
12527
12808
  var import_react75 = require("react");
12528
- var import_jsx_runtime109 = require("react/jsx-runtime");
12809
+ var import_jsx_runtime110 = require("react/jsx-runtime");
12529
12810
  var ChartPanel = ({
12530
12811
  title,
12531
12812
  description,
@@ -12543,14 +12824,14 @@ var ChartPanel = ({
12543
12824
  const titleId = (0, import_react75.useId)();
12544
12825
  const resolvedTitle = title ?? artifact?.title;
12545
12826
  const hasHeader = Boolean(resolvedTitle || description || actions);
12546
- const body = loading ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(ChartArtifactView, { artifact, embedded: true, height }) : null);
12547
- return /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
12827
+ const body = loading ? /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(ChartArtifactView, { artifact, embedded: true, height }) : null);
12828
+ return /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(
12548
12829
  "section",
12549
12830
  {
12550
12831
  className: cn(metricCardShellClass, "aui-app-chart-panel", className),
12551
12832
  "aria-labelledby": resolvedTitle ? titleId : void 0,
12552
12833
  children: [
12553
- /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12834
+ /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
12554
12835
  MetricCardHeader,
12555
12836
  {
12556
12837
  title: resolvedTitle,
@@ -12559,14 +12840,14 @@ var ChartPanel = ({
12559
12840
  actions
12560
12841
  }
12561
12842
  ),
12562
- /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12843
+ /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
12563
12844
  "div",
12564
12845
  {
12565
12846
  className: cn(
12566
12847
  "relative min-h-0 w-full",
12567
12848
  hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
12568
12849
  ),
12569
- children: body ?? /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
12850
+ children: body ?? /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
12570
12851
  "div",
12571
12852
  {
12572
12853
  className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
@@ -12584,7 +12865,7 @@ var ChartPanel = ({
12584
12865
 
12585
12866
  // src/app/data/MetricRow.tsx
12586
12867
  var import_react76 = require("react");
12587
- var import_jsx_runtime110 = require("react/jsx-runtime");
12868
+ var import_jsx_runtime111 = require("react/jsx-runtime");
12588
12869
  var MetricRow = ({
12589
12870
  title,
12590
12871
  titleTag,
@@ -12610,13 +12891,13 @@ var MetricRow = ({
12610
12891
  onMetricChange?.(id);
12611
12892
  };
12612
12893
  const hasHeader = Boolean(title || titleTag || description || actions);
12613
- return /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(
12894
+ return /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
12614
12895
  "section",
12615
12896
  {
12616
12897
  className: cn(metricCardShellClass, className),
12617
12898
  "aria-labelledby": title ? titleId : void 0,
12618
12899
  children: [
12619
- /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
12900
+ /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
12620
12901
  MetricCardHeader,
12621
12902
  {
12622
12903
  title,
@@ -12626,7 +12907,7 @@ var MetricRow = ({
12626
12907
  actions
12627
12908
  }
12628
12909
  ),
12629
- /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
12910
+ /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
12630
12911
  "div",
12631
12912
  {
12632
12913
  role: selectable ? "group" : void 0,
@@ -12637,18 +12918,18 @@ var MetricRow = ({
12637
12918
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
12638
12919
  hasHeader && "mt-3.5 border-t border-border/40"
12639
12920
  ),
12640
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(
12921
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
12641
12922
  "div",
12642
12923
  {
12643
12924
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
12644
12925
  "aria-hidden": true,
12645
12926
  children: [
12646
- /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(Skeleton, { className: "h-3 w-20" }),
12647
- /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(Skeleton, { className: "h-7 w-24" })
12927
+ /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Skeleton, { className: "h-3 w-20" }),
12928
+ /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Skeleton, { className: "h-7 w-24" })
12648
12929
  ]
12649
12930
  },
12650
12931
  `skeleton-${index}`
12651
- )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
12932
+ )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
12652
12933
  MetricTile,
12653
12934
  {
12654
12935
  label: m.label,
@@ -12676,7 +12957,7 @@ var MetricRow = ({
12676
12957
 
12677
12958
  // src/app/data/MetricChartCard.tsx
12678
12959
  var import_react77 = require("react");
12679
- var import_jsx_runtime111 = require("react/jsx-runtime");
12960
+ var import_jsx_runtime112 = require("react/jsx-runtime");
12680
12961
  var MetricChartCard = ({
12681
12962
  title,
12682
12963
  titleTag,
@@ -12712,13 +12993,13 @@ var MetricChartCard = ({
12712
12993
  };
12713
12994
  const hasHeader = Boolean(title || titleTag || description || actions);
12714
12995
  const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
12715
- return /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
12996
+ return /* @__PURE__ */ (0, import_jsx_runtime112.jsxs)(
12716
12997
  "section",
12717
12998
  {
12718
12999
  className: cn(metricCardShellClass, className),
12719
13000
  "aria-labelledby": title ? titleId : void 0,
12720
13001
  children: [
12721
- /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
13002
+ /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
12722
13003
  MetricCardHeader,
12723
13004
  {
12724
13005
  title,
@@ -12728,7 +13009,7 @@ var MetricChartCard = ({
12728
13009
  titleId
12729
13010
  }
12730
13011
  ),
12731
- /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
13012
+ /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
12732
13013
  "div",
12733
13014
  {
12734
13015
  role: "group",
@@ -12739,18 +13020,18 @@ var MetricChartCard = ({
12739
13020
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
12740
13021
  hasHeader && "mt-3.5 border-t border-border/40"
12741
13022
  ),
12742
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
13023
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime112.jsxs)(
12743
13024
  "div",
12744
13025
  {
12745
13026
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
12746
13027
  "aria-hidden": true,
12747
13028
  children: [
12748
- /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Skeleton, { className: "h-3 w-20" }),
12749
- /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Skeleton, { className: "h-7 w-24" })
13029
+ /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(Skeleton, { className: "h-3 w-20" }),
13030
+ /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(Skeleton, { className: "h-7 w-24" })
12750
13031
  ]
12751
13032
  },
12752
13033
  `skeleton-${index}`
12753
- )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
13034
+ )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
12754
13035
  MetricTile,
12755
13036
  {
12756
13037
  label: m.label,
@@ -12771,14 +13052,14 @@ var MetricChartCard = ({
12771
13052
  ))
12772
13053
  }
12773
13054
  ),
12774
- /* @__PURE__ */ (0, import_jsx_runtime111.jsx)("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
13055
+ /* @__PURE__ */ (0, import_jsx_runtime112.jsx)("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
12775
13056
  Skeleton,
12776
13057
  {
12777
13058
  className: "w-full rounded-lg",
12778
13059
  style: { height },
12779
13060
  "aria-hidden": true
12780
13061
  }
12781
- ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
13062
+ ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
12782
13063
  LineAreaChart,
12783
13064
  {
12784
13065
  data: active.data,
@@ -12798,7 +13079,7 @@ var MetricChartCard = ({
12798
13079
  ariaLabel: chartAriaLabel
12799
13080
  },
12800
13081
  active.id
12801
- ) : /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
13082
+ ) : /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
12802
13083
  "div",
12803
13084
  {
12804
13085
  className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
@@ -12904,8 +13185,8 @@ function useLiveQuery(fetcher, options = {}) {
12904
13185
 
12905
13186
  // src/ui/untitled-button.tsx
12906
13187
  var import_class_variance_authority2 = require("class-variance-authority");
12907
- var import_radix_ui9 = require("radix-ui");
12908
- var import_jsx_runtime112 = require("react/jsx-runtime");
13188
+ var import_radix_ui10 = require("radix-ui");
13189
+ var import_jsx_runtime113 = require("react/jsx-runtime");
12909
13190
  var SOLID_SKEUOMORPHIC_SHADOW2 = "shadow-skeuomorphic-solid";
12910
13191
  var BORDERED_SKEUOMORPHIC_SHADOW2 = "shadow-skeuomorphic-bordered";
12911
13192
  var untitledButtonVariants = (0, import_class_variance_authority2.cva)(
@@ -12988,10 +13269,10 @@ var untitledButtonVariants = (0, import_class_variance_authority2.cva)(
12988
13269
  )
12989
13270
  },
12990
13271
  size: {
12991
- sm: "h-9 gap-1.5 rounded-lg px-3 text-sm",
12992
- md: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
12993
- lg: "h-11 gap-2 rounded-lg px-4 text-base",
12994
- xl: "h-12 gap-2 rounded-lg px-5 text-base"
13272
+ sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
13273
+ md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
13274
+ lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
13275
+ xl: "h-11 gap-2 rounded-lg px-4 text-base"
12995
13276
  }
12996
13277
  },
12997
13278
  defaultVariants: {
@@ -13016,8 +13297,8 @@ function UntitledButton({
13016
13297
  const isDisabled = disabled || isLoading;
13017
13298
  const classes = cn(untitledButtonVariants({ color, size }), className);
13018
13299
  if (asChild) {
13019
- return /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
13020
- import_radix_ui9.Slot.Root,
13300
+ return /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
13301
+ import_radix_ui10.Slot.Root,
13021
13302
  {
13022
13303
  className: classes,
13023
13304
  "aria-disabled": isDisabled ? true : void 0,
@@ -13027,7 +13308,7 @@ function UntitledButton({
13027
13308
  }
13028
13309
  );
13029
13310
  }
13030
- return /* @__PURE__ */ (0, import_jsx_runtime112.jsxs)(
13311
+ return /* @__PURE__ */ (0, import_jsx_runtime113.jsxs)(
13031
13312
  "button",
13032
13313
  {
13033
13314
  type,
@@ -13036,7 +13317,7 @@ function UntitledButton({
13036
13317
  className: classes,
13037
13318
  ...props,
13038
13319
  children: [
13039
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(
13320
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
13040
13321
  "span",
13041
13322
  {
13042
13323
  "aria-hidden": true,
@@ -13051,8 +13332,8 @@ function UntitledButton({
13051
13332
  }
13052
13333
 
13053
13334
  // src/ui/banner.tsx
13054
- var import_lucide_react26 = require("lucide-react");
13055
- var import_jsx_runtime113 = require("react/jsx-runtime");
13335
+ var import_lucide_react27 = require("lucide-react");
13336
+ var import_jsx_runtime114 = require("react/jsx-runtime");
13056
13337
  var bannerSoftClass = {
13057
13338
  default: "border-border/50 bg-muted/30 text-foreground/90 dark:bg-muted/15",
13058
13339
  primary: "border-primary/15 bg-primary/5 text-primary-800 dark:text-primary-200 [&_[data-banner-icon]]:text-primary",
@@ -13097,7 +13378,7 @@ function Banner({
13097
13378
  }) {
13098
13379
  const isSolid = variant === "solid";
13099
13380
  const isSingleLine = !title;
13100
- return /* @__PURE__ */ (0, import_jsx_runtime113.jsxs)(
13381
+ return /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(
13101
13382
  "div",
13102
13383
  {
13103
13384
  "data-slot": "banner",
@@ -13112,7 +13393,7 @@ function Banner({
13112
13393
  ),
13113
13394
  ...props,
13114
13395
  children: [
13115
- icon ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
13396
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
13116
13397
  "span",
13117
13398
  {
13118
13399
  "data-banner-icon": true,
@@ -13123,9 +13404,9 @@ function Banner({
13123
13404
  children: icon
13124
13405
  }
13125
13406
  ) : null,
13126
- /* @__PURE__ */ (0, import_jsx_runtime113.jsxs)("div", { className: "min-w-0 flex-1", children: [
13127
- title ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)("p", { className: "font-medium tracking-tight", children: title }) : null,
13128
- children ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
13407
+ /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)("div", { className: "min-w-0 flex-1", children: [
13408
+ title ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("p", { className: "font-medium tracking-tight", children: title }) : null,
13409
+ children ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
13129
13410
  "div",
13130
13411
  {
13131
13412
  className: cn(
@@ -13136,8 +13417,8 @@ function Banner({
13136
13417
  }
13137
13418
  ) : null
13138
13419
  ] }),
13139
- actions ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
13140
- onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
13420
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
13421
+ onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
13141
13422
  "button",
13142
13423
  {
13143
13424
  type: "button",
@@ -13148,7 +13429,7 @@ function Banner({
13148
13429
  isSingleLine ? "self-center" : "-mt-0.5",
13149
13430
  isSolid ? "opacity-80 hover:bg-background/15 hover:opacity-100" : "text-muted-foreground hover:bg-foreground/10 hover:text-foreground"
13150
13431
  ),
13151
- children: /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(import_lucide_react26.XIcon, { className: "size-4", "aria-hidden": true })
13432
+ children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(import_lucide_react27.XIcon, { className: "size-4", "aria-hidden": true })
13152
13433
  }
13153
13434
  ) : null
13154
13435
  ]
@@ -13157,7 +13438,7 @@ function Banner({
13157
13438
  }
13158
13439
 
13159
13440
  // src/ui/timeline.tsx
13160
- var import_jsx_runtime114 = require("react/jsx-runtime");
13441
+ var import_jsx_runtime115 = require("react/jsx-runtime");
13161
13442
  var timelineRowGap = {
13162
13443
  sm: "pb-4",
13163
13444
  default: "pb-6"
@@ -13190,16 +13471,16 @@ var toneStyles = {
13190
13471
  }
13191
13472
  };
13192
13473
  function Timeline({ items, size = "default", className, ...props }) {
13193
- return /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
13474
+ return /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
13194
13475
  const last = index === items.length - 1;
13195
13476
  const tone = item.tone ?? "default";
13196
13477
  const styles = toneStyles[tone];
13197
- return /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(
13478
+ return /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)(
13198
13479
  "li",
13199
13480
  {
13200
13481
  className: cn("relative flex gap-3.5 last:pb-0", timelineRowGap[size]),
13201
13482
  children: [
13202
- !last ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
13483
+ !last ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13203
13484
  "span",
13204
13485
  {
13205
13486
  "aria-hidden": true,
@@ -13209,7 +13490,7 @@ function Timeline({ items, size = "default", className, ...props }) {
13209
13490
  )
13210
13491
  }
13211
13492
  ) : null,
13212
- /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("div", { className: "relative flex w-5 shrink-0 justify-center pt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
13493
+ /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("div", { className: "relative flex w-5 shrink-0 justify-center pt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13213
13494
  "span",
13214
13495
  {
13215
13496
  className: cn(
@@ -13217,7 +13498,7 @@ function Timeline({ items, size = "default", className, ...props }) {
13217
13498
  styles.border,
13218
13499
  item.icon ? "size-6" : "size-4"
13219
13500
  ),
13220
- children: item.icon ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("div", { className: cn("text-xs", styles.icon), children: item.icon }) : /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
13501
+ children: item.icon ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("div", { className: cn("text-xs", styles.icon), children: item.icon }) : /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13221
13502
  "span",
13222
13503
  {
13223
13504
  className: cn(
@@ -13228,12 +13509,12 @@ function Timeline({ items, size = "default", className, ...props }) {
13228
13509
  )
13229
13510
  }
13230
13511
  ) }),
13231
- /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)("div", { className: "min-w-0 flex-1 pb-0.5", children: [
13232
- /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
13233
- /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("p", { className: "text-sm font-normal text-foreground", children: item.title }),
13234
- item.meta ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
13512
+ /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)("div", { className: "min-w-0 flex-1 pb-0.5", children: [
13513
+ /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
13514
+ /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("p", { className: "text-sm font-normal text-foreground", children: item.title }),
13515
+ item.meta ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
13235
13516
  ] }),
13236
- item.description ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)("p", { className: "mt-0.5 text-[13px] leading-relaxed text-muted-foreground", children: item.description }) : null
13517
+ item.description ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("p", { className: "mt-0.5 text-[13px] leading-relaxed text-muted-foreground", children: item.description }) : null
13237
13518
  ] })
13238
13519
  ]
13239
13520
  },
@@ -13247,8 +13528,8 @@ var React6 = __toESM(require("react"), 1);
13247
13528
  var import_core2 = require("@dnd-kit/core");
13248
13529
  var import_sortable = require("@dnd-kit/sortable");
13249
13530
  var import_utilities = require("@dnd-kit/utilities");
13250
- var import_lucide_react27 = require("lucide-react");
13251
- var import_jsx_runtime115 = require("react/jsx-runtime");
13531
+ var import_lucide_react28 = require("lucide-react");
13532
+ var import_jsx_runtime116 = require("react/jsx-runtime");
13252
13533
  var columnTitleToneClass = {
13253
13534
  default: "text-foreground",
13254
13535
  primary: "text-blue-600 dark:text-blue-400",
@@ -13310,7 +13591,7 @@ function SortableCard({
13310
13591
  transition
13311
13592
  };
13312
13593
  const dragHandleProps = disabled ? void 0 : { ...attributes, ...listeners2 };
13313
- return /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)(
13594
+ return /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)(
13314
13595
  "div",
13315
13596
  {
13316
13597
  ref: setNodeRef,
@@ -13325,7 +13606,7 @@ function SortableCard({
13325
13606
  className
13326
13607
  ),
13327
13608
  children: [
13328
- !disabled && dragHandle === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13609
+ !disabled && dragHandle === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13329
13610
  "button",
13330
13611
  {
13331
13612
  type: "button",
@@ -13333,7 +13614,7 @@ function SortableCard({
13333
13614
  className: "absolute right-1.5 top-1.5 z-10 grid size-6 cursor-grab touch-none place-items-center rounded-md text-muted-foreground/40 opacity-0 transition hover:bg-foreground/5 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 group-hover/kanban-card:opacity-100 active:cursor-grabbing",
13334
13615
  ...attributes,
13335
13616
  ...listeners2,
13336
- children: /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(import_lucide_react27.GripVerticalIcon, { className: "size-4", "aria-hidden": true })
13617
+ children: /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(import_lucide_react28.GripVerticalIcon, { className: "size-4", "aria-hidden": true })
13337
13618
  }
13338
13619
  ) : null,
13339
13620
  renderCard(card, { column, isDragging, isOverlay: false, dragHandleProps })
@@ -13357,7 +13638,7 @@ function KanbanColumnView({
13357
13638
  }) {
13358
13639
  const tone = column.tone ?? "default";
13359
13640
  const { setNodeRef, isOver } = (0, import_core2.useDroppable)({ id: column.id, disabled });
13360
- return /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)(
13641
+ return /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)(
13361
13642
  "div",
13362
13643
  {
13363
13644
  "data-slot": "kanban-column",
@@ -13367,9 +13648,9 @@ function KanbanColumnView({
13367
13648
  className
13368
13649
  ),
13369
13650
  children: [
13370
- renderColumnHeader ? renderColumnHeader(column) : /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)("div", { className: "flex flex-col gap-0.5 px-1 pb-0.5", children: [
13371
- /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)("div", { className: "flex items-center gap-2", children: [
13372
- /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13651
+ renderColumnHeader ? renderColumnHeader(column) : /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)("div", { className: "flex flex-col gap-0.5 px-1 pb-0.5", children: [
13652
+ /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)("div", { className: "flex items-center gap-2", children: [
13653
+ /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13373
13654
  "h3",
13374
13655
  {
13375
13656
  className: cn(
@@ -13379,12 +13660,12 @@ function KanbanColumnView({
13379
13660
  children: column.title
13380
13661
  }
13381
13662
  ),
13382
- /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("span", { className: "shrink-0 text-xs font-normal tabular-nums text-muted-foreground/60", children: column.cards.length }),
13383
- column.action ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("div", { className: "shrink-0", children: column.action }) : null
13663
+ /* @__PURE__ */ (0, import_jsx_runtime116.jsx)("span", { className: "shrink-0 text-xs font-normal tabular-nums text-muted-foreground/60", children: column.cards.length }),
13664
+ column.action ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)("div", { className: "shrink-0", children: column.action }) : null
13384
13665
  ] }),
13385
- column.description ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
13666
+ column.description ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
13386
13667
  ] }),
13387
- /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(import_sortable.SortableContext, { items: cardIds, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13668
+ /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(import_sortable.SortableContext, { items: cardIds, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13388
13669
  "div",
13389
13670
  {
13390
13671
  ref: setNodeRef,
@@ -13394,9 +13675,9 @@ function KanbanColumnView({
13394
13675
  densityListClass[density],
13395
13676
  isOver && "bg-muted/50"
13396
13677
  ),
13397
- children: column.cards.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("div", { className: "flex flex-1 items-center justify-center rounded-xl border border-dashed border-border/60 px-2 py-8 text-center text-xs text-muted-foreground/70", children: emptyColumnLabel ?? "Drop here" }) : column.cards.map((card) => {
13678
+ children: column.cards.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)("div", { className: "flex flex-1 items-center justify-center rounded-xl border border-dashed border-border/60 px-2 py-8 text-center text-xs text-muted-foreground/70", children: emptyColumnLabel ?? "Drop here" }) : column.cards.map((card) => {
13398
13679
  const id = getCardId(card);
13399
- return /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13680
+ return /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13400
13681
  SortableCard,
13401
13682
  {
13402
13683
  card,
@@ -13414,7 +13695,7 @@ function KanbanColumnView({
13414
13695
  })
13415
13696
  }
13416
13697
  ) }),
13417
- column.footer ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
13698
+ column.footer ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
13418
13699
  ]
13419
13700
  }
13420
13701
  );
@@ -13553,7 +13834,7 @@ function Kanban({
13553
13834
  onColumnsChange?.(next);
13554
13835
  }
13555
13836
  };
13556
- return /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)(
13837
+ return /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)(
13557
13838
  import_core2.DndContext,
13558
13839
  {
13559
13840
  sensors,
@@ -13567,7 +13848,7 @@ function Kanban({
13567
13848
  if (isControlled) setInternal(cloneColumns(columnsProp));
13568
13849
  },
13569
13850
  children: [
13570
- /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13851
+ /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13571
13852
  "div",
13572
13853
  {
13573
13854
  "data-slot": "kanban",
@@ -13578,7 +13859,7 @@ function Kanban({
13578
13859
  density === "compact" ? "gap-3" : "gap-4",
13579
13860
  className
13580
13861
  ),
13581
- children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13862
+ children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13582
13863
  KanbanColumnView,
13583
13864
  {
13584
13865
  column,
@@ -13598,7 +13879,7 @@ function Kanban({
13598
13879
  ))
13599
13880
  }
13600
13881
  ),
13601
- /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(import_core2.DragOverlay, { children: activeCard ? /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(
13882
+ /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(import_core2.DragOverlay, { children: activeCard ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
13602
13883
  "div",
13603
13884
  {
13604
13885
  "data-slot": "kanban-card-overlay",
@@ -13747,8 +14028,8 @@ When you call a tool that returns an artifact (\`make_chart\`, \`ask_question\`,
13747
14028
  `.trim();
13748
14029
 
13749
14030
  // src/auth/guard.tsx
13750
- var import_lucide_react28 = require("lucide-react");
13751
- var import_jsx_runtime116 = require("react/jsx-runtime");
14031
+ var import_lucide_react29 = require("lucide-react");
14032
+ var import_jsx_runtime117 = require("react/jsx-runtime");
13752
14033
  var AuthGuard = ({
13753
14034
  children,
13754
14035
  requireAuth = false,
@@ -13759,7 +14040,7 @@ var AuthGuard = ({
13759
14040
  return children;
13760
14041
  }
13761
14042
  if (loading) {
13762
- return /* @__PURE__ */ (0, import_jsx_runtime116.jsx)("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(import_lucide_react28.Loader2, { className: "w-8 h-8 animate-spin" }) });
14043
+ return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_lucide_react29.Loader2, { className: "w-8 h-8 animate-spin" }) });
13763
14044
  }
13764
14045
  if (requireAuth && !isAuthenticated && !isEmbedded) {
13765
14046
  const returnTo = encodeURIComponent(
@@ -13772,162 +14053,7 @@ var AuthGuard = ({
13772
14053
  };
13773
14054
 
13774
14055
  // src/index.ts
13775
- var import_react79 = require("@assistant-ui/react");
13776
-
13777
- // src/ui/select.tsx
13778
- var import_radix_ui10 = require("radix-ui");
13779
- var import_lucide_react29 = require("lucide-react");
13780
- var import_jsx_runtime117 = require("react/jsx-runtime");
13781
- function Select({
13782
- ...props
13783
- }) {
13784
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.Root, { "data-slot": "select", ...props });
13785
- }
13786
- function SelectGroup({
13787
- ...props
13788
- }) {
13789
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.Group, { "data-slot": "select-group", ...props });
13790
- }
13791
- function SelectValue({
13792
- ...props
13793
- }) {
13794
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.Value, { "data-slot": "select-value", ...props });
13795
- }
13796
- function SelectTrigger({
13797
- className,
13798
- size = "default",
13799
- children,
13800
- ...props
13801
- }) {
13802
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)(
13803
- import_radix_ui10.Select.Trigger,
13804
- {
13805
- "data-slot": "select-trigger",
13806
- "data-size": size,
13807
- className: cn(
13808
- controlClass({ size }),
13809
- "flex w-fit items-center justify-between gap-2 whitespace-nowrap *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
13810
- className
13811
- ),
13812
- ...props,
13813
- children: [
13814
- children,
13815
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_lucide_react29.ChevronDownIcon, { className: "size-4 opacity-50" }) })
13816
- ]
13817
- }
13818
- );
13819
- }
13820
- function SelectContent({
13821
- className,
13822
- children,
13823
- position = "popper",
13824
- ...props
13825
- }) {
13826
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)(
13827
- import_radix_ui10.Select.Content,
13828
- {
13829
- "data-slot": "select-content",
13830
- className: cn(
13831
- overlayListPanelClass,
13832
- "relative max-h-[var(--radix-select-content-available-height)] min-w-[8rem] origin-[var(--radix-select-content-transform-origin)] overflow-x-hidden overflow-y-auto",
13833
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
13834
- className
13835
- ),
13836
- position,
13837
- ...props,
13838
- children: [
13839
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(SelectScrollUpButton, {}),
13840
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
13841
- import_radix_ui10.Select.Viewport,
13842
- {
13843
- className: cn(
13844
- "p-1",
13845
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
13846
- ),
13847
- children
13848
- }
13849
- ),
13850
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(SelectScrollDownButton, {})
13851
- ]
13852
- }
13853
- ) });
13854
- }
13855
- function SelectLabel({
13856
- className,
13857
- ...props
13858
- }) {
13859
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
13860
- import_radix_ui10.Select.Label,
13861
- {
13862
- "data-slot": "select-label",
13863
- className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", className),
13864
- ...props
13865
- }
13866
- );
13867
- }
13868
- function SelectItem({
13869
- className,
13870
- children,
13871
- ...props
13872
- }) {
13873
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)(
13874
- import_radix_ui10.Select.Item,
13875
- {
13876
- "data-slot": "select-item",
13877
- className: cn(
13878
- overlayItemClass,
13879
- "w-full py-1.5 pr-8 pl-2 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
13880
- className
13881
- ),
13882
- ...props,
13883
- children: [
13884
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_lucide_react29.CheckIcon, { className: "size-4" }) }) }),
13885
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_radix_ui10.Select.ItemText, { children })
13886
- ]
13887
- }
13888
- );
13889
- }
13890
- function SelectSeparator({
13891
- className,
13892
- ...props
13893
- }) {
13894
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
13895
- import_radix_ui10.Select.Separator,
13896
- {
13897
- "data-slot": "select-separator",
13898
- className: cn("-mx-1 my-1 h-px bg-border", className),
13899
- ...props
13900
- }
13901
- );
13902
- }
13903
- function SelectScrollUpButton({
13904
- className,
13905
- ...props
13906
- }) {
13907
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
13908
- import_radix_ui10.Select.ScrollUpButton,
13909
- {
13910
- "data-slot": "select-scroll-up-button",
13911
- className: cn("flex cursor-default items-center justify-center py-1", className),
13912
- ...props,
13913
- children: /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_lucide_react29.ChevronUpIcon, { className: "size-4" })
13914
- }
13915
- );
13916
- }
13917
- function SelectScrollDownButton({
13918
- className,
13919
- ...props
13920
- }) {
13921
- return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
13922
- import_radix_ui10.Select.ScrollDownButton,
13923
- {
13924
- "data-slot": "select-scroll-down-button",
13925
- className: cn("flex cursor-default items-center justify-center py-1", className),
13926
- ...props,
13927
- children: /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_lucide_react29.ChevronDownIcon, { className: "size-4" })
13928
- }
13929
- );
13930
- }
14056
+ var import_react84 = require("@assistant-ui/react");
13931
14057
 
13932
14058
  // src/ui/input.tsx
13933
14059
  var import_jsx_runtime118 = require("react/jsx-runtime");
@@ -13961,7 +14087,7 @@ function Textarea({ className, ...props }) {
13961
14087
  "data-slot": "textarea",
13962
14088
  className: cn(
13963
14089
  controlSurfaceClass,
13964
- "min-h-16 w-full resize-y rounded-lg px-3 py-2 leading-relaxed",
14090
+ "min-h-16 w-full resize-y rounded-lg px-2.5 py-1.5 leading-relaxed",
13965
14091
  className
13966
14092
  ),
13967
14093
  ...props
@@ -14220,7 +14346,7 @@ function BreadcrumbEllipsis({
14220
14346
  "data-slot": "breadcrumb-ellipsis",
14221
14347
  role: "presentation",
14222
14348
  "aria-hidden": "true",
14223
- className: cn("flex size-9 items-center justify-center", className),
14349
+ className: cn("flex size-8 items-center justify-center", className),
14224
14350
  ...props,
14225
14351
  children: [
14226
14352
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_lucide_react31.MoreHorizontalIcon, { className: "size-4" }),
@@ -14356,7 +14482,7 @@ function ToolbarButton({
14356
14482
  {
14357
14483
  "data-slot": "toolbar-button",
14358
14484
  className: cn(
14359
- "inline-flex h-8 min-w-8 items-center justify-center rounded-md px-2 text-sm font-medium text-foreground outline-none transition-colors",
14485
+ "inline-flex h-7 min-w-7 items-center justify-center rounded-md px-1.5 text-sm font-medium text-foreground outline-none transition-colors",
14360
14486
  "hover:bg-muted focus-visible:ring-2 focus-visible:ring-foreground/10 disabled:pointer-events-none disabled:opacity-50",
14361
14487
  className
14362
14488
  ),
@@ -14399,7 +14525,7 @@ function ToolbarToggleItem({
14399
14525
  {
14400
14526
  "data-slot": "toolbar-toggle-item",
14401
14527
  className: cn(
14402
- "inline-flex h-8 min-w-8 items-center justify-center rounded-md px-2 text-sm outline-none transition-colors",
14528
+ "inline-flex h-7 min-w-7 items-center justify-center rounded-md px-1.5 text-sm outline-none transition-colors",
14403
14529
  "hover:bg-muted focus-visible:ring-2 focus-visible:ring-foreground/10",
14404
14530
  "data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
14405
14531
  className
@@ -14417,7 +14543,7 @@ function ToolbarLink({
14417
14543
  {
14418
14544
  "data-slot": "toolbar-link",
14419
14545
  className: cn(
14420
- "inline-flex h-8 items-center rounded-md px-2 text-sm text-foreground outline-none transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-foreground/10",
14546
+ "inline-flex h-7 items-center rounded-md px-1.5 text-sm text-foreground outline-none transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-foreground/10",
14421
14547
  className
14422
14548
  ),
14423
14549
  ...props
@@ -14438,7 +14564,7 @@ function Menubar({
14438
14564
  {
14439
14565
  "data-slot": "menubar",
14440
14566
  className: cn(
14441
- "flex h-9 items-center gap-1 rounded-lg border border-border bg-gradient-to-b from-elevated-from to-elevated-to p-1 shadow-card",
14567
+ "flex h-8 items-center gap-1 rounded-lg border border-border bg-gradient-to-b from-elevated-from to-elevated-to p-1 shadow-card",
14442
14568
  className
14443
14569
  ),
14444
14570
  ...props
@@ -14459,7 +14585,7 @@ function MenubarTrigger({
14459
14585
  {
14460
14586
  "data-slot": "menubar-trigger",
14461
14587
  className: cn(
14462
- "flex cursor-default items-center rounded-md px-2 py-1 text-sm font-medium outline-none select-none",
14588
+ "flex cursor-default items-center rounded-md px-2 py-0.5 text-sm font-medium outline-none select-none",
14463
14589
  "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
14464
14590
  className
14465
14591
  ),
@@ -14521,7 +14647,7 @@ function MenubarCheckboxItem({
14521
14647
  import_radix_ui18.Menubar.CheckboxItem,
14522
14648
  {
14523
14649
  "data-slot": "menubar-checkbox-item",
14524
- className: cn(overlayItemClass, "py-1.5 pr-2 pl-8", className),
14650
+ className: cn(overlayItemClass, "py-1 pr-2 pl-8", className),
14525
14651
  checked,
14526
14652
  ...props,
14527
14653
  children: [
@@ -14545,7 +14671,7 @@ function MenubarRadioItem({
14545
14671
  import_radix_ui18.Menubar.RadioItem,
14546
14672
  {
14547
14673
  "data-slot": "menubar-radio-item",
14548
- className: cn(overlayItemClass, "py-1.5 pr-2 pl-8", className),
14674
+ className: cn(overlayItemClass, "py-1 pr-2 pl-8", className),
14549
14675
  ...props,
14550
14676
  children: [
14551
14677
  /* @__PURE__ */ (0, import_jsx_runtime128.jsx)("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(import_radix_ui18.Menubar.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(import_lucide_react33.CircleIcon, { className: "size-2 fill-current" }) }) }),
@@ -14831,7 +14957,7 @@ function CommandDialog({
14831
14957
  {
14832
14958
  className: cn("overflow-hidden p-0", className),
14833
14959
  showCloseButton,
14834
- children: /* @__PURE__ */ (0, import_jsx_runtime130.jsx)(Command, { className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:size-4 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-2 [&_[cmdk-item]_svg]:size-4", children })
14960
+ children: /* @__PURE__ */ (0, import_jsx_runtime130.jsx)(Command, { className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:size-4 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-1.5 [&_[cmdk-item]_svg]:size-4", children })
14835
14961
  }
14836
14962
  )
14837
14963
  ] });
@@ -14844,7 +14970,7 @@ function CommandInput({
14844
14970
  "div",
14845
14971
  {
14846
14972
  "data-slot": "command-input-wrapper",
14847
- className: "flex h-10 items-center gap-2 border-b border-border px-3",
14973
+ className: "flex h-9 items-center gap-2 border-b border-border px-2.5",
14848
14974
  children: [
14849
14975
  /* @__PURE__ */ (0, import_jsx_runtime130.jsx)(import_lucide_react35.SearchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
14850
14976
  /* @__PURE__ */ (0, import_jsx_runtime130.jsx)(
@@ -14852,7 +14978,7 @@ function CommandInput({
14852
14978
  {
14853
14979
  "data-slot": "command-input",
14854
14980
  className: cn(
14855
- "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground/70 disabled:cursor-not-allowed disabled:opacity-50",
14981
+ "flex h-9 w-full rounded-md bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground/70 disabled:cursor-not-allowed disabled:opacity-50",
14856
14982
  className
14857
14983
  ),
14858
14984
  ...props
@@ -14882,7 +15008,7 @@ function CommandEmpty({
14882
15008
  import_cmdk.Command.Empty,
14883
15009
  {
14884
15010
  "data-slot": "command-empty",
14885
- className: "py-6 text-center text-sm text-muted-foreground",
15011
+ className: "py-4 text-center text-sm text-muted-foreground",
14886
15012
  ...props
14887
15013
  }
14888
15014
  );
@@ -14896,7 +15022,7 @@ function CommandGroup({
14896
15022
  {
14897
15023
  "data-slot": "command-group",
14898
15024
  className: cn(
14899
- "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
15025
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
14900
15026
  className
14901
15027
  ),
14902
15028
  ...props
@@ -14978,12 +15104,12 @@ function Calendar({
14978
15104
  button_previous: cn(navButtonClass, defaults.button_previous),
14979
15105
  button_next: cn(navButtonClass, defaults.button_next),
14980
15106
  month_caption: cn(
14981
- "flex h-10 items-center justify-center",
15107
+ "flex h-9 items-center justify-center",
14982
15108
  defaults.month_caption
14983
15109
  ),
14984
15110
  caption_label: cn("text-sm font-semibold", defaults.caption_label),
14985
15111
  dropdowns: cn(
14986
- "flex h-10 items-center justify-center gap-1.5 text-sm font-semibold",
15112
+ "flex h-9 items-center justify-center gap-1.5 text-sm font-semibold",
14987
15113
  defaults.dropdowns
14988
15114
  ),
14989
15115
  dropdown_root: cn(
@@ -14994,17 +15120,17 @@ function Calendar({
14994
15120
  month_grid: cn("border-separate border-spacing-y-1", defaults.month_grid),
14995
15121
  weekdays: cn(defaults.weekdays),
14996
15122
  weekday: cn(
14997
- "size-10 pb-2 text-xs font-medium text-muted-foreground",
15123
+ "size-9 pb-2 text-xs font-medium text-muted-foreground",
14998
15124
  defaults.weekday
14999
15125
  ),
15000
15126
  week: cn(defaults.week),
15001
- week_number_header: cn("size-10", defaults.week_number_header),
15127
+ week_number_header: cn("size-9", defaults.week_number_header),
15002
15128
  week_number: cn(
15003
15129
  "text-xs text-muted-foreground",
15004
15130
  defaults.week_number
15005
15131
  ),
15006
15132
  day: cn(
15007
- "relative size-10 p-0 text-center text-sm focus-within:relative focus-within:z-10",
15133
+ "relative size-9 p-0 text-center text-sm focus-within:relative focus-within:z-10",
15008
15134
  defaults.day
15009
15135
  ),
15010
15136
  range_start: cn("rounded-l-md", defaults.range_start),
@@ -15302,7 +15428,7 @@ function InputOTPSlot({
15302
15428
  "data-slot": "input-otp-slot",
15303
15429
  className: cn(
15304
15430
  controlSurfaceClass,
15305
- "relative size-9 rounded-none text-center text-sm tabular-nums",
15431
+ "relative size-8 rounded-none text-center text-sm tabular-nums",
15306
15432
  "-ms-px first:ms-0 first:rounded-s-lg last:rounded-e-lg",
15307
15433
  "focus-visible:z-10",
15308
15434
  className
@@ -15503,7 +15629,7 @@ function AccordionTrigger({
15503
15629
  {
15504
15630
  "data-slot": "accordion-trigger",
15505
15631
  className: cn(
15506
- "flex flex-1 items-center justify-between gap-4 rounded-md py-4 text-left text-sm font-medium outline-none transition-all",
15632
+ "flex flex-1 items-center justify-between gap-4 rounded-md py-3 text-left text-sm font-medium outline-none transition-all",
15507
15633
  "focus-visible:ring-2 focus-visible:ring-foreground/10 disabled:pointer-events-none disabled:opacity-50",
15508
15634
  "hover:underline [&[data-state=open]>svg]:rotate-180",
15509
15635
  className
@@ -15527,7 +15653,7 @@ function AccordionContent({
15527
15653
  "data-slot": "accordion-content",
15528
15654
  className: "overflow-hidden text-sm text-muted-foreground data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
15529
15655
  ...props,
15530
- children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)("div", { className: cn("pt-0 pb-4", className), children })
15656
+ children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)("div", { className: cn("pt-0 pb-3", className), children })
15531
15657
  }
15532
15658
  );
15533
15659
  }
@@ -15728,10 +15854,10 @@ var sheetContentVariants = (0, import_class_variance_authority5.cva)(
15728
15854
  {
15729
15855
  variants: {
15730
15856
  side: {
15731
- top: "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top fixed top-4 inset-x-4 mx-auto w-[calc(100vw-2rem)] sm:max-w-lg rounded-2xl p-6",
15732
- bottom: "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom fixed bottom-4 inset-x-4 mx-auto w-[calc(100vw-2rem)] sm:max-w-lg rounded-2xl p-6",
15733
- left: "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left fixed top-4 bottom-4 left-4 w-[calc(100vw-2rem)] rounded-2xl p-6",
15734
- right: "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right fixed top-4 bottom-4 right-4 w-[calc(100vw-2rem)] rounded-2xl p-6"
15857
+ top: "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top fixed top-4 inset-x-4 mx-auto w-[calc(100vw-2rem)] sm:max-w-lg rounded-2xl p-5",
15858
+ bottom: "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom fixed bottom-4 inset-x-4 mx-auto w-[calc(100vw-2rem)] sm:max-w-lg rounded-2xl p-5",
15859
+ left: "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left fixed top-4 bottom-4 left-4 w-[calc(100vw-2rem)] rounded-2xl p-5",
15860
+ right: "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right fixed top-4 bottom-4 right-4 w-[calc(100vw-2rem)] rounded-2xl p-5"
15735
15861
  },
15736
15862
  size: {
15737
15863
  default: "",
@@ -16375,7 +16501,7 @@ function ContextMenuSubContent({
16375
16501
  var import_class_variance_authority7 = require("class-variance-authority");
16376
16502
  var import_jsx_runtime151 = require("react/jsx-runtime");
16377
16503
  var alertVariants = (0, import_class_variance_authority7.cva)(
16378
- "relative grid w-full gap-1 rounded-xl border px-4 py-3 text-sm [&>svg]:absolute [&>svg]:top-3.5 [&>svg]:left-4 [&>svg]:size-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11",
16504
+ "relative grid w-full gap-1 rounded-xl border px-3.5 py-2.5 text-sm [&>svg]:absolute [&>svg]:top-3 [&>svg]:left-3.5 [&>svg]:size-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-10",
16379
16505
  {
16380
16506
  variants: {
16381
16507
  variant: {
@@ -16433,7 +16559,7 @@ function Card({ className, ...props }) {
16433
16559
  "data-slot": "card",
16434
16560
  className: cn(
16435
16561
  TIMBAL_V2_ELEVATED_SURFACE,
16436
- "flex flex-col gap-4 rounded-xl py-4 text-card-foreground",
16562
+ "flex flex-col gap-3 rounded-xl py-3 text-card-foreground",
16437
16563
  className
16438
16564
  ),
16439
16565
  ...props
@@ -16445,7 +16571,7 @@ function CardHeader({ className, ...props }) {
16445
16571
  "div",
16446
16572
  {
16447
16573
  "data-slot": "card-header",
16448
- className: cn("flex flex-col gap-1.5 px-4", className),
16574
+ className: cn("flex flex-col gap-1.5 px-3.5", className),
16449
16575
  ...props
16450
16576
  }
16451
16577
  );
@@ -16471,14 +16597,14 @@ function CardDescription({ className, ...props }) {
16471
16597
  );
16472
16598
  }
16473
16599
  function CardContent({ className, ...props }) {
16474
- return /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { "data-slot": "card-content", className: cn("px-4", className), ...props });
16600
+ return /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { "data-slot": "card-content", className: cn("px-3.5", className), ...props });
16475
16601
  }
16476
16602
  function CardFooter({ className, ...props }) {
16477
16603
  return /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(
16478
16604
  "div",
16479
16605
  {
16480
16606
  "data-slot": "card-footer",
16481
- className: cn("flex items-center px-4", className),
16607
+ className: cn("flex items-center px-3.5", className),
16482
16608
  ...props
16483
16609
  }
16484
16610
  );
@@ -16548,7 +16674,7 @@ function TableHead({ className, ...props }) {
16548
16674
  {
16549
16675
  "data-slot": "table-head",
16550
16676
  className: cn(
16551
- "h-10 px-3 text-left align-middle text-xs font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
16677
+ "h-8 px-2.5 text-left align-middle text-xs font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
16552
16678
  className
16553
16679
  ),
16554
16680
  ...props
@@ -16561,7 +16687,7 @@ function TableCell({ className, ...props }) {
16561
16687
  {
16562
16688
  "data-slot": "table-cell",
16563
16689
  className: cn(
16564
- "p-3 align-middle text-sm [&:has([role=checkbox])]:pr-0",
16690
+ "py-2 px-2.5 align-middle text-sm [&:has([role=checkbox])]:pr-0",
16565
16691
  className
16566
16692
  ),
16567
16693
  ...props
@@ -16615,7 +16741,7 @@ function Toast({
16615
16741
  "data-slot": "toast",
16616
16742
  className: cn(
16617
16743
  TIMBAL_V2_ELEVATED_SURFACE,
16618
- "group pointer-events-auto relative flex w-full items-center justify-between gap-3 overflow-hidden rounded-xl p-4 pr-8 shadow-card-elevated transition-all",
16744
+ "group pointer-events-auto relative flex w-full items-center justify-between gap-3 overflow-hidden rounded-xl p-3 pr-7 shadow-card-elevated transition-all",
16619
16745
  "data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full",
16620
16746
  variant === "destructive" && "border-destructive/45 bg-destructive/10 text-destructive",
16621
16747
  className
@@ -16936,7 +17062,7 @@ function Rating({
16936
17062
  const value = isControlled ? valueProp : uncontrolled;
16937
17063
  const [hover, setHover] = React11.useState(null);
16938
17064
  const interactive = !readOnly && !disabled;
16939
- const shown = hover ?? value;
17065
+ const shown2 = hover ?? value;
16940
17066
  const set = (next) => {
16941
17067
  if (!interactive) return;
16942
17068
  if (!isControlled) setUncontrolled(next);
@@ -16974,7 +17100,7 @@ function Rating({
16974
17100
  onMouseLeave: () => setHover(null),
16975
17101
  children: Array.from({ length: max }, (_, i) => {
16976
17102
  const unit = i + 1;
16977
- const filled = unit <= shown;
17103
+ const filled = unit <= shown2;
16978
17104
  return /* @__PURE__ */ (0, import_jsx_runtime158.jsx)(
16979
17105
  "button",
16980
17106
  {
@@ -17107,8 +17233,8 @@ var React13 = __toESM(require("react"), 1);
17107
17233
  var import_lucide_react47 = require("lucide-react");
17108
17234
  var import_jsx_runtime160 = require("react/jsx-runtime");
17109
17235
  var tagInputSizeClass = {
17110
- sm: "min-h-9 gap-1 px-1.5 py-1",
17111
- default: "min-h-10 gap-1.5 px-2 py-1.5"
17236
+ sm: "min-h-8 gap-1 px-1.5 py-0.5",
17237
+ default: "min-h-9 gap-1 px-2 py-1"
17112
17238
  };
17113
17239
  function TagInput({
17114
17240
  value: valueProp,
@@ -17216,8 +17342,8 @@ var snippetVariantClass = {
17216
17342
  ghost: "border-transparent bg-foreground/[0.04]"
17217
17343
  };
17218
17344
  var snippetSizeClass = {
17219
- sm: "gap-1.5 py-1 pl-2.5 pr-1 text-xs",
17220
- default: "gap-2 py-1.5 pl-3 pr-1.5 text-sm"
17345
+ sm: "gap-1.5 py-0.5 pl-2 pr-0.5 text-xs",
17346
+ default: "gap-2 py-1 pl-2.5 pr-1 text-sm"
17221
17347
  };
17222
17348
  function Snippet({
17223
17349
  children,
@@ -17333,6 +17459,373 @@ function CircularProgress({
17333
17459
  }
17334
17460
  );
17335
17461
  }
17462
+
17463
+ // src/site/Reveal.tsx
17464
+ var React14 = __toESM(require("react"), 1);
17465
+ var import_react79 = require("motion/react");
17466
+
17467
+ // src/site/easing.ts
17468
+ var EASE = {
17469
+ /** Strong slow-out — the workhorse for entrances and reveals. */
17470
+ out: [0.16, 1, 0.3, 1],
17471
+ /** Symmetric in-out for loops and continuous motion. */
17472
+ inOut: [0.65, 0, 0.35, 1],
17473
+ /** Gentle in-out, good for parallax / large translations. */
17474
+ soft: [0.4, 0, 0.2, 1]
17475
+ };
17476
+ var DURATION = {
17477
+ fast: 0.4,
17478
+ base: 0.7,
17479
+ slow: 1.1
17480
+ };
17481
+ var SPRING = {
17482
+ /** Tight, responsive follow. */
17483
+ snappy: { stiffness: 350, damping: 30, mass: 0.4 },
17484
+ /** Looser, more elastic follow. */
17485
+ smooth: { stiffness: 150, damping: 20, mass: 0.6 }
17486
+ };
17487
+
17488
+ // src/site/Reveal.tsx
17489
+ var import_jsx_runtime163 = require("react/jsx-runtime");
17490
+ function hidden(variant, distance) {
17491
+ switch (variant) {
17492
+ case "fade":
17493
+ return { opacity: 0 };
17494
+ case "fade-up":
17495
+ return { opacity: 0, y: distance };
17496
+ case "fade-down":
17497
+ return { opacity: 0, y: -distance };
17498
+ case "fade-left":
17499
+ return { opacity: 0, x: distance };
17500
+ case "fade-right":
17501
+ return { opacity: 0, x: -distance };
17502
+ case "blur":
17503
+ return { opacity: 0, filter: "blur(12px)" };
17504
+ case "scale":
17505
+ return { opacity: 0, scale: 0.94 };
17506
+ case "mask-up":
17507
+ return { y: "110%" };
17508
+ }
17509
+ }
17510
+ function shown(variant) {
17511
+ if (variant === "mask-up") return { y: "0%" };
17512
+ if (variant === "blur") return { opacity: 1, filter: "blur(0px)" };
17513
+ if (variant === "scale") return { opacity: 1, scale: 1 };
17514
+ return { opacity: 1, x: 0, y: 0 };
17515
+ }
17516
+ var Reveal = React14.forwardRef(function Reveal2({
17517
+ variant = "fade-up",
17518
+ delay = 0,
17519
+ duration = DURATION.base,
17520
+ distance = 28,
17521
+ amount = 0.3,
17522
+ repeat = false,
17523
+ as = "div",
17524
+ className,
17525
+ children,
17526
+ ...rest
17527
+ }, ref) {
17528
+ const reduce = (0, import_react79.useReducedMotion)();
17529
+ const isMask = variant === "mask-up";
17530
+ if (reduce) {
17531
+ const Tag = as;
17532
+ return /* @__PURE__ */ (0, import_jsx_runtime163.jsx)(Tag, { ref, className: cn(isMask && "overflow-hidden", className), ...rest, children });
17533
+ }
17534
+ const variants = {
17535
+ hidden: hidden(variant, distance),
17536
+ shown: shown(variant)
17537
+ };
17538
+ const MotionTag = import_react79.motion[as] ?? import_react79.motion.div;
17539
+ const inner = /* @__PURE__ */ (0, import_jsx_runtime163.jsx)(
17540
+ MotionTag,
17541
+ {
17542
+ ref,
17543
+ className,
17544
+ variants,
17545
+ initial: "hidden",
17546
+ whileInView: "shown",
17547
+ viewport: { once: !repeat, amount },
17548
+ transition: { duration, delay, ease: EASE.out },
17549
+ ...rest,
17550
+ children
17551
+ }
17552
+ );
17553
+ if (isMask) {
17554
+ return /* @__PURE__ */ (0, import_jsx_runtime163.jsx)("span", { className: "block overflow-hidden", children: inner });
17555
+ }
17556
+ return inner;
17557
+ });
17558
+
17559
+ // src/site/TextReveal.tsx
17560
+ var React15 = __toESM(require("react"), 1);
17561
+ var import_react80 = require("motion/react");
17562
+ var import_jsx_runtime164 = require("react/jsx-runtime");
17563
+ var tokenVariants = {
17564
+ hidden: { y: "115%" },
17565
+ shown: { y: "0%" }
17566
+ };
17567
+ function TextReveal({
17568
+ children,
17569
+ splitBy = "words",
17570
+ stagger = 0.06,
17571
+ delay = 0,
17572
+ duration = DURATION.base,
17573
+ repeat = false,
17574
+ amount = 0.4,
17575
+ as = "span",
17576
+ className,
17577
+ ...rest
17578
+ }) {
17579
+ const reduce = (0, import_react80.useReducedMotion)();
17580
+ const Tag = as;
17581
+ const tokens = React15.useMemo(() => {
17582
+ if (splitBy === "lines") return children.split("\n");
17583
+ return children.split(/(\s+)/).filter((t) => t.length > 0);
17584
+ }, [children, splitBy]);
17585
+ if (reduce) {
17586
+ return /* @__PURE__ */ (0, import_jsx_runtime164.jsx)(Tag, { className, ...rest, children });
17587
+ }
17588
+ const containerVariants = {
17589
+ hidden: {},
17590
+ shown: { transition: { staggerChildren: stagger, delayChildren: delay } }
17591
+ };
17592
+ return /* @__PURE__ */ (0, import_jsx_runtime164.jsx)(
17593
+ import_react80.motion.span,
17594
+ {
17595
+ variants: containerVariants,
17596
+ initial: "hidden",
17597
+ whileInView: "shown",
17598
+ viewport: { once: !repeat, amount },
17599
+ className: cn(as === "span" ? "inline" : "block", className),
17600
+ ...rest,
17601
+ children: /* @__PURE__ */ (0, import_jsx_runtime164.jsx)(Tag, { className: as === "span" ? "inline" : "block", children: tokens.map(
17602
+ (token, i) => /^\s+$/.test(token) && splitBy === "words" ? /* @__PURE__ */ (0, import_jsx_runtime164.jsx)("span", { children: " " }, i) : /* @__PURE__ */ (0, import_jsx_runtime164.jsx)(
17603
+ "span",
17604
+ {
17605
+ className: cn(
17606
+ "overflow-hidden",
17607
+ splitBy === "lines" ? "block" : "inline-block align-bottom"
17608
+ ),
17609
+ children: /* @__PURE__ */ (0, import_jsx_runtime164.jsx)(
17610
+ import_react80.motion.span,
17611
+ {
17612
+ className: "inline-block",
17613
+ variants: tokenVariants,
17614
+ transition: { duration, ease: EASE.out },
17615
+ children: token
17616
+ }
17617
+ )
17618
+ },
17619
+ i
17620
+ )
17621
+ ) })
17622
+ }
17623
+ );
17624
+ }
17625
+
17626
+ // src/site/Parallax.tsx
17627
+ var React16 = __toESM(require("react"), 1);
17628
+ var import_react81 = require("motion/react");
17629
+ var import_jsx_runtime165 = require("react/jsx-runtime");
17630
+ var Parallax = React16.forwardRef(function Parallax2({ speed = 0.2, axis = "y", smooth = true, className, children, style, ...rest }, forwardedRef) {
17631
+ const reduce = (0, import_react81.useReducedMotion)();
17632
+ const innerRef = React16.useRef(null);
17633
+ React16.useImperativeHandle(forwardedRef, () => innerRef.current);
17634
+ const { scrollYProgress } = (0, import_react81.useScroll)({
17635
+ target: innerRef,
17636
+ offset: ["start end", "end start"]
17637
+ });
17638
+ const range = 100 * speed;
17639
+ const raw = (0, import_react81.useTransform)(scrollYProgress, [0, 1], [range, -range]);
17640
+ const smoothed = (0, import_react81.useSpring)(raw, { stiffness: 120, damping: 30, mass: 0.4 });
17641
+ const value = smooth ? smoothed : raw;
17642
+ if (reduce) {
17643
+ return /* @__PURE__ */ (0, import_jsx_runtime165.jsx)("div", { ref: innerRef, className, style, ...rest, children });
17644
+ }
17645
+ return /* @__PURE__ */ (0, import_jsx_runtime165.jsx)(
17646
+ import_react81.motion.div,
17647
+ {
17648
+ ref: innerRef,
17649
+ className: cn("will-change-transform", className),
17650
+ style: { ...style, [axis]: value },
17651
+ ...rest,
17652
+ children
17653
+ }
17654
+ );
17655
+ });
17656
+
17657
+ // src/site/Marquee.tsx
17658
+ var React17 = __toESM(require("react"), 1);
17659
+ var import_react82 = require("motion/react");
17660
+ var import_jsx_runtime166 = require("react/jsx-runtime");
17661
+ var Marquee = React17.forwardRef(function Marquee2({ speed = 60, direction = "left", pauseOnHover = true, gap = "3rem", className, children, ...rest }, ref) {
17662
+ const reduce = (0, import_react82.useReducedMotion)();
17663
+ const x = (0, import_react82.useMotionValue)(0);
17664
+ const setWidthRef = React17.useRef(0);
17665
+ const groupRef = React17.useRef(null);
17666
+ const [paused, setPaused] = React17.useState(false);
17667
+ React17.useEffect(() => {
17668
+ const el = groupRef.current;
17669
+ if (!el) return;
17670
+ const measure = () => {
17671
+ const gapPx = parseFloat(getComputedStyle(el.parentElement).columnGap || "0") || 0;
17672
+ setWidthRef.current = el.offsetWidth + gapPx;
17673
+ };
17674
+ measure();
17675
+ const ro = new ResizeObserver(measure);
17676
+ ro.observe(el);
17677
+ return () => ro.disconnect();
17678
+ }, []);
17679
+ (0, import_react82.useAnimationFrame)((_, delta) => {
17680
+ if (reduce || paused || setWidthRef.current === 0) return;
17681
+ const dir = direction === "left" ? -1 : 1;
17682
+ const moveBy = speed * delta / 1e3;
17683
+ let next = x.get() + dir * moveBy;
17684
+ const w = setWidthRef.current;
17685
+ if (next <= -w) next += w;
17686
+ else if (next >= w) next -= w;
17687
+ x.set(next);
17688
+ });
17689
+ if (reduce) {
17690
+ return /* @__PURE__ */ (0, import_jsx_runtime166.jsx)(
17691
+ "div",
17692
+ {
17693
+ ref,
17694
+ className: cn("flex w-full items-center overflow-x-auto", className),
17695
+ style: { columnGap: gap },
17696
+ ...rest,
17697
+ children
17698
+ }
17699
+ );
17700
+ }
17701
+ return /* @__PURE__ */ (0, import_jsx_runtime166.jsx)(
17702
+ "div",
17703
+ {
17704
+ ref,
17705
+ className: cn("w-full overflow-hidden", className),
17706
+ onMouseEnter: pauseOnHover ? () => setPaused(true) : void 0,
17707
+ onMouseLeave: pauseOnHover ? () => setPaused(false) : void 0,
17708
+ ...rest,
17709
+ children: /* @__PURE__ */ (0, import_jsx_runtime166.jsxs)(import_react82.motion.div, { className: "flex w-max flex-nowrap items-center", style: { x, columnGap: gap }, children: [
17710
+ /* @__PURE__ */ (0, import_jsx_runtime166.jsx)("div", { ref: groupRef, className: "flex flex-nowrap items-center", style: { columnGap: gap }, children }),
17711
+ /* @__PURE__ */ (0, import_jsx_runtime166.jsx)("div", { className: "flex flex-nowrap items-center", style: { columnGap: gap }, "aria-hidden": true, children })
17712
+ ] })
17713
+ }
17714
+ );
17715
+ });
17716
+
17717
+ // src/site/Magnetic.tsx
17718
+ var React18 = __toESM(require("react"), 1);
17719
+ var import_react83 = require("motion/react");
17720
+ var import_jsx_runtime167 = require("react/jsx-runtime");
17721
+ var Magnetic = React18.forwardRef(function Magnetic2({ strength = 0.35, max = 24, spring = "snappy", className, children, ...rest }, forwardedRef) {
17722
+ const reduce = (0, import_react83.useReducedMotion)();
17723
+ const innerRef = React18.useRef(null);
17724
+ React18.useImperativeHandle(forwardedRef, () => innerRef.current);
17725
+ const mvx = (0, import_react83.useMotionValue)(0);
17726
+ const mvy = (0, import_react83.useMotionValue)(0);
17727
+ const x = (0, import_react83.useSpring)(mvx, SPRING[spring]);
17728
+ const y = (0, import_react83.useSpring)(mvy, SPRING[spring]);
17729
+ const clamp3 = (v) => Math.max(-max, Math.min(max, v));
17730
+ function handleMove(e) {
17731
+ if (reduce) return;
17732
+ const el = innerRef.current;
17733
+ if (!el) return;
17734
+ const rect = el.getBoundingClientRect();
17735
+ const cx = rect.left + rect.width / 2;
17736
+ const cy = rect.top + rect.height / 2;
17737
+ mvx.set(clamp3((e.clientX - cx) * strength));
17738
+ mvy.set(clamp3((e.clientY - cy) * strength));
17739
+ }
17740
+ function reset() {
17741
+ mvx.set(0);
17742
+ mvy.set(0);
17743
+ }
17744
+ if (reduce) {
17745
+ return /* @__PURE__ */ (0, import_jsx_runtime167.jsx)("div", { ref: innerRef, className: cn("inline-block", className), ...rest, children });
17746
+ }
17747
+ return /* @__PURE__ */ (0, import_jsx_runtime167.jsx)(
17748
+ import_react83.motion.div,
17749
+ {
17750
+ ref: innerRef,
17751
+ className: cn("inline-block", className),
17752
+ style: { x, y },
17753
+ onMouseMove: handleMove,
17754
+ onMouseLeave: reset,
17755
+ ...rest,
17756
+ children
17757
+ }
17758
+ );
17759
+ });
17760
+
17761
+ // src/site/agent-instructions.ts
17762
+ var SITE_AGENT_INSTRUCTIONS = `
17763
+ ## Site kit (@timbal-ai/timbal-react/site)
17764
+
17765
+ Expressive **motion & interaction primitives** for marketing, brand, landing, and editorial pages \u2014 the counterpart to \`/app\` (which is for dashboards and operations UIs). Import from \`@timbal-ai/timbal-react/site\` (or the package root).
17766
+
17767
+ These are **mechanics, not art direction**: they animate whatever you put inside them. Compose them under a chosen aesthetic; they do not impose colors, type, or layout.
17768
+
17769
+ ### When to use \`/site\` (and when not to)
17770
+
17771
+ - **Do** use \`/site\` for landing pages, hero sections, feature walkthroughs, logo walls, pricing pages, and editorial/brand storytelling.
17772
+ - **Do not** use \`/site\` inside dashboards, settings, tables, or in-thread chat artifacts \u2014 those stay on \`/app\` and \`/artifacts\`. Animated dashboard chrome reads as slop.
17773
+ - Every primitive is **reduced-motion-aware** (collapses to static, no-transform output under \`prefers-reduced-motion\`) and **SSR-safe** (no layout shift \u2014 elements occupy their space from first paint). You never need to guard them yourself.
17774
+ - Built on the \`motion\` engine the package already bundles \u2014 **no extra dependencies**.
17775
+
17776
+ ### Dosing (anti-overuse \u2014 read this)
17777
+
17778
+ The failure mode is animating *everything*. Restraint is the brand signal.
17779
+
17780
+ - Pick **one** signature motion per section (e.g. a \`TextReveal\` headline **or** a \`Parallax\` hero image, not both stacked).
17781
+ - Stagger entrances down the page; do not fire ten reveals at once on first paint.
17782
+ - Reserve \`Magnetic\` for **primary** CTAs / nav, not every button.
17783
+ - Keep \`Parallax\` \`speed\` subtle (\`0.15\`\u2013\`0.35\`); large values look gimmicky.
17784
+ - Default durations are intentionally slow and weighted (\`DURATION.base\` = 0.7s) \u2014 that confident pacing is the point. Don't speed everything up to app-kit's 150ms.
17785
+
17786
+ ### Component menu
17787
+
17788
+ | Component | Use for | Key props |
17789
+ |-----------|---------|-----------|
17790
+ | \`Reveal\` | Fade/slide a block in as it scrolls into view (headings, cards, images, list items). | \`variant\` (\`fade\` \\| \`fade-up\` \\| \`fade-down\` \\| \`fade-left\` \\| \`fade-right\` \\| \`blur\` \\| \`scale\` \\| \`mask-up\`, default \`fade-up\`), \`delay\`, \`duration\`, \`distance\` (px, default 28), \`amount\` (visibility fraction 0\u20131 / \`"some"\` / \`"all"\`, default 0.3), \`repeat\` (replay on re-enter), \`as\` (render element, e.g. \`"section"\`/\`"li"\`). |
17791
+ | \`TextReveal\` | Signature editorial headline entrance \u2014 text rides up token-by-token from a clip on a stagger. | \`children\` (**plain string only**), \`splitBy\` (\`words\` default \\| \`lines\`), \`stagger\` (default 0.06), \`delay\`, \`duration\`, \`amount\` (default 0.4), \`repeat\`, \`as\` (\`span\` default \\| \`h1\`\u2013\`h4\` \\| \`p\`). |
17792
+ | \`Parallax\` | Depth \u2014 translate a layer relative to scroll (hero images, background art). | \`speed\` (-0.6\u20260.6, positive = lags behind scroll, default 0.2), \`axis\` (\`y\` default \\| \`x\`), \`smooth\` (spring-damped, default true). |
17793
+ | \`Marquee\` | Seamless infinite scrolling row (logo walls, testimonials, ticker). Duplicates children internally \u2014 no visible seam. | \`speed\` (px/s, default 60), \`direction\` (\`left\` default \\| \`right\`), \`pauseOnHover\` (default true), \`gap\` (CSS length, default \`"3rem"\`). |
17794
+ | \`Magnetic\` | Pointer-following "magnetic" affordance for a **single** interactive child (primary CTA, nav link). | \`strength\` (fraction of cursor offset, default 0.35), \`max\` (px clamp, default 24), \`spring\` (\`"snappy"\` default \\| \`"smooth"\`). Wrap one button/link. |
17795
+
17796
+ ### Motion tokens
17797
+
17798
+ \`EASE\` (cubic-bezier tuples: \`out\` / \`inOut\` / \`soft\`), \`DURATION\` (\`fast\` 0.4s / \`base\` 0.7s / \`slow\` 1.1s), and \`SPRING\` (\`snappy\` / \`smooth\`) are exported for custom \`motion\` work that should match the kit's feel. Prefer the component defaults; reach for tokens only when hand-rolling a bespoke animation.
17799
+
17800
+ ### Example imports
17801
+
17802
+ \`\`\`tsx
17803
+ import { Reveal, TextReveal, Parallax, Marquee, Magnetic } from "@timbal-ai/timbal-react/site";
17804
+ \`\`\`
17805
+
17806
+ \`\`\`tsx
17807
+ <Reveal variant="fade-up" delay={0.1}>
17808
+ <TextReveal as="h1" className="text-6xl font-semibold">
17809
+ Built for the long run
17810
+ </TextReveal>
17811
+ </Reveal>
17812
+
17813
+ <Parallax speed={0.3}>
17814
+ <img src={hero} alt="" className="h-full w-full object-cover" />
17815
+ </Parallax>
17816
+
17817
+ <Magnetic strength={0.4}>
17818
+ <Button>Get started</Button>
17819
+ </Magnetic>
17820
+ \`\`\`
17821
+
17822
+ ### Rules
17823
+
17824
+ - \`/site\` is for the **marketing/brand surface**, not the product app shell (\`/app\`) or in-chat widgets (\`/artifacts\`).
17825
+ - \`TextReveal\` takes a **string** child only \u2014 it splits internally; do not pass JSX.
17826
+ - \`Magnetic\` wraps a **single** interactive child; don't wrap whole layouts.
17827
+ - Trust the reduced-motion / SSR handling \u2014 never add your own \`prefers-reduced-motion\` guards around these.
17828
+ `.trim();
17336
17829
  // Annotate the CommonJS export names for ESM import in node:
17337
17830
  0 && (module.exports = {
17338
17831
  APP_KIT_AGENT_INSTRUCTIONS,
@@ -17445,6 +17938,7 @@ function CircularProgress({
17445
17938
  ContextMenuTrigger,
17446
17939
  CopyButton,
17447
17940
  DEFAULT_UPLOAD_ACCEPT,
17941
+ DURATION,
17448
17942
  DangerZone,
17449
17943
  DangerZoneAction,
17450
17944
  DataTable,
@@ -17478,6 +17972,7 @@ function CircularProgress({
17478
17972
  DropdownMenuSubContent,
17479
17973
  DropdownMenuSubTrigger,
17480
17974
  DropdownMenuTrigger,
17975
+ EASE,
17481
17976
  EmptyState,
17482
17977
  ExpandableSection,
17483
17978
  Field,
@@ -17523,7 +18018,9 @@ function CircularProgress({
17523
18018
  KbdGroup,
17524
18019
  Label,
17525
18020
  LineAreaChart,
18021
+ Magnetic,
17526
18022
  MarkdownText,
18023
+ Marquee,
17527
18024
  MemoPillSegmentedTabs,
17528
18025
  Menubar,
17529
18026
  MenubarCheckboxItem,
@@ -17562,6 +18059,7 @@ function CircularProgress({
17562
18059
  PaginationLink,
17563
18060
  PaginationNext,
17564
18061
  PaginationPrevious,
18062
+ Parallax,
17565
18063
  PieChart,
17566
18064
  PillSegmentedTabs,
17567
18065
  PlanBadge,
@@ -17578,8 +18076,11 @@ function CircularProgress({
17578
18076
  RadioGroupItem,
17579
18077
  Rating,
17580
18078
  ResourceCard,
18079
+ Reveal,
17581
18080
  SEMANTIC_COLOR_TOKENS,
18081
+ SITE_AGENT_INSTRUCTIONS,
17582
18082
  SLOP_BUDGETS,
18083
+ SPRING,
17583
18084
  STUDIO_NAV_MODE,
17584
18085
  ScrollArea,
17585
18086
  ScrollBar,
@@ -17639,6 +18140,7 @@ function CircularProgress({
17639
18140
  TableHeader,
17640
18141
  TableRow,
17641
18142
  TagInput,
18143
+ TextReveal,
17642
18144
  Textarea,
17643
18145
  Thread,
17644
18146
  ThreadPrimitive,