@bmsuisse/ui 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29,7 +29,16 @@ var buttonVariants = cva(
29
29
  // Tailwind theme (this package doesn't define them) — same pattern as AlertBox's
30
30
  // warning/info/success colors, which assume tokens the shared theme doesn't have.
31
31
  "swiss-primary": "bg-swiss-primary text-swiss-primary-foreground font-semibold shadow-sm hover:bg-swiss-primary-hover hover:-translate-y-px active:translate-y-px",
32
- "swiss-secondary": "border border-input bg-background font-semibold text-foreground shadow-sm hover:bg-accent hover:-translate-y-px active:translate-y-px"
32
+ "swiss-secondary": "border border-input bg-background font-semibold text-foreground shadow-sm hover:bg-accent hover:-translate-y-px active:translate-y-px",
33
+ // Violet "AI action" accent, in the same three weights as default/outline/ghost.
34
+ // Fixed Tailwind palette rather than a token — same choice as Badge's `warning`
35
+ // and AlertBox/StatusBadge's warning/info/success tones: the shared theme has no
36
+ // `ai` color, and unlike `swiss-primary` these should work in a consuming app with
37
+ // zero setup. Tints go through `violet-500/N` (not per-mode `violet-50`/`-950`
38
+ // shades) so one class list reads correctly on both light and dark surfaces.
39
+ ai: "bg-violet-600 text-white shadow-sm hover:bg-violet-600/90",
40
+ "ai-subtle": "border border-violet-500/30 bg-violet-500/10 text-violet-700 hover:bg-violet-500/20 dark:text-violet-300",
41
+ "ai-ghost": "text-violet-700 hover:bg-violet-500/10 dark:text-violet-300"
33
42
  },
34
43
  size: {
35
44
  default: "h-9 px-3",
@@ -219,7 +228,7 @@ DialogDescription.displayName = "DialogDescription";
219
228
  var DialogFooter = ({
220
229
  className,
221
230
  ...props
222
- }) => /* @__PURE__ */ jsx7("div", { className: cn("flex justify-end gap-2", className), ...props });
231
+ }) => /* @__PURE__ */ jsx7("div", { className: cn("mt-4 flex justify-end gap-2", className), ...props });
223
232
 
224
233
  // src/primitives/popover.tsx
225
234
  import * as PopoverPrimitive from "@radix-ui/react-popover";
@@ -802,8 +811,42 @@ var Modal = ({
802
811
  ] }) });
803
812
 
804
813
  // src/patterns/modal/ConfirmDialog.tsx
805
- import { useState as useState2 } from "react";
806
- import { Fragment, jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
814
+ import { useState as useState3 } from "react";
815
+
816
+ // src/patterns/modal/DialogActions.tsx
817
+ import { Fragment, useState as useState2 } from "react";
818
+
819
+ // src/lib/platform.ts
820
+ function isWindowsPlatform() {
821
+ if (typeof navigator === "undefined") return false;
822
+ return /Windows/i.test(navigator.userAgent);
823
+ }
824
+ function getConfirmButtonPlacement() {
825
+ return isWindowsPlatform() ? "leading" : "trailing";
826
+ }
827
+
828
+ // src/patterns/modal/DialogActions.tsx
829
+ import { Fragment as Fragment2, jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
830
+ function DialogActions({ actions, cancel }) {
831
+ const [placement] = useState2(getConfirmButtonPlacement);
832
+ const cancelLeading = placement === "trailing";
833
+ const ordered = cancelLeading ? actions : [...actions].reverse();
834
+ const renderedActions = ordered.map((action, index) => /* @__PURE__ */ jsx21(Fragment, { children: action }, actionKey(action, index)));
835
+ return cancelLeading ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
836
+ cancel,
837
+ renderedActions
838
+ ] }) : /* @__PURE__ */ jsxs7(Fragment2, { children: [
839
+ renderedActions,
840
+ cancel
841
+ ] });
842
+ }
843
+ function actionKey(action, index) {
844
+ if (action && typeof action === "object" && "key" in action && action.key !== null) return action.key;
845
+ return index;
846
+ }
847
+
848
+ // src/patterns/modal/ConfirmDialog.tsx
849
+ import { jsx as jsx22 } from "react/jsx-runtime";
807
850
  var ConfirmDialog = ({
808
851
  open,
809
852
  onOpenChange,
@@ -816,7 +859,7 @@ var ConfirmDialog = ({
816
859
  confirmTestId,
817
860
  cancelTestId
818
861
  }) => {
819
- const [pending, setPending] = useState2(false);
862
+ const [pending, setPending] = useState3(false);
820
863
  const handleCancel = () => {
821
864
  onOpenChange(false);
822
865
  };
@@ -831,34 +874,33 @@ var ConfirmDialog = ({
831
874
  setPending(false);
832
875
  }
833
876
  };
834
- return /* @__PURE__ */ jsx21(
877
+ const cancelButton = /* @__PURE__ */ jsx22(Button, { variant: "outline", onClick: handleCancel, disabled: pending, "data-testid": cancelTestId, children: cancelLabel });
878
+ const confirmButton = /* @__PURE__ */ jsx22(
879
+ Button,
880
+ {
881
+ variant: variant === "destructive" ? "destructive" : "default",
882
+ onClick: handleConfirm,
883
+ disabled: pending,
884
+ "data-testid": confirmTestId,
885
+ children: pending ? `${confirmLabel}\u2026` : confirmLabel
886
+ }
887
+ );
888
+ return /* @__PURE__ */ jsx22(
835
889
  Modal,
836
890
  {
837
891
  open,
838
892
  onOpenChange,
839
893
  title,
840
894
  description,
841
- footer: /* @__PURE__ */ jsxs7(Fragment, { children: [
842
- /* @__PURE__ */ jsx21(Button, { variant: "outline", onClick: handleCancel, disabled: pending, "data-testid": cancelTestId, children: cancelLabel }),
843
- /* @__PURE__ */ jsx21(
844
- Button,
845
- {
846
- variant: variant === "destructive" ? "destructive" : "default",
847
- onClick: handleConfirm,
848
- disabled: pending,
849
- "data-testid": confirmTestId,
850
- children: pending ? `${confirmLabel}\u2026` : confirmLabel
851
- }
852
- )
853
- ] }),
895
+ footer: /* @__PURE__ */ jsx22(DialogActions, { actions: [confirmButton], cancel: cancelButton }),
854
896
  children: null
855
897
  }
856
898
  );
857
899
  };
858
900
 
859
901
  // src/patterns/modal/FormModal.tsx
860
- import { useState as useState3 } from "react";
861
- import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
902
+ import { useState as useState4 } from "react";
903
+ import { jsx as jsx23, jsxs as jsxs8 } from "react/jsx-runtime";
862
904
  var FormModal = ({
863
905
  open,
864
906
  onOpenChange,
@@ -873,7 +915,7 @@ var FormModal = ({
873
915
  submitTestId,
874
916
  cancelTestId
875
917
  }) => {
876
- const [pending, setPending] = useState3(false);
918
+ const [pending, setPending] = useState4(false);
877
919
  const handleSubmit = async (event) => {
878
920
  event.preventDefault();
879
921
  setPending(true);
@@ -886,14 +928,14 @@ var FormModal = ({
886
928
  const handleCancel = () => {
887
929
  onOpenChange(false);
888
930
  };
889
- return /* @__PURE__ */ jsx22(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx22(DialogContent, { className, children: /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, children: [
931
+ return /* @__PURE__ */ jsx23(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx23(DialogContent, { className, children: /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, children: [
890
932
  /* @__PURE__ */ jsxs8(DialogHeader, { children: [
891
- /* @__PURE__ */ jsx22(DialogTitle, { children: title }),
892
- description ? /* @__PURE__ */ jsx22(DialogDescription, { children: description }) : null
933
+ /* @__PURE__ */ jsx23(DialogTitle, { children: title }),
934
+ description ? /* @__PURE__ */ jsx23(DialogDescription, { children: description }) : null
893
935
  ] }),
894
936
  children,
895
937
  /* @__PURE__ */ jsxs8(DialogFooter, { children: [
896
- /* @__PURE__ */ jsx22(
938
+ /* @__PURE__ */ jsx23(
897
939
  Button,
898
940
  {
899
941
  type: "button",
@@ -904,18 +946,18 @@ var FormModal = ({
904
946
  children: cancelLabel
905
947
  }
906
948
  ),
907
- /* @__PURE__ */ jsx22(Button, { type: "submit", disabled: pending || submitDisabled, "data-testid": submitTestId, children: pending ? `${submitLabel}\u2026` : submitLabel })
949
+ /* @__PURE__ */ jsx23(Button, { type: "submit", disabled: pending || submitDisabled, "data-testid": submitTestId, children: pending ? `${submitLabel}\u2026` : submitLabel })
908
950
  ] })
909
951
  ] }) }) });
910
952
  };
911
953
 
912
954
  // src/patterns/modal/ResponsivePanel.tsx
913
- import { useRef as useRef2, useState as useState5 } from "react";
955
+ import { useRef as useRef2, useState as useState6 } from "react";
914
956
 
915
957
  // src/lib/useMediaQuery.ts
916
- import { useEffect as useEffect2, useState as useState4 } from "react";
958
+ import { useEffect as useEffect2, useState as useState5 } from "react";
917
959
  function useMediaQuery(query) {
918
- const [matches, setMatches] = useState4(
960
+ const [matches, setMatches] = useState5(
919
961
  () => typeof window !== "undefined" ? window.matchMedia(query).matches : false
920
962
  );
921
963
  useEffect2(() => {
@@ -929,7 +971,7 @@ function useMediaQuery(query) {
929
971
  }
930
972
 
931
973
  // src/patterns/modal/ResponsivePanel.tsx
932
- import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
974
+ import { jsx as jsx24, jsxs as jsxs9 } from "react/jsx-runtime";
933
975
  var desktopSizeClasses = {
934
976
  sm: "max-w-md",
935
977
  md: "max-w-xl",
@@ -964,8 +1006,8 @@ var cornerGripPosition = {
964
1006
  "bottom-right": "right-1 bottom-1 border-r-2 border-b-2"
965
1007
  };
966
1008
  function useDesktopPanelGeometry() {
967
- const [style, setStyle] = useState5(void 0);
968
- const [isDragging, setIsDragging] = useState5(false);
1009
+ const [style, setStyle] = useState6(void 0);
1010
+ const [isDragging, setIsDragging] = useState6(false);
969
1011
  const drag = useRef2(null);
970
1012
  const lastRect = useRef2(null);
971
1013
  const beginDrag = (mode, corner) => (e) => {
@@ -1055,7 +1097,7 @@ var ResponsivePanel = ({
1055
1097
  const onInteractOutside = closeOnOutsideClick ? void 0 : (e) => e.preventDefault();
1056
1098
  if (isDesktop) {
1057
1099
  const corners = ["top-left", "top-right", "bottom-left", "bottom-right"];
1058
- return /* @__PURE__ */ jsx23(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(
1100
+ return /* @__PURE__ */ jsx24(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(
1059
1101
  DialogContent,
1060
1102
  {
1061
1103
  "data-slot": "dialog-content",
@@ -1068,7 +1110,7 @@ var ResponsivePanel = ({
1068
1110
  className
1069
1111
  ),
1070
1112
  children: [
1071
- resizable && corners.map((corner) => /* @__PURE__ */ jsx23(
1113
+ resizable && corners.map((corner) => /* @__PURE__ */ jsx24(
1072
1114
  "div",
1073
1115
  {
1074
1116
  "data-testid": `panel-resize-handle-${corner}`,
@@ -1081,7 +1123,7 @@ var ResponsivePanel = ({
1081
1123
  onPointerMove: geometry.onPointerMove,
1082
1124
  onPointerUp: geometry.onPointerUp,
1083
1125
  onPointerCancel: geometry.onPointerUp,
1084
- children: /* @__PURE__ */ jsx23(
1126
+ children: /* @__PURE__ */ jsx24(
1085
1127
  "div",
1086
1128
  {
1087
1129
  className: cn(
@@ -1102,18 +1144,18 @@ var ResponsivePanel = ({
1102
1144
  onPointerUp: draggable ? geometry.onPointerUp : void 0,
1103
1145
  onPointerCancel: draggable ? geometry.onPointerUp : void 0,
1104
1146
  children: [
1105
- /* @__PURE__ */ jsx23(DialogTitle, { children: title }),
1106
- description ? /* @__PURE__ */ jsx23(DialogDescription, { children: description }) : null
1147
+ /* @__PURE__ */ jsx24(DialogTitle, { children: title }),
1148
+ description ? /* @__PURE__ */ jsx24(DialogDescription, { children: description }) : null
1107
1149
  ]
1108
1150
  }
1109
1151
  ),
1110
1152
  children,
1111
- footer ? /* @__PURE__ */ jsx23(DialogFooter, { children: footer }) : null
1153
+ footer ? /* @__PURE__ */ jsx24(DialogFooter, { children: footer }) : null
1112
1154
  ]
1113
1155
  }
1114
1156
  ) });
1115
1157
  }
1116
- return /* @__PURE__ */ jsx23(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(
1158
+ return /* @__PURE__ */ jsx24(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(
1117
1159
  SheetContent,
1118
1160
  {
1119
1161
  side: "bottom",
@@ -1126,19 +1168,76 @@ var ResponsivePanel = ({
1126
1168
  ),
1127
1169
  children: [
1128
1170
  /* @__PURE__ */ jsxs9(SheetHeader, { children: [
1129
- /* @__PURE__ */ jsx23(SheetTitle, { children: title }),
1130
- description ? /* @__PURE__ */ jsx23(SheetDescription, { children: description }) : null
1171
+ /* @__PURE__ */ jsx24(SheetTitle, { children: title }),
1172
+ description ? /* @__PURE__ */ jsx24(SheetDescription, { children: description }) : null
1131
1173
  ] }),
1132
1174
  children,
1133
- footer ? /* @__PURE__ */ jsx23(SheetFooter, { children: footer }) : null
1175
+ footer ? /* @__PURE__ */ jsx24(SheetFooter, { children: footer }) : null
1134
1176
  ]
1135
1177
  }
1136
1178
  ) });
1137
1179
  };
1138
1180
 
1181
+ // src/patterns/modal/QuestionDialog.tsx
1182
+ import { useState as useState7 } from "react";
1183
+ import { jsx as jsx25 } from "react/jsx-runtime";
1184
+ var QuestionDialog = ({
1185
+ open,
1186
+ onOpenChange,
1187
+ title,
1188
+ description,
1189
+ actions,
1190
+ cancelLabel = "Cancel",
1191
+ cancelTestId
1192
+ }) => {
1193
+ const [pendingIndex, setPendingIndex] = useState7(null);
1194
+ const pending = pendingIndex !== null;
1195
+ const handleCancel = () => {
1196
+ onOpenChange(false);
1197
+ };
1198
+ const handleActionClick = async (action, index) => {
1199
+ setPendingIndex(index);
1200
+ try {
1201
+ await action.onClick();
1202
+ onOpenChange(false);
1203
+ } catch (error) {
1204
+ console.error(error);
1205
+ } finally {
1206
+ setPendingIndex(null);
1207
+ }
1208
+ };
1209
+ return /* @__PURE__ */ jsx25(
1210
+ Modal,
1211
+ {
1212
+ open,
1213
+ onOpenChange,
1214
+ title,
1215
+ description,
1216
+ footer: /* @__PURE__ */ jsx25(
1217
+ DialogActions,
1218
+ {
1219
+ actions: actions.map((action, index) => /* @__PURE__ */ jsx25(
1220
+ Button,
1221
+ {
1222
+ variant: action.variant ?? "default",
1223
+ onClick: () => handleActionClick(action, index),
1224
+ disabled: pending,
1225
+ "data-testid": action.testId,
1226
+ children: pendingIndex === index ? `${action.label}\u2026` : action.label
1227
+ },
1228
+ action.label
1229
+ )),
1230
+ cancel: /* @__PURE__ */ jsx25(Button, { variant: "outline", onClick: handleCancel, disabled: pending, "data-testid": cancelTestId, children: cancelLabel })
1231
+ }
1232
+ ),
1233
+ children: null
1234
+ }
1235
+ );
1236
+ };
1237
+
1139
1238
  // src/patterns/form-field/FormField.tsx
1140
1239
  import { cloneElement, isValidElement, useId } from "react";
1141
- import { jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
1240
+ import { jsx as jsx26, jsxs as jsxs10 } from "react/jsx-runtime";
1142
1241
  function FormField({
1143
1242
  label,
1144
1243
  htmlFor,
@@ -1172,13 +1271,13 @@ function FormField({
1172
1271
  ] }) : null
1173
1272
  ] }),
1174
1273
  child,
1175
- error ? /* @__PURE__ */ jsx24("p", { id: messageId, className: "text-sm text-destructive", children: error }) : description ? /* @__PURE__ */ jsx24("p", { id: messageId, className: "text-sm text-muted-foreground", children: description }) : null
1274
+ error ? /* @__PURE__ */ jsx26("p", { id: messageId, className: "text-sm text-destructive", children: error }) : description ? /* @__PURE__ */ jsx26("p", { id: messageId, className: "text-sm text-muted-foreground", children: description }) : null
1176
1275
  ] });
1177
1276
  }
1178
1277
 
1179
1278
  // src/patterns/combobox/Combobox.tsx
1180
1279
  import { Check as Check4, ChevronsUpDown, X as X3 } from "lucide-react";
1181
- import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState6 } from "react";
1280
+ import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState8 } from "react";
1182
1281
 
1183
1282
  // src/lib/optionGrouping.ts
1184
1283
  function groupMembers(options, group) {
@@ -1207,7 +1306,7 @@ function buildRenderChunks(visibleOptions) {
1207
1306
  }
1208
1307
 
1209
1308
  // src/lib/optionGrid.tsx
1210
- import { jsx as jsx25, jsxs as jsxs11 } from "react/jsx-runtime";
1309
+ import { jsx as jsx27, jsxs as jsxs11 } from "react/jsx-runtime";
1211
1310
  function renderGridOptions({
1212
1311
  columns,
1213
1312
  options,
@@ -1219,9 +1318,9 @@ function renderGridOptions({
1219
1318
  testId
1220
1319
  }) {
1221
1320
  return /* @__PURE__ */ jsxs11(Table, { className: "text-sm", children: [
1222
- /* @__PURE__ */ jsx25(TableHeader, { children: /* @__PURE__ */ jsxs11(TableRow, { className: "hover:bg-transparent", children: [
1223
- multiple && /* @__PURE__ */ jsx25(TableHead, { className: "sticky top-0 z-10 w-6 bg-muted" }),
1224
- columns.map((column) => /* @__PURE__ */ jsx25(
1321
+ /* @__PURE__ */ jsx27(TableHeader, { children: /* @__PURE__ */ jsxs11(TableRow, { className: "hover:bg-transparent", children: [
1322
+ multiple && /* @__PURE__ */ jsx27(TableHead, { className: "sticky top-0 z-10 w-6 bg-muted" }),
1323
+ columns.map((column) => /* @__PURE__ */ jsx27(
1225
1324
  TableHead,
1226
1325
  {
1227
1326
  className: cn("sticky top-0 z-10 whitespace-nowrap bg-muted text-xs", column.className),
@@ -1230,7 +1329,7 @@ function renderGridOptions({
1230
1329
  column.key
1231
1330
  ))
1232
1331
  ] }) }),
1233
- /* @__PURE__ */ jsx25(TableBody, { children: options.map((option, index) => {
1332
+ /* @__PURE__ */ jsx27(TableBody, { children: options.map((option, index) => {
1234
1333
  const selected = isSelected(option);
1235
1334
  const active = index === activeIndex;
1236
1335
  return /* @__PURE__ */ jsxs11(
@@ -1257,7 +1356,7 @@ function renderGridOptions({
1257
1356
  option.disabled && "pointer-events-none opacity-50"
1258
1357
  ),
1259
1358
  children: [
1260
- multiple && /* @__PURE__ */ jsx25(TableCell, { className: "w-6", children: /* @__PURE__ */ jsx25(
1359
+ multiple && /* @__PURE__ */ jsx27(TableCell, { className: "w-6", children: /* @__PURE__ */ jsx27(
1261
1360
  "input",
1262
1361
  {
1263
1362
  type: "checkbox",
@@ -1267,7 +1366,7 @@ function renderGridOptions({
1267
1366
  className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
1268
1367
  }
1269
1368
  ) }),
1270
- columns.map((column) => /* @__PURE__ */ jsx25(TableCell, { className: cn("truncate", column.className), children: column.key === "label" ? option.label : option.data?.[column.key] }, column.key))
1369
+ columns.map((column) => /* @__PURE__ */ jsx27(TableCell, { className: cn("truncate", column.className), children: column.key === "label" ? option.label : option.data?.[column.key] }, column.key))
1271
1370
  ]
1272
1371
  },
1273
1372
  option.value
@@ -1277,7 +1376,7 @@ function renderGridOptions({
1277
1376
  }
1278
1377
 
1279
1378
  // src/patterns/combobox/Combobox.tsx
1280
- import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
1379
+ import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
1281
1380
  function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
1282
1381
  const selectedSet = new Set(selectedValues);
1283
1382
  const groupsPresent = Array.from(
@@ -1316,9 +1415,9 @@ function Combobox(props) {
1316
1415
  columns
1317
1416
  } = props;
1318
1417
  const testId = props["data-testid"];
1319
- const [open, setOpen] = useState6(false);
1320
- const [search, setSearch] = useState6("");
1321
- const [activeIndex, setActiveIndex] = useState6(0);
1418
+ const [open, setOpen] = useState8(false);
1419
+ const [search, setSearch] = useState8("");
1420
+ const [activeIndex, setActiveIndex] = useState8(0);
1322
1421
  const inputRef = useRef3(null);
1323
1422
  const selectedValues = useMemo(
1324
1423
  () => props.multiple ? props.value : props.value ? [props.value] : [],
@@ -1397,7 +1496,7 @@ function Combobox(props) {
1397
1496
  isActive && "bg-accent text-accent-foreground"
1398
1497
  ),
1399
1498
  children: [
1400
- props.multiple ? /* @__PURE__ */ jsx26(
1499
+ props.multiple ? /* @__PURE__ */ jsx28(
1401
1500
  "input",
1402
1501
  {
1403
1502
  type: "checkbox",
@@ -1406,15 +1505,15 @@ function Combobox(props) {
1406
1505
  tabIndex: -1,
1407
1506
  className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
1408
1507
  }
1409
- ) : /* @__PURE__ */ jsx26(Check4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
1410
- /* @__PURE__ */ jsx26("span", { className: "truncate", children: option.label })
1508
+ ) : /* @__PURE__ */ jsx28(Check4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
1509
+ /* @__PURE__ */ jsx28("span", { className: "truncate", children: option.label })
1411
1510
  ]
1412
1511
  },
1413
1512
  option.value
1414
1513
  );
1415
1514
  }
1416
1515
  return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: setOpen, children: [
1417
- /* @__PURE__ */ jsx26(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
1516
+ /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
1418
1517
  Button,
1419
1518
  {
1420
1519
  id,
@@ -1430,9 +1529,9 @@ function Combobox(props) {
1430
1529
  className
1431
1530
  ),
1432
1531
  children: [
1433
- /* @__PURE__ */ jsx26("span", { className: "truncate", children: triggerLabel }),
1532
+ /* @__PURE__ */ jsx28("span", { className: "truncate", children: triggerLabel }),
1434
1533
  /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1", children: [
1435
- clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx26(
1534
+ clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx28(
1436
1535
  "span",
1437
1536
  {
1438
1537
  role: "button",
@@ -1447,15 +1546,15 @@ function Combobox(props) {
1447
1546
  props.onChange(null);
1448
1547
  }
1449
1548
  },
1450
- children: /* @__PURE__ */ jsx26(X3, { className: "h-3.5 w-3.5" })
1549
+ children: /* @__PURE__ */ jsx28(X3, { className: "h-3.5 w-3.5" })
1451
1550
  }
1452
1551
  ) : null,
1453
- /* @__PURE__ */ jsx26(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
1552
+ /* @__PURE__ */ jsx28(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
1454
1553
  ] })
1455
1554
  ]
1456
1555
  }
1457
1556
  ) }),
1458
- /* @__PURE__ */ jsx26(
1557
+ /* @__PURE__ */ jsx28(
1459
1558
  PopoverContent,
1460
1559
  {
1461
1560
  align: "start",
@@ -1466,7 +1565,7 @@ function Combobox(props) {
1466
1565
  ),
1467
1566
  onOpenAutoFocus: (event) => event.preventDefault(),
1468
1567
  children: /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", children: [
1469
- /* @__PURE__ */ jsx26(
1568
+ /* @__PURE__ */ jsx28(
1470
1569
  Input,
1471
1570
  {
1472
1571
  ref: inputRef,
@@ -1480,7 +1579,7 @@ function Combobox(props) {
1480
1579
  "aria-label": searchPlaceholder
1481
1580
  }
1482
1581
  ),
1483
- /* @__PURE__ */ jsx26("div", { role: "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: visibleOptions.length === 0 ? /* @__PURE__ */ jsx26("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
1582
+ /* @__PURE__ */ jsx28("div", { role: "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: visibleOptions.length === 0 ? /* @__PURE__ */ jsx28("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
1484
1583
  columns,
1485
1584
  options: visibleOptions,
1486
1585
  activeIndex,
@@ -1507,7 +1606,7 @@ function Combobox(props) {
1507
1606
  "data-group-header": true,
1508
1607
  className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
1509
1608
  children: [
1510
- props.multiple ? /* @__PURE__ */ jsx26(
1609
+ props.multiple ? /* @__PURE__ */ jsx28(
1511
1610
  "input",
1512
1611
  {
1513
1612
  type: "checkbox",
@@ -1521,7 +1620,7 @@ function Combobox(props) {
1521
1620
  "aria-label": `Select all of ${groupLabel(chunk.group)}`
1522
1621
  }
1523
1622
  ) : null,
1524
- /* @__PURE__ */ jsx26("span", { className: "truncate", children: groupLabel(chunk.group) })
1623
+ /* @__PURE__ */ jsx28("span", { className: "truncate", children: groupLabel(chunk.group) })
1525
1624
  ]
1526
1625
  }
1527
1626
  ),
@@ -1537,8 +1636,8 @@ function Combobox(props) {
1537
1636
 
1538
1637
  // src/patterns/tag-combobox/TagCombobox.tsx
1539
1638
  import { X as X4 } from "lucide-react";
1540
- import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState7 } from "react";
1541
- import { jsx as jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
1639
+ import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState9 } from "react";
1640
+ import { jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
1542
1641
  function TagCombobox({
1543
1642
  options,
1544
1643
  value,
@@ -1557,9 +1656,9 @@ function TagCombobox({
1557
1656
  ...rest
1558
1657
  }) {
1559
1658
  const testId = rest["data-testid"];
1560
- const [open, setOpen] = useState7(false);
1561
- const [search, setSearch] = useState7("");
1562
- const [activeIndex, setActiveIndex] = useState7(0);
1659
+ const [open, setOpen] = useState9(false);
1660
+ const [search, setSearch] = useState9("");
1661
+ const [activeIndex, setActiveIndex] = useState9(0);
1563
1662
  const inputRef = useRef4(null);
1564
1663
  const fieldRef = useRef4(null);
1565
1664
  const selectedOptions = useMemo2(
@@ -1645,7 +1744,7 @@ function TagCombobox({
1645
1744
  isActive && "bg-accent text-accent-foreground"
1646
1745
  ),
1647
1746
  children: [
1648
- /* @__PURE__ */ jsx27(
1747
+ /* @__PURE__ */ jsx29(
1649
1748
  "input",
1650
1749
  {
1651
1750
  type: "checkbox",
@@ -1655,14 +1754,14 @@ function TagCombobox({
1655
1754
  className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
1656
1755
  }
1657
1756
  ),
1658
- /* @__PURE__ */ jsx27("span", { className: "truncate", children: option.label })
1757
+ /* @__PURE__ */ jsx29("span", { className: "truncate", children: option.label })
1659
1758
  ]
1660
1759
  },
1661
1760
  option.value
1662
1761
  );
1663
1762
  }
1664
1763
  return /* @__PURE__ */ jsxs13(Popover, { open, onOpenChange: setOpen, children: [
1665
- /* @__PURE__ */ jsx27(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs13(
1764
+ /* @__PURE__ */ jsx29(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs13(
1666
1765
  "div",
1667
1766
  {
1668
1767
  ref: fieldRef,
@@ -1685,8 +1784,8 @@ function TagCombobox({
1685
1784
  {
1686
1785
  className: "inline-flex items-center gap-1 rounded-full bg-muted py-0.5 pr-1 pl-2.5 text-xs font-medium text-muted-foreground",
1687
1786
  children: [
1688
- /* @__PURE__ */ jsx27("span", { className: "max-w-48 truncate", children: option.label }),
1689
- !disabled && /* @__PURE__ */ jsx27(
1787
+ /* @__PURE__ */ jsx29("span", { className: "max-w-48 truncate", children: option.label }),
1788
+ !disabled && /* @__PURE__ */ jsx29(
1690
1789
  "button",
1691
1790
  {
1692
1791
  type: "button",
@@ -1696,14 +1795,14 @@ function TagCombobox({
1696
1795
  event.stopPropagation();
1697
1796
  removeValue(option.value);
1698
1797
  },
1699
- children: /* @__PURE__ */ jsx27(X4, { className: "h-3 w-3" })
1798
+ children: /* @__PURE__ */ jsx29(X4, { className: "h-3 w-3" })
1700
1799
  }
1701
1800
  )
1702
1801
  ]
1703
1802
  },
1704
1803
  option.value
1705
1804
  )),
1706
- /* @__PURE__ */ jsx27(
1805
+ /* @__PURE__ */ jsx29(
1707
1806
  "input",
1708
1807
  {
1709
1808
  ref: inputRef,
@@ -1721,7 +1820,7 @@ function TagCombobox({
1721
1820
  ]
1722
1821
  }
1723
1822
  ) }),
1724
- /* @__PURE__ */ jsx27(
1823
+ /* @__PURE__ */ jsx29(
1725
1824
  PopoverContent,
1726
1825
  {
1727
1826
  align: "start",
@@ -1735,7 +1834,7 @@ function TagCombobox({
1735
1834
  onInteractOutside: (event) => {
1736
1835
  if (fieldRef.current?.contains(event.target)) event.preventDefault();
1737
1836
  },
1738
- children: /* @__PURE__ */ jsx27("div", { role: "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: loading ? /* @__PURE__ */ jsx27("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : visibleOptions.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
1837
+ children: /* @__PURE__ */ jsx29("div", { role: "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: loading ? /* @__PURE__ */ jsx29("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : visibleOptions.length === 0 ? /* @__PURE__ */ jsx29("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
1739
1838
  columns,
1740
1839
  options: visibleOptions,
1741
1840
  activeIndex,
@@ -1761,7 +1860,7 @@ function TagCombobox({
1761
1860
  "data-group-header": true,
1762
1861
  className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
1763
1862
  children: [
1764
- /* @__PURE__ */ jsx27(
1863
+ /* @__PURE__ */ jsx29(
1765
1864
  "input",
1766
1865
  {
1767
1866
  type: "checkbox",
@@ -1775,7 +1874,7 @@ function TagCombobox({
1775
1874
  "aria-label": `Select all of ${groupLabel(chunk.group)}`
1776
1875
  }
1777
1876
  ),
1778
- /* @__PURE__ */ jsx27("span", { className: "truncate", children: groupLabel(chunk.group) })
1877
+ /* @__PURE__ */ jsx29("span", { className: "truncate", children: groupLabel(chunk.group) })
1779
1878
  ]
1780
1879
  }
1781
1880
  ),
@@ -1790,7 +1889,7 @@ function TagCombobox({
1790
1889
 
1791
1890
  // src/patterns/alert-box/AlertBox.tsx
1792
1891
  import { AlertCircle, AlertTriangle, CheckCircle2, Info } from "lucide-react";
1793
- import { jsx as jsx28, jsxs as jsxs14 } from "react/jsx-runtime";
1892
+ import { jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
1794
1893
  var variantStyles = {
1795
1894
  error: "border-destructive bg-destructive/10 text-red-800 dark:text-red-300",
1796
1895
  warning: "border-amber-500 bg-amber-500/10 text-amber-900 dark:text-amber-200",
@@ -1798,24 +1897,24 @@ var variantStyles = {
1798
1897
  success: "border-emerald-500 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
1799
1898
  };
1800
1899
  var defaultIcons = {
1801
- error: /* @__PURE__ */ jsx28(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1802
- warning: /* @__PURE__ */ jsx28(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1803
- info: /* @__PURE__ */ jsx28(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1804
- success: /* @__PURE__ */ jsx28(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
1900
+ error: /* @__PURE__ */ jsx30(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1901
+ warning: /* @__PURE__ */ jsx30(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1902
+ info: /* @__PURE__ */ jsx30(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1903
+ success: /* @__PURE__ */ jsx30(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
1805
1904
  };
1806
1905
  var AlertBox = ({ variant, title, children, className, icon }) => {
1807
1906
  const resolvedIcon = icon === void 0 ? defaultIcons[variant] : icon;
1808
- return /* @__PURE__ */ jsx28("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-row items-start gap-2", children: [
1809
- resolvedIcon != null && /* @__PURE__ */ jsx28("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
1907
+ return /* @__PURE__ */ jsx30("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-row items-start gap-2", children: [
1908
+ resolvedIcon != null && /* @__PURE__ */ jsx30("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
1810
1909
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
1811
- title && /* @__PURE__ */ jsx28("p", { className: "font-bold", children: title }),
1812
- /* @__PURE__ */ jsx28("div", { children })
1910
+ title && /* @__PURE__ */ jsx30("p", { className: "font-bold", children: title }),
1911
+ /* @__PURE__ */ jsx30("div", { children })
1813
1912
  ] })
1814
1913
  ] }) });
1815
1914
  };
1816
1915
 
1817
1916
  // src/patterns/status-badge/StatusBadge.tsx
1818
- import { jsx as jsx29 } from "react/jsx-runtime";
1917
+ import { jsx as jsx31 } from "react/jsx-runtime";
1819
1918
  var TONE_CLASSES = {
1820
1919
  success: "border-transparent bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300",
1821
1920
  warning: "border-transparent bg-amber-500/15 text-amber-800 dark:bg-amber-500/20 dark:text-amber-300",
@@ -1862,11 +1961,11 @@ function resolveTone(status, tone, toneMap) {
1862
1961
  var StatusBadge = ({ status, label, tone, toneMap, className, ...props }) => {
1863
1962
  const resolvedTone = resolveTone(status, tone, toneMap);
1864
1963
  const displayLabel = label ?? deriveLabel(status);
1865
- return /* @__PURE__ */ jsx29(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
1964
+ return /* @__PURE__ */ jsx31(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
1866
1965
  };
1867
1966
 
1868
1967
  // src/patterns/button-group/ButtonGroup.tsx
1869
- import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
1968
+ import { jsx as jsx32, jsxs as jsxs15 } from "react/jsx-runtime";
1870
1969
  function ButtonGroup({
1871
1970
  options,
1872
1971
  value,
@@ -1876,7 +1975,7 @@ function ButtonGroup({
1876
1975
  className,
1877
1976
  ...props
1878
1977
  }) {
1879
- return /* @__PURE__ */ jsx30("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
1978
+ return /* @__PURE__ */ jsx32("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
1880
1979
  const selected = option.value === value;
1881
1980
  const Icon2 = option.icon;
1882
1981
  return /* @__PURE__ */ jsxs15(
@@ -1897,7 +1996,7 @@ function ButtonGroup({
1897
1996
  index > 0 && "-ml-px"
1898
1997
  ),
1899
1998
  children: [
1900
- Icon2 && /* @__PURE__ */ jsx30(Icon2, { className: "h-4 w-4" }),
1999
+ Icon2 && /* @__PURE__ */ jsx32(Icon2, { className: "h-4 w-4" }),
1901
2000
  option.label
1902
2001
  ]
1903
2002
  },
@@ -1909,7 +2008,7 @@ function ButtonGroup({
1909
2008
  // src/patterns/loading-spinner/LoadingSpinner.tsx
1910
2009
  import { cva as cva4 } from "class-variance-authority";
1911
2010
  import { Loader2 } from "lucide-react";
1912
- import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
2011
+ import { jsx as jsx33, jsxs as jsxs16 } from "react/jsx-runtime";
1913
2012
  var loadingSpinnerIconVariants = cva4("animate-spin text-muted-foreground", {
1914
2013
  variants: {
1915
2014
  size: {
@@ -1928,26 +2027,26 @@ var LoadingSpinner = ({
1928
2027
  className,
1929
2028
  ...props
1930
2029
  }) => /* @__PURE__ */ jsxs16("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
1931
- /* @__PURE__ */ jsx31(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
1932
- label ? /* @__PURE__ */ jsx31("span", { children: label }) : null
2030
+ /* @__PURE__ */ jsx33(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
2031
+ label ? /* @__PURE__ */ jsx33("span", { children: label }) : null
1933
2032
  ] });
1934
2033
  var LoadingOverlay = ({
1935
2034
  size = "lg",
1936
2035
  label,
1937
2036
  className,
1938
2037
  ...props
1939
- }) => /* @__PURE__ */ jsx31(
2038
+ }) => /* @__PURE__ */ jsx33(
1940
2039
  "div",
1941
2040
  {
1942
2041
  className: cn("flex h-full min-h-32 w-full items-center justify-center", className),
1943
2042
  ...props,
1944
- children: /* @__PURE__ */ jsx31(LoadingSpinner, { size, label })
2043
+ children: /* @__PURE__ */ jsx33(LoadingSpinner, { size, label })
1945
2044
  }
1946
2045
  );
1947
2046
 
1948
2047
  // src/patterns/search-bar/SearchBar.tsx
1949
2048
  import { Search, X as X5 } from "lucide-react";
1950
- import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
2049
+ import { jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
1951
2050
  function SearchBar({
1952
2051
  value,
1953
2052
  onChange,
@@ -1960,8 +2059,8 @@ function SearchBar({
1960
2059
  }) {
1961
2060
  const showClear = onClear !== false && value.length > 0;
1962
2061
  return /* @__PURE__ */ jsxs17("div", { className: cn("relative", className), children: [
1963
- /* @__PURE__ */ jsx32("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: isLoading ? /* @__PURE__ */ jsx32(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx32(Search, { className: "h-4 w-4", "aria-hidden": "true" }) }),
1964
- /* @__PURE__ */ jsx32(
2062
+ /* @__PURE__ */ jsx34("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: isLoading ? /* @__PURE__ */ jsx34(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx34(Search, { className: "h-4 w-4", "aria-hidden": "true" }) }),
2063
+ /* @__PURE__ */ jsx34(
1965
2064
  "input",
1966
2065
  {
1967
2066
  ref: inputRef,
@@ -1980,14 +2079,14 @@ function SearchBar({
1980
2079
  ...props
1981
2080
  }
1982
2081
  ),
1983
- showClear && /* @__PURE__ */ jsx32(
2082
+ showClear && /* @__PURE__ */ jsx34(
1984
2083
  "button",
1985
2084
  {
1986
2085
  type: "button",
1987
2086
  onClick: () => onClear ? onClear() : onChange(""),
1988
2087
  "aria-label": clearLabel,
1989
2088
  className: "absolute top-1/2 right-2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground hover:bg-accent hover:text-foreground",
1990
- children: /* @__PURE__ */ jsx32(X5, { className: "h-4 w-4", "aria-hidden": "true" })
2089
+ children: /* @__PURE__ */ jsx34(X5, { className: "h-4 w-4", "aria-hidden": "true" })
1991
2090
  }
1992
2091
  )
1993
2092
  ] });
@@ -1995,7 +2094,7 @@ function SearchBar({
1995
2094
 
1996
2095
  // src/patterns/kpi-card/KpiCard.tsx
1997
2096
  import { memo } from "react";
1998
- import { Fragment as Fragment2, jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
2097
+ import { Fragment as Fragment3, jsx as jsx35, jsxs as jsxs18 } from "react/jsx-runtime";
1999
2098
  var DONUT_COLORS = [
2000
2099
  "var(--chart-1, #3b82f6)",
2001
2100
  "var(--chart-2, #22c55e)",
@@ -2018,11 +2117,11 @@ function DonutChart({
2018
2117
  let offset = 0;
2019
2118
  return /* @__PURE__ */ jsxs18("div", { className: "relative inline-flex shrink-0 items-center justify-center", style: { width: size, height: size }, children: [
2020
2119
  /* @__PURE__ */ jsxs18("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "-rotate-90", children: [
2021
- /* @__PURE__ */ jsx33("circle", { cx: size / 2, cy: size / 2, r: radius, fill: "none", stroke: "currentColor", className: "text-muted/40", strokeWidth: thickness }),
2120
+ /* @__PURE__ */ jsx35("circle", { cx: size / 2, cy: size / 2, r: radius, fill: "none", stroke: "currentColor", className: "text-muted/40", strokeWidth: thickness }),
2022
2121
  data.map((segment, i) => {
2023
2122
  const fraction = segment.value / total;
2024
2123
  const dash = fraction * circumference;
2025
- const el = /* @__PURE__ */ jsx33(
2124
+ const el = /* @__PURE__ */ jsx35(
2026
2125
  "circle",
2027
2126
  {
2028
2127
  cx: size / 2,
@@ -2042,8 +2141,8 @@ function DonutChart({
2042
2141
  })
2043
2142
  ] }),
2044
2143
  (centerValue || centerLabel) && /* @__PURE__ */ jsxs18("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: [
2045
- centerValue && /* @__PURE__ */ jsx33("span", { className: "text-base font-black tracking-tight text-foreground tabular-nums", children: centerValue }),
2046
- centerLabel && /* @__PURE__ */ jsx33("span", { className: "text-[9px] font-medium text-muted-foreground uppercase", children: centerLabel })
2144
+ centerValue && /* @__PURE__ */ jsx35("span", { className: "text-base font-black tracking-tight text-foreground tabular-nums", children: centerValue }),
2145
+ centerLabel && /* @__PURE__ */ jsx35("span", { className: "text-[9px] font-medium text-muted-foreground uppercase", children: centerLabel })
2047
2146
  ] })
2048
2147
  ] });
2049
2148
  }
@@ -2055,7 +2154,7 @@ function Sparkline({ data, color }) {
2055
2154
  const W = 72;
2056
2155
  const H = 28;
2057
2156
  const pts = data.map((v, i) => `${i / (data.length - 1) * W},${H - (v - min) / range * (H - 2) - 1}`).join(" ");
2058
- return /* @__PURE__ */ jsx33("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible opacity-70", children: /* @__PURE__ */ jsx33("polyline", { points: pts, fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
2157
+ return /* @__PURE__ */ jsx35("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible opacity-70", children: /* @__PURE__ */ jsx35("polyline", { points: pts, fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
2059
2158
  }
2060
2159
  function AreaSparkline({ data, color }) {
2061
2160
  if (data.length < 2) return null;
@@ -2070,13 +2169,13 @@ function AreaSparkline({ data, color }) {
2070
2169
  const linePts = xs.map((x, i) => `${x},${ys[i]}`).join(" ");
2071
2170
  const areaPath = `M${xs[0]},${H} ` + xs.map((x, i) => `L${x},${ys[i]}`).join(" ") + ` L${xs[xs.length - 1]},${H} Z`;
2072
2171
  return /* @__PURE__ */ jsxs18("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible", preserveAspectRatio: "none", children: [
2073
- /* @__PURE__ */ jsx33("defs", { children: /* @__PURE__ */ jsxs18("linearGradient", { id: "kpi-card-spark-fill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
2074
- /* @__PURE__ */ jsx33("stop", { offset: "0%", stopColor: color, stopOpacity: "0.22" }),
2075
- /* @__PURE__ */ jsx33("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
2172
+ /* @__PURE__ */ jsx35("defs", { children: /* @__PURE__ */ jsxs18("linearGradient", { id: "kpi-card-spark-fill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
2173
+ /* @__PURE__ */ jsx35("stop", { offset: "0%", stopColor: color, stopOpacity: "0.22" }),
2174
+ /* @__PURE__ */ jsx35("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
2076
2175
  ] }) }),
2077
- /* @__PURE__ */ jsx33("path", { d: areaPath, fill: "url(#kpi-card-spark-fill)" }),
2078
- /* @__PURE__ */ jsx33("polyline", { points: linePts, fill: "none", stroke: color, strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }),
2079
- /* @__PURE__ */ jsx33("circle", { cx: xs[xs.length - 1], cy: ys[ys.length - 1], r: "2.5", fill: color })
2176
+ /* @__PURE__ */ jsx35("path", { d: areaPath, fill: "url(#kpi-card-spark-fill)" }),
2177
+ /* @__PURE__ */ jsx35("polyline", { points: linePts, fill: "none", stroke: color, strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }),
2178
+ /* @__PURE__ */ jsx35("circle", { cx: xs[xs.length - 1], cy: ys[ys.length - 1], r: "2.5", fill: color })
2080
2179
  ] });
2081
2180
  }
2082
2181
  var rowBetween = "flex items-center justify-between";
@@ -2097,8 +2196,10 @@ var KpiCard = memo(function KpiCard2({
2097
2196
  progressLabel,
2098
2197
  segments,
2099
2198
  centerValue,
2199
+ background,
2100
2200
  testId
2101
2201
  }) {
2202
+ const bgStyle = background ? { background } : void 0;
2102
2203
  if (variant === "hero") {
2103
2204
  return /* @__PURE__ */ jsxs18(
2104
2205
  "div",
@@ -2106,17 +2207,17 @@ var KpiCard = memo(function KpiCard2({
2106
2207
  "data-testid": testId,
2107
2208
  className: "relative col-span-2 flex min-w-0 flex-1 flex-col gap-1.5 overflow-hidden rounded-2xl p-4 md:p-3 lg:col-span-1 lg:max-w-[340px] lg:min-w-[260px]",
2108
2209
  style: {
2109
- background: "var(--color-primary)",
2110
- boxShadow: "0 2px 10px color-mix(in oklch, var(--color-primary) 18%, transparent)"
2210
+ background: background ?? "var(--color-primary)",
2211
+ boxShadow: `0 2px 10px color-mix(in oklch, ${background ?? "var(--color-primary)"} 18%, transparent)`
2111
2212
  },
2112
2213
  children: [
2113
2214
  /* @__PURE__ */ jsxs18("div", { className: "relative flex items-start justify-between", children: [
2114
- /* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.14em] text-white/70 uppercase", children: label }),
2115
- Icon2 && /* @__PURE__ */ jsx33("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-white/15 ring-1 ring-white/20", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-3 w-3 text-white/90", "aria-hidden": "true" }) })
2215
+ /* @__PURE__ */ jsx35("p", { className: "text-[11px] font-bold tracking-[0.14em] text-white/70 uppercase", children: label }),
2216
+ Icon2 && /* @__PURE__ */ jsx35("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-white/15 ring-1 ring-white/20", children: /* @__PURE__ */ jsx35(Icon2, { className: "h-3 w-3 text-white/90", "aria-hidden": "true" }) })
2116
2217
  ] }),
2117
- /* @__PURE__ */ jsx33("div", { className: "relative", children: loading ? /* @__PURE__ */ jsx33("div", { className: "h-6 w-28 animate-pulse rounded-lg bg-white/15" }) : /* @__PURE__ */ jsx33("p", { className: "text-2xl leading-none font-black tracking-tight text-white tabular-nums md:text-[22px]", children: value ?? "\u2014" }) }),
2218
+ /* @__PURE__ */ jsx35("div", { className: "relative", children: loading ? /* @__PURE__ */ jsx35("div", { className: "h-6 w-28 animate-pulse rounded-lg bg-white/15" }) : /* @__PURE__ */ jsx35("p", { className: "text-2xl leading-none font-black tracking-tight text-white tabular-nums md:text-[22px]", children: value ?? "\u2014" }) }),
2118
2219
  (badge || badgeLabel) && !loading && /* @__PURE__ */ jsxs18("div", { className: "relative flex items-center gap-2", children: [
2119
- badge && /* @__PURE__ */ jsx33(
2220
+ badge && /* @__PURE__ */ jsx35(
2120
2221
  "span",
2121
2222
  {
2122
2223
  className: cn(
@@ -2126,43 +2227,44 @@ var KpiCard = memo(function KpiCard2({
2126
2227
  children: badge.text
2127
2228
  }
2128
2229
  ),
2129
- badgeLabel && /* @__PURE__ */ jsx33("span", { className: "text-[11px] text-white/70", children: badgeLabel })
2230
+ badgeLabel && /* @__PURE__ */ jsx35("span", { className: "text-[11px] text-white/70", children: badgeLabel })
2130
2231
  ] }),
2131
- sub && !loading && /* @__PURE__ */ jsx33("p", { className: "relative -mt-1 text-[11px] font-medium text-white/75 tabular-nums", children: sub }),
2232
+ sub && !loading && /* @__PURE__ */ jsx35("p", { className: "relative -mt-1 text-[11px] font-medium text-white/75 tabular-nums", children: sub }),
2132
2233
  progress != null && !loading && /* @__PURE__ */ jsxs18("div", { className: "relative flex items-center gap-1.5", children: [
2133
- /* @__PURE__ */ jsx33("div", { className: "h-1 flex-1 overflow-hidden rounded-full bg-white/20", children: /* @__PURE__ */ jsx33("div", { className: "h-1 rounded-full bg-white/85", style: { width: `${Math.min(100, Math.max(0, progress))}%` } }) }),
2134
- progressLabel && /* @__PURE__ */ jsx33("span", { className: "shrink-0 text-[11px] font-semibold text-white/80 tabular-nums", children: progressLabel })
2234
+ /* @__PURE__ */ jsx35("div", { className: "h-1 flex-1 overflow-hidden rounded-full bg-white/20", children: /* @__PURE__ */ jsx35("div", { className: "h-1 rounded-full bg-white/85", style: { width: `${Math.min(100, Math.max(0, progress))}%` } }) }),
2235
+ progressLabel && /* @__PURE__ */ jsx35("span", { className: "shrink-0 text-[11px] font-semibold text-white/80 tabular-nums", children: progressLabel })
2135
2236
  ] }),
2136
- sparkline && sparkline.length > 1 && !loading && /* @__PURE__ */ jsx33("div", { className: "relative -mx-4 mt-auto -mb-4 md:-mx-3 md:-mb-3", children: /* @__PURE__ */ jsx33(AreaSparkline, { data: sparkline, color: "#ffffff" }) })
2237
+ sparkline && sparkline.length > 1 && !loading && /* @__PURE__ */ jsx35("div", { className: "relative -mx-4 mt-auto -mb-4 md:-mx-3 md:-mb-3", children: /* @__PURE__ */ jsx35(AreaSparkline, { data: sparkline, color: "#ffffff" }) })
2137
2238
  ]
2138
2239
  }
2139
2240
  );
2140
2241
  }
2141
2242
  if (variant === "mini") {
2142
- const iconSlot = Icon2 ? href ? /* @__PURE__ */ jsx33(
2243
+ const iconSlot = Icon2 ? href ? /* @__PURE__ */ jsx35(
2143
2244
  "a",
2144
2245
  {
2145
2246
  href,
2146
2247
  className: "tap-target flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary transition-[background-color,scale] hover:bg-primary/20 active:scale-90 motion-reduce:transition-none md:h-6 md:w-6",
2147
- children: /* @__PURE__ */ jsx33(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" })
2248
+ children: /* @__PURE__ */ jsx35(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" })
2148
2249
  }
2149
- ) : /* @__PURE__ */ jsx33("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary md:h-6 md:w-6", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" }) }) : null;
2250
+ ) : /* @__PURE__ */ jsx35("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary md:h-6 md:w-6", children: /* @__PURE__ */ jsx35(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" }) }) : null;
2150
2251
  return /* @__PURE__ */ jsxs18(
2151
2252
  "div",
2152
2253
  {
2153
2254
  "data-testid": testId,
2255
+ style: bgStyle,
2154
2256
  className: cn(
2155
2257
  "flex h-full min-w-0 flex-1 flex-col gap-1.5 overflow-hidden rounded-2xl border border-border bg-card p-3 transition-shadow md:p-2.5",
2156
2258
  href && "hover:border-primary/20 hover:shadow-md"
2157
2259
  ),
2158
2260
  children: [
2159
2261
  /* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
2160
- /* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2262
+ /* @__PURE__ */ jsx35("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2161
2263
  iconSlot
2162
2264
  ] }),
2163
- /* @__PURE__ */ jsx33("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: loading ? /* @__PURE__ */ jsx33(Skeleton, { className: "h-6 w-16" }) : /* @__PURE__ */ jsxs18(Fragment2, { children: [
2164
- /* @__PURE__ */ jsx33("p", { className: "min-w-0 animate-in truncate text-[20px] leading-none font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none md:text-[16px]", children: value ?? "\u2014" }),
2165
- badge && /* @__PURE__ */ jsx33(
2265
+ /* @__PURE__ */ jsx35("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: loading ? /* @__PURE__ */ jsx35(Skeleton, { className: "h-6 w-16" }) : /* @__PURE__ */ jsxs18(Fragment3, { children: [
2266
+ /* @__PURE__ */ jsx35("p", { className: "min-w-0 animate-in truncate text-[20px] leading-none font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none md:text-[16px]", children: value ?? "\u2014" }),
2267
+ badge && /* @__PURE__ */ jsx35(
2166
2268
  "span",
2167
2269
  {
2168
2270
  className: cn(
@@ -2174,7 +2276,7 @@ var KpiCard = memo(function KpiCard2({
2174
2276
  )
2175
2277
  ] }) }),
2176
2278
  (sub || sparkline) && !loading && /* @__PURE__ */ jsxs18("div", { className: "flex items-end justify-between gap-2", children: [
2177
- sub && /* @__PURE__ */ jsx33(
2279
+ sub && /* @__PURE__ */ jsx35(
2178
2280
  "p",
2179
2281
  {
2180
2282
  className: cn(
@@ -2184,7 +2286,7 @@ var KpiCard = memo(function KpiCard2({
2184
2286
  children: sub
2185
2287
  }
2186
2288
  ),
2187
- sparkline && /* @__PURE__ */ jsx33(Sparkline, { data: sparkline, color: sparklineColor })
2289
+ sparkline && /* @__PURE__ */ jsx35(Sparkline, { data: sparkline, color: sparklineColor })
2188
2290
  ] })
2189
2291
  ]
2190
2292
  }
@@ -2192,22 +2294,22 @@ var KpiCard = memo(function KpiCard2({
2192
2294
  }
2193
2295
  if (variant === "donut") {
2194
2296
  const total = segments?.reduce((sum, s) => sum + s.value, 0) ?? 0;
2195
- return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, className: "rounded-xl border border-border bg-card p-5", children: [
2297
+ return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, style: bgStyle, className: "rounded-xl border border-border bg-card p-5", children: [
2196
2298
  /* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
2197
- /* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2198
- Icon2 && /* @__PURE__ */ jsx33("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
2299
+ /* @__PURE__ */ jsx35("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2300
+ Icon2 && /* @__PURE__ */ jsx35("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx35(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
2199
2301
  ] }),
2200
2302
  loading ? /* @__PURE__ */ jsxs18("div", { className: "mt-3 flex items-center gap-4", children: [
2201
- /* @__PURE__ */ jsx33(Skeleton, { className: "h-24 w-24 shrink-0 rounded-full" }),
2303
+ /* @__PURE__ */ jsx35(Skeleton, { className: "h-24 w-24 shrink-0 rounded-full" }),
2202
2304
  /* @__PURE__ */ jsxs18("div", { className: "flex flex-1 flex-col gap-2", children: [
2203
- /* @__PURE__ */ jsx33(Skeleton, { className: "h-3 w-full" }),
2204
- /* @__PURE__ */ jsx33(Skeleton, { className: "h-3 w-3/4" }),
2205
- /* @__PURE__ */ jsx33(Skeleton, { className: "h-3 w-1/2" })
2305
+ /* @__PURE__ */ jsx35(Skeleton, { className: "h-3 w-full" }),
2306
+ /* @__PURE__ */ jsx35(Skeleton, { className: "h-3 w-3/4" }),
2307
+ /* @__PURE__ */ jsx35(Skeleton, { className: "h-3 w-1/2" })
2206
2308
  ] })
2207
2309
  ] }) : segments && segments.length > 0 ? /* @__PURE__ */ jsxs18("div", { className: "mt-3 flex items-center gap-4", children: [
2208
- /* @__PURE__ */ jsx33(DonutChart, { data: segments, centerValue: centerValue ?? String(total) }),
2209
- /* @__PURE__ */ jsx33("ul", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: segments.map((segment, i) => /* @__PURE__ */ jsxs18("li", { className: "flex items-center gap-2 text-[12px]", children: [
2210
- /* @__PURE__ */ jsx33(
2310
+ /* @__PURE__ */ jsx35(DonutChart, { data: segments, centerValue: centerValue ?? String(total) }),
2311
+ /* @__PURE__ */ jsx35("ul", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: segments.map((segment, i) => /* @__PURE__ */ jsxs18("li", { className: "flex items-center gap-2 text-[12px]", children: [
2312
+ /* @__PURE__ */ jsx35(
2211
2313
  "span",
2212
2314
  {
2213
2315
  className: "h-2 w-2 shrink-0 rounded-full",
@@ -2215,11 +2317,11 @@ var KpiCard = memo(function KpiCard2({
2215
2317
  "aria-hidden": "true"
2216
2318
  }
2217
2319
  ),
2218
- /* @__PURE__ */ jsx33("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: segment.label }),
2219
- /* @__PURE__ */ jsx33("span", { className: "shrink-0 font-semibold text-foreground tabular-nums", children: total > 0 ? `${Math.round(segment.value / total * 100)}%` : segment.value })
2320
+ /* @__PURE__ */ jsx35("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: segment.label }),
2321
+ /* @__PURE__ */ jsx35("span", { className: "shrink-0 font-semibold text-foreground tabular-nums", children: total > 0 ? `${Math.round(segment.value / total * 100)}%` : segment.value })
2220
2322
  ] }, segment.label)) })
2221
- ] }) : /* @__PURE__ */ jsx33("span", { className: "mt-3 block text-2xl font-black text-muted-foreground/40", children: "\u2014" }),
2222
- sub && !loading && /* @__PURE__ */ jsx33("p", { className: "mt-2.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
2323
+ ] }) : /* @__PURE__ */ jsx35("span", { className: "mt-3 block text-2xl font-black text-muted-foreground/40", children: "\u2014" }),
2324
+ sub && !loading && /* @__PURE__ */ jsx35("p", { className: "mt-2.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
2223
2325
  ] });
2224
2326
  }
2225
2327
  const strValue = value != null ? String(value) : null;
@@ -2229,28 +2331,28 @@ var KpiCard = memo(function KpiCard2({
2229
2331
  const prefix = isSuffix ? null : parts?.[0];
2230
2332
  const number = isSuffix ? parts[0] : parts?.[1] ?? parts?.[0];
2231
2333
  const suffix = isSuffix ? parts[1] : null;
2232
- return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, className: "rounded-xl border border-border bg-card p-5", children: [
2334
+ return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, style: bgStyle, className: "rounded-xl border border-border bg-card p-5", children: [
2233
2335
  /* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
2234
- /* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2235
- Icon2 && /* @__PURE__ */ jsx33("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
2336
+ /* @__PURE__ */ jsx35("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2337
+ Icon2 && /* @__PURE__ */ jsx35("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx35(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
2236
2338
  ] }),
2237
2339
  /* @__PURE__ */ jsxs18("div", { className: "mt-3", children: [
2238
- loading ? /* @__PURE__ */ jsx33(Skeleton, { className: "h-7 w-20" }) : value == null ? /* @__PURE__ */ jsx33("span", { className: "text-2xl font-black text-muted-foreground/40", children: "\u2014" }) : plain ? /* @__PURE__ */ jsx33("span", { className: "animate-in text-2xl font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: strValue }) : /* @__PURE__ */ jsxs18("span", { className: "animate-in font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: [
2340
+ loading ? /* @__PURE__ */ jsx35(Skeleton, { className: "h-7 w-20" }) : value == null ? /* @__PURE__ */ jsx35("span", { className: "text-2xl font-black text-muted-foreground/40", children: "\u2014" }) : plain ? /* @__PURE__ */ jsx35("span", { className: "animate-in text-2xl font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: strValue }) : /* @__PURE__ */ jsxs18("span", { className: "animate-in font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: [
2239
2341
  prefix && /* @__PURE__ */ jsxs18("span", { className: "text-sm text-muted-foreground", children: [
2240
2342
  prefix,
2241
2343
  " "
2242
2344
  ] }),
2243
- /* @__PURE__ */ jsx33("span", { className: "text-2xl", children: number }),
2345
+ /* @__PURE__ */ jsx35("span", { className: "text-2xl", children: number }),
2244
2346
  suffix && /* @__PURE__ */ jsxs18("span", { className: "text-sm text-muted-foreground", children: [
2245
2347
  " ",
2246
2348
  suffix
2247
2349
  ] })
2248
2350
  ] }),
2249
- sub && !loading && /* @__PURE__ */ jsx33("p", { className: "mt-0.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
2351
+ sub && !loading && /* @__PURE__ */ jsx35("p", { className: "mt-0.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
2250
2352
  ] }),
2251
2353
  (badge || badgeLabel || sparkline) && !loading && /* @__PURE__ */ jsxs18("div", { className: "mt-2.5 flex items-end justify-between gap-2", children: [
2252
2354
  /* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-center gap-2", children: [
2253
- badge && /* @__PURE__ */ jsx33(
2355
+ badge && /* @__PURE__ */ jsx35(
2254
2356
  "span",
2255
2357
  {
2256
2358
  className: cn(
@@ -2260,16 +2362,16 @@ var KpiCard = memo(function KpiCard2({
2260
2362
  children: badge.text
2261
2363
  }
2262
2364
  ),
2263
- badgeLabel && /* @__PURE__ */ jsx33("span", { className: "text-[11px] text-muted-foreground", children: badgeLabel })
2365
+ badgeLabel && /* @__PURE__ */ jsx35("span", { className: "text-[11px] text-muted-foreground", children: badgeLabel })
2264
2366
  ] }),
2265
- sparkline && /* @__PURE__ */ jsx33(Sparkline, { data: sparkline, color: sparklineColor })
2367
+ sparkline && /* @__PURE__ */ jsx35(Sparkline, { data: sparkline, color: sparklineColor })
2266
2368
  ] })
2267
2369
  ] });
2268
2370
  });
2269
2371
 
2270
2372
  // src/patterns/search-panel/SearchPanel.tsx
2271
2373
  import { Search as Search2 } from "lucide-react";
2272
- import { jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
2374
+ import { jsx as jsx36, jsxs as jsxs19 } from "react/jsx-runtime";
2273
2375
  function SearchPanel({
2274
2376
  value,
2275
2377
  onChange,
@@ -2294,8 +2396,8 @@ function SearchPanel({
2294
2396
  ),
2295
2397
  children: [
2296
2398
  /* @__PURE__ */ jsxs19("div", { className: "flex h-14 items-center gap-3 px-4", children: [
2297
- isLoading ? /* @__PURE__ */ jsx34(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx34(Search2, { className: "h-5 w-5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
2298
- /* @__PURE__ */ jsx34(
2399
+ isLoading ? /* @__PURE__ */ jsx36(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx36(Search2, { className: "h-5 w-5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
2400
+ /* @__PURE__ */ jsx36(
2299
2401
  "input",
2300
2402
  {
2301
2403
  ref: inputRef,
@@ -2307,7 +2409,7 @@ function SearchPanel({
2307
2409
  }
2308
2410
  ),
2309
2411
  trailingSlot,
2310
- shortcutHint && /* @__PURE__ */ jsx34(
2412
+ shortcutHint && /* @__PURE__ */ jsx36(
2311
2413
  "kbd",
2312
2414
  {
2313
2415
  "aria-hidden": "true",
@@ -2316,7 +2418,7 @@ function SearchPanel({
2316
2418
  }
2317
2419
  )
2318
2420
  ] }),
2319
- modes && modes.length > 0 && /* @__PURE__ */ jsx34("div", { className: "flex items-center gap-2 px-4 pb-3", children: modes.map(({ key, label, icon: Icon2 }) => /* @__PURE__ */ jsxs19(
2421
+ modes && modes.length > 0 && /* @__PURE__ */ jsx36("div", { className: "flex items-center gap-2 px-4 pb-3", children: modes.map(({ key, label, icon: Icon2 }) => /* @__PURE__ */ jsxs19(
2320
2422
  "button",
2321
2423
  {
2322
2424
  type: "button",
@@ -2326,7 +2428,7 @@ function SearchPanel({
2326
2428
  activeMode === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
2327
2429
  ),
2328
2430
  children: [
2329
- Icon2 && /* @__PURE__ */ jsx34(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2431
+ Icon2 && /* @__PURE__ */ jsx36(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2330
2432
  label
2331
2433
  ]
2332
2434
  },
@@ -2339,9 +2441,9 @@ function SearchPanel({
2339
2441
 
2340
2442
  // src/patterns/search-panel/SearchTrigger.tsx
2341
2443
  import { Search as Search3 } from "lucide-react";
2342
- import { jsx as jsx35 } from "react/jsx-runtime";
2444
+ import { jsx as jsx37 } from "react/jsx-runtime";
2343
2445
  function SearchTrigger({ label = "Search", ...props }) {
2344
- return /* @__PURE__ */ jsx35(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": label, ...props, children: /* @__PURE__ */ jsx35(Search3, { className: "h-[18px] w-[18px]", "aria-hidden": "true" }) });
2446
+ return /* @__PURE__ */ jsx37(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": label, ...props, children: /* @__PURE__ */ jsx37(Search3, { className: "h-[18px] w-[18px]", "aria-hidden": "true" }) });
2345
2447
  }
2346
2448
 
2347
2449
  // src/patterns/sidebar/context.ts
@@ -2353,7 +2455,7 @@ function useSidebarCollapsed() {
2353
2455
  }
2354
2456
 
2355
2457
  // src/patterns/sidebar/NavItem.tsx
2356
- import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
2458
+ import { jsx as jsx38, jsxs as jsxs20 } from "react/jsx-runtime";
2357
2459
  function NavItem({
2358
2460
  as,
2359
2461
  icon: Icon2,
@@ -2377,7 +2479,7 @@ function NavItem({
2377
2479
  ),
2378
2480
  ...rest,
2379
2481
  children: [
2380
- Icon2 && /* @__PURE__ */ jsx36(
2482
+ Icon2 && /* @__PURE__ */ jsx38(
2381
2483
  Icon2,
2382
2484
  {
2383
2485
  className: cn("h-[18px] w-[18px] shrink-0", active ? "text-nav-primary" : "text-muted-foreground"),
@@ -2385,7 +2487,7 @@ function NavItem({
2385
2487
  "aria-hidden": true
2386
2488
  }
2387
2489
  ),
2388
- /* @__PURE__ */ jsx36(
2490
+ /* @__PURE__ */ jsx38(
2389
2491
  "span",
2390
2492
  {
2391
2493
  className: cn(
@@ -2401,7 +2503,7 @@ function NavItem({
2401
2503
  );
2402
2504
  if (!collapsed) return row;
2403
2505
  return /* @__PURE__ */ jsxs20(Tooltip, { children: [
2404
- /* @__PURE__ */ jsx36(TooltipTrigger, { asChild: true, children: row }),
2506
+ /* @__PURE__ */ jsx38(TooltipTrigger, { asChild: true, children: row }),
2405
2507
  /* @__PURE__ */ jsxs20(TooltipContent, { side: "right", className: "flex items-center gap-1.5", children: [
2406
2508
  label,
2407
2509
  badge
@@ -2411,8 +2513,8 @@ function NavItem({
2411
2513
 
2412
2514
  // src/patterns/sidebar/NavGroup.tsx
2413
2515
  import { ChevronDown as ChevronDown2 } from "lucide-react";
2414
- import { useState as useState8 } from "react";
2415
- import { jsx as jsx37, jsxs as jsxs21 } from "react/jsx-runtime";
2516
+ import { useState as useState10 } from "react";
2517
+ import { jsx as jsx39, jsxs as jsxs21 } from "react/jsx-runtime";
2416
2518
  function NavGroup({
2417
2519
  label,
2418
2520
  defaultCollapsed = false,
@@ -2421,7 +2523,7 @@ function NavGroup({
2421
2523
  children,
2422
2524
  className
2423
2525
  }) {
2424
- const [internalCollapsed, setInternalCollapsed] = useState8(defaultCollapsed);
2526
+ const [internalCollapsed, setInternalCollapsed] = useState10(defaultCollapsed);
2425
2527
  const groupCollapsed = collapsed ?? internalCollapsed;
2426
2528
  const railCollapsed = useSidebarCollapsed();
2427
2529
  const setGroupCollapsed = (next) => {
@@ -2429,7 +2531,7 @@ function NavGroup({
2429
2531
  if (collapsed === void 0) setInternalCollapsed(next);
2430
2532
  };
2431
2533
  return /* @__PURE__ */ jsxs21("div", { className: cn("flex flex-col gap-0.5", className), children: [
2432
- label && (railCollapsed ? /* @__PURE__ */ jsx37("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs21(
2534
+ label && (railCollapsed ? /* @__PURE__ */ jsx39("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs21(
2433
2535
  "button",
2434
2536
  {
2435
2537
  type: "button",
@@ -2437,8 +2539,8 @@ function NavGroup({
2437
2539
  "aria-expanded": !groupCollapsed,
2438
2540
  className: "group flex w-full items-center justify-between px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground/80 hover:text-foreground",
2439
2541
  children: [
2440
- /* @__PURE__ */ jsx37("span", { children: label }),
2441
- /* @__PURE__ */ jsx37(
2542
+ /* @__PURE__ */ jsx39("span", { children: label }),
2543
+ /* @__PURE__ */ jsx39(
2442
2544
  ChevronDown2,
2443
2545
  {
2444
2546
  className: cn(
@@ -2451,7 +2553,7 @@ function NavGroup({
2451
2553
  ]
2452
2554
  }
2453
2555
  )),
2454
- !groupCollapsed && /* @__PURE__ */ jsx37("div", { className: "flex flex-col gap-0.5", children })
2556
+ !groupCollapsed && /* @__PURE__ */ jsx39("div", { className: "flex flex-col gap-0.5", children })
2455
2557
  ] });
2456
2558
  }
2457
2559
 
@@ -2461,9 +2563,9 @@ import {
2461
2563
  useCallback,
2462
2564
  useEffect as useEffect5,
2463
2565
  useRef as useRef5,
2464
- useState as useState9
2566
+ useState as useState11
2465
2567
  } from "react";
2466
- import { Fragment as Fragment3, jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
2568
+ import { Fragment as Fragment4, jsx as jsx40, jsxs as jsxs22 } from "react/jsx-runtime";
2467
2569
  function resolve(value, collapsed) {
2468
2570
  return typeof value === "function" ? value(collapsed) : value;
2469
2571
  }
@@ -2482,9 +2584,9 @@ function Sidebar({
2482
2584
  children,
2483
2585
  className
2484
2586
  }) {
2485
- const [internalWidth, setInternalWidth] = useState9(defaultWidth);
2587
+ const [internalWidth, setInternalWidth] = useState11(defaultWidth);
2486
2588
  const currentWidth = width ?? internalWidth;
2487
- const [isDragging, setIsDragging] = useState9(false);
2589
+ const [isDragging, setIsDragging] = useState11(false);
2488
2590
  const setWidth = useCallback(
2489
2591
  (next) => {
2490
2592
  const clamped = Math.min(maxWidth, Math.max(minWidth, next));
@@ -2510,7 +2612,7 @@ function Sidebar({
2510
2612
  dragStartRef.current = null;
2511
2613
  setIsDragging(false);
2512
2614
  };
2513
- return /* @__PURE__ */ jsx38(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx38(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs22(
2615
+ return /* @__PURE__ */ jsx40(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx40(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs22(
2514
2616
  "aside",
2515
2617
  {
2516
2618
  style: { width: collapsed ? railWidth : currentWidth },
@@ -2520,7 +2622,7 @@ function Sidebar({
2520
2622
  className
2521
2623
  ),
2522
2624
  children: [
2523
- (header || onCollapsedChange) && /* @__PURE__ */ jsx38("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: collapsed && onCollapsedChange ? (
2625
+ (header || onCollapsedChange) && /* @__PURE__ */ jsx40("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: collapsed && onCollapsedChange ? (
2524
2626
  // Collapsed rail: the header slot (e.g. a compact logo mark) and the
2525
2627
  // expand toggle would otherwise compete for the narrow rail's width
2526
2628
  // and clip each other, so they're stacked instead — the header shows
@@ -2534,8 +2636,8 @@ function Sidebar({
2534
2636
  "aria-label": "Expand sidebar",
2535
2637
  className: "group relative flex h-9 w-9 shrink-0 items-center justify-center",
2536
2638
  children: [
2537
- header && /* @__PURE__ */ jsx38("span", { className: "transition-opacity group-hover:opacity-0", children: resolve(header, collapsed) }),
2538
- /* @__PURE__ */ jsx38(
2639
+ header && /* @__PURE__ */ jsx40("span", { className: "transition-opacity group-hover:opacity-0", children: resolve(header, collapsed) }),
2640
+ /* @__PURE__ */ jsx40(
2539
2641
  PanelLeftOpen,
2540
2642
  {
2541
2643
  className: "absolute h-4 w-4 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
@@ -2545,22 +2647,22 @@ function Sidebar({
2545
2647
  ]
2546
2648
  }
2547
2649
  )
2548
- ) : /* @__PURE__ */ jsxs22(Fragment3, { children: [
2549
- header && /* @__PURE__ */ jsx38("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
2550
- onCollapsedChange && /* @__PURE__ */ jsx38(
2650
+ ) : /* @__PURE__ */ jsxs22(Fragment4, { children: [
2651
+ header && /* @__PURE__ */ jsx40("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
2652
+ onCollapsedChange && /* @__PURE__ */ jsx40(
2551
2653
  "button",
2552
2654
  {
2553
2655
  type: "button",
2554
2656
  onClick: () => onCollapsedChange(!collapsed),
2555
2657
  "aria-label": "Collapse sidebar",
2556
2658
  className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
2557
- children: /* @__PURE__ */ jsx38(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
2659
+ children: /* @__PURE__ */ jsx40(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
2558
2660
  }
2559
2661
  )
2560
2662
  ] }) }),
2561
- /* @__PURE__ */ jsx38(SidebarNav, { children }),
2562
- footer && /* @__PURE__ */ jsx38("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
2563
- resizable && !collapsed && /* @__PURE__ */ jsx38(
2663
+ /* @__PURE__ */ jsx40(SidebarNav, { children }),
2664
+ footer && /* @__PURE__ */ jsx40("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
2665
+ resizable && !collapsed && /* @__PURE__ */ jsx40(
2564
2666
  "div",
2565
2667
  {
2566
2668
  onPointerDown: handlePointerDown,
@@ -2576,7 +2678,7 @@ function Sidebar({
2576
2678
  }
2577
2679
  function SidebarNav({ children, className, ...rest }) {
2578
2680
  const ref = useRef5(null);
2579
- const [showFade, setShowFade] = useState9(false);
2681
+ const [showFade, setShowFade] = useState11(false);
2580
2682
  useEffect5(() => {
2581
2683
  const el = ref.current;
2582
2684
  if (!el) return;
@@ -2591,7 +2693,7 @@ function SidebarNav({ children, className, ...rest }) {
2591
2693
  };
2592
2694
  }, []);
2593
2695
  return /* @__PURE__ */ jsxs22("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
2594
- /* @__PURE__ */ jsx38(
2696
+ /* @__PURE__ */ jsx40(
2595
2697
  "div",
2596
2698
  {
2597
2699
  ref,
@@ -2600,7 +2702,7 @@ function SidebarNav({ children, className, ...rest }) {
2600
2702
  children
2601
2703
  }
2602
2704
  ),
2603
- showFade && /* @__PURE__ */ jsx38(
2705
+ showFade && /* @__PURE__ */ jsx40(
2604
2706
  "div",
2605
2707
  {
2606
2708
  className: "pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-gradient-to-t from-sidebar to-transparent",
@@ -2610,10 +2712,326 @@ function SidebarNav({ children, className, ...rest }) {
2610
2712
  ] });
2611
2713
  }
2612
2714
 
2715
+ // src/patterns/ai/AiButton.tsx
2716
+ import { Sparkles } from "lucide-react";
2717
+ import { forwardRef as forwardRef17 } from "react";
2718
+ import { jsx as jsx41, jsxs as jsxs23 } from "react/jsx-runtime";
2719
+ var AiButton = forwardRef17(
2720
+ ({ variant = "ai-subtle", loading = false, icon: Icon2 = Sparkles, disabled, children, ...props }, ref) => /* @__PURE__ */ jsxs23(
2721
+ Button,
2722
+ {
2723
+ ref,
2724
+ variant,
2725
+ disabled: disabled || loading,
2726
+ "aria-busy": loading || void 0,
2727
+ ...props,
2728
+ children: [
2729
+ loading ? /* @__PURE__ */ jsx41(LoadingSpinner, { size: "sm", className: "[&_svg]:text-current" }) : /* @__PURE__ */ jsx41(Icon2, { className: "h-4 w-4", "aria-hidden": "true" }),
2730
+ children
2731
+ ]
2732
+ }
2733
+ )
2734
+ );
2735
+ AiButton.displayName = "AiButton";
2736
+
2737
+ // src/patterns/ai/AiExplainButton.tsx
2738
+ import { RefreshCw, Sparkles as Sparkles2 } from "lucide-react";
2739
+ import { useState as useState12 } from "react";
2740
+ import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
2741
+ function AiExplainButton({
2742
+ onExplain,
2743
+ label = "Explain",
2744
+ title,
2745
+ align = "start",
2746
+ contentClassName = "w-80",
2747
+ size = "sm",
2748
+ ...props
2749
+ }) {
2750
+ const [open, setOpen] = useState12(false);
2751
+ const [loading, setLoading] = useState12(false);
2752
+ const [content, setContent] = useState12(null);
2753
+ const [error, setError] = useState12(null);
2754
+ const load = async () => {
2755
+ setLoading(true);
2756
+ setError(null);
2757
+ try {
2758
+ setContent(await onExplain());
2759
+ } catch (err) {
2760
+ setError(err instanceof Error ? err.message : String(err));
2761
+ } finally {
2762
+ setLoading(false);
2763
+ }
2764
+ };
2765
+ const handleOpenChange = (next) => {
2766
+ setOpen(next);
2767
+ if (next && content === null && error === null && !loading) void load();
2768
+ };
2769
+ return /* @__PURE__ */ jsxs24(Popover, { open, onOpenChange: handleOpenChange, children: [
2770
+ /* @__PURE__ */ jsx42(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx42(AiButton, { size, ...props, children: label }) }),
2771
+ /* @__PURE__ */ jsxs24(PopoverContent, { align, className: contentClassName, children: [
2772
+ /* @__PURE__ */ jsxs24("p", { className: "mb-2 flex items-center gap-1.5 text-sm font-semibold", children: [
2773
+ /* @__PURE__ */ jsx42(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
2774
+ title ?? label
2775
+ ] }),
2776
+ loading ? /* @__PURE__ */ jsx42(LoadingSpinner, { size: "sm", label: "Thinking\u2026", className: "text-sm text-muted-foreground" }) : error ? /* @__PURE__ */ jsxs24("div", { className: "flex flex-col items-start gap-2", children: [
2777
+ /* @__PURE__ */ jsx42("div", { role: "alert", className: "w-full", children: /* @__PURE__ */ jsx42(AlertBox, { variant: "error", children: error }) }),
2778
+ /* @__PURE__ */ jsxs24(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void load(), children: [
2779
+ /* @__PURE__ */ jsx42(RefreshCw, { className: "h-4 w-4", "aria-hidden": "true" }),
2780
+ "Try again"
2781
+ ] })
2782
+ ] }) : /* @__PURE__ */ jsx42("div", { className: "max-h-80 overflow-y-auto text-sm leading-relaxed", children: content })
2783
+ ] })
2784
+ ] });
2785
+ }
2786
+
2787
+ // src/patterns/ai/VoiceInputButton.tsx
2788
+ import { Mic, MicOff } from "lucide-react";
2789
+ import { forwardRef as forwardRef18 } from "react";
2790
+
2791
+ // src/patterns/ai/useSpeechRecognition.ts
2792
+ import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef6, useState as useState13 } from "react";
2793
+ function getCtor() {
2794
+ if (typeof window === "undefined") return void 0;
2795
+ const w = window;
2796
+ return w.SpeechRecognition ?? w.webkitSpeechRecognition;
2797
+ }
2798
+ function browserEngine() {
2799
+ const Ctor = getCtor();
2800
+ return Ctor ? new Ctor() : null;
2801
+ }
2802
+ function describeSpeechError(code) {
2803
+ switch (code) {
2804
+ case "aborted":
2805
+ return null;
2806
+ case "not-allowed":
2807
+ case "service-not-allowed":
2808
+ return "Microphone access is blocked. Allow it in the browser's site settings and try again.";
2809
+ case "audio-capture":
2810
+ return "No microphone was found.";
2811
+ case "no-speech":
2812
+ return "No speech was detected.";
2813
+ case "network":
2814
+ return "Speech recognition needs a network connection.";
2815
+ default:
2816
+ return `Speech recognition failed (${code}).`;
2817
+ }
2818
+ }
2819
+ function useSpeechRecognition({
2820
+ lang,
2821
+ onResult,
2822
+ onError,
2823
+ engine
2824
+ } = {}) {
2825
+ const [listening, setListening] = useState13(false);
2826
+ const [interim, setInterim] = useState13("");
2827
+ const [error, setError] = useState13(null);
2828
+ const recognitionRef = useRef6(null);
2829
+ const onResultRef = useRef6(onResult);
2830
+ const onErrorRef = useRef6(onError);
2831
+ const engineRef = useRef6(engine);
2832
+ onResultRef.current = onResult;
2833
+ onErrorRef.current = onError;
2834
+ engineRef.current = engine;
2835
+ const supported = engine !== void 0 || getCtor() !== void 0;
2836
+ const stop = useCallback2(() => {
2837
+ recognitionRef.current?.stop();
2838
+ }, []);
2839
+ const start = useCallback2(() => {
2840
+ if (recognitionRef.current) return;
2841
+ const recognition = engineRef.current ? engineRef.current() : browserEngine();
2842
+ if (!recognition) return;
2843
+ recognition.lang = lang ?? (typeof document !== "undefined" ? document.documentElement.lang : "") ?? "";
2844
+ if (!recognition.lang) recognition.lang = "en-US";
2845
+ recognition.continuous = true;
2846
+ recognition.interimResults = true;
2847
+ recognition.onresult = (event) => {
2848
+ let pending = "";
2849
+ for (let i = event.resultIndex; i < event.results.length; i += 1) {
2850
+ const result = event.results[i];
2851
+ if (!result) continue;
2852
+ if (result.isFinal) onResultRef.current?.(result[0].transcript.trim());
2853
+ else pending += result[0].transcript;
2854
+ }
2855
+ setInterim(pending);
2856
+ };
2857
+ recognition.onerror = (event) => {
2858
+ setError(event.error);
2859
+ onErrorRef.current?.(event.error);
2860
+ };
2861
+ recognition.onend = () => {
2862
+ recognitionRef.current = null;
2863
+ setListening(false);
2864
+ setInterim("");
2865
+ };
2866
+ recognitionRef.current = recognition;
2867
+ setError(null);
2868
+ setInterim("");
2869
+ setListening(true);
2870
+ recognition.start();
2871
+ }, [lang]);
2872
+ const toggle = useCallback2(() => {
2873
+ if (recognitionRef.current) stop();
2874
+ else start();
2875
+ }, [start, stop]);
2876
+ useEffect6(() => () => recognitionRef.current?.abort(), []);
2877
+ return { supported, listening, interim, error, start, stop, toggle };
2878
+ }
2879
+
2880
+ // src/patterns/ai/VoiceInputButton.tsx
2881
+ import { jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
2882
+ var VoiceInputButton = forwardRef18(
2883
+ ({
2884
+ onTranscript,
2885
+ onError,
2886
+ lang,
2887
+ engine,
2888
+ label,
2889
+ startLabel = "Start dictation",
2890
+ stopLabel = "Stop dictation",
2891
+ showInterim = false,
2892
+ size,
2893
+ disabled,
2894
+ className,
2895
+ ...props
2896
+ }, ref) => {
2897
+ const { supported, listening, interim, toggle } = useSpeechRecognition({
2898
+ lang,
2899
+ onResult: onTranscript,
2900
+ onError,
2901
+ engine
2902
+ });
2903
+ const unsupportedTitle = "Speech recognition isn't available in this browser";
2904
+ return (
2905
+ // `title` lives on the wrapper, not the button: buttonVariants sets
2906
+ // `disabled:pointer-events-none`, so a disabled button never shows its own.
2907
+ /* @__PURE__ */ jsxs25("span", { className: "inline-flex min-w-0 items-center gap-2", title: supported ? void 0 : unsupportedTitle, children: [
2908
+ /* @__PURE__ */ jsxs25(
2909
+ Button,
2910
+ {
2911
+ ref,
2912
+ type: "button",
2913
+ variant: "outline",
2914
+ size: size ?? (label ? "default" : "icon"),
2915
+ onClick: toggle,
2916
+ disabled: disabled || !supported,
2917
+ "aria-label": label ? void 0 : supported ? listening ? stopLabel : startLabel : unsupportedTitle,
2918
+ "aria-pressed": supported ? listening : void 0,
2919
+ className: cn(
2920
+ listening && "border-destructive bg-destructive/10 text-red-800 hover:bg-destructive/15 hover:text-red-800 dark:text-red-300 dark:hover:text-red-300",
2921
+ className
2922
+ ),
2923
+ ...props,
2924
+ children: [
2925
+ supported ? /* @__PURE__ */ jsx43(Mic, { className: cn("h-4 w-4", listening && "animate-pulse motion-reduce:animate-none"), "aria-hidden": "true" }) : /* @__PURE__ */ jsx43(MicOff, { className: "h-4 w-4", "aria-hidden": "true" }),
2926
+ label
2927
+ ]
2928
+ }
2929
+ ),
2930
+ showInterim && interim ? /* @__PURE__ */ jsx43("span", { "aria-live": "polite", className: "truncate text-sm text-muted-foreground italic", children: interim }) : null
2931
+ ] })
2932
+ );
2933
+ }
2934
+ );
2935
+ VoiceInputButton.displayName = "VoiceInputButton";
2936
+
2937
+ // src/patterns/ai/VoiceTranscript.tsx
2938
+ import { Undo2, Wand2 } from "lucide-react";
2939
+ import { useState as useState14 } from "react";
2940
+ import { jsx as jsx44, jsxs as jsxs26 } from "react/jsx-runtime";
2941
+ function VoiceTranscript({
2942
+ value,
2943
+ onChange,
2944
+ onTransform,
2945
+ transformLabel = "Transform with AI",
2946
+ placeholder = "Speak or type\u2026",
2947
+ lang,
2948
+ engine,
2949
+ rows = 4,
2950
+ disabled,
2951
+ className,
2952
+ ...props
2953
+ }) {
2954
+ const [loading, setLoading] = useState14(false);
2955
+ const [error, setError] = useState14(null);
2956
+ const [previous, setPrevious] = useState14(null);
2957
+ const append = (chunk) => {
2958
+ onChange(value ? `${value.trimEnd()} ${chunk}` : chunk);
2959
+ };
2960
+ const transform = async () => {
2961
+ if (!onTransform) return;
2962
+ const before = value;
2963
+ setLoading(true);
2964
+ setError(null);
2965
+ try {
2966
+ const next = await onTransform(before);
2967
+ setPrevious(before);
2968
+ onChange(next);
2969
+ } catch (err) {
2970
+ setError(err instanceof Error ? err.message : String(err));
2971
+ } finally {
2972
+ setLoading(false);
2973
+ }
2974
+ };
2975
+ const undo = () => {
2976
+ if (previous === null) return;
2977
+ onChange(previous);
2978
+ setPrevious(null);
2979
+ };
2980
+ return /* @__PURE__ */ jsxs26("div", { className: cn("flex flex-col gap-2", className), ...props, children: [
2981
+ /* @__PURE__ */ jsx44(
2982
+ Textarea,
2983
+ {
2984
+ value,
2985
+ onChange: (event) => onChange(event.target.value),
2986
+ placeholder,
2987
+ rows,
2988
+ disabled: disabled || loading,
2989
+ "aria-busy": loading || void 0
2990
+ }
2991
+ ),
2992
+ /* @__PURE__ */ jsxs26("div", { className: "flex flex-wrap items-center gap-2", children: [
2993
+ /* @__PURE__ */ jsx44(
2994
+ VoiceInputButton,
2995
+ {
2996
+ onTranscript: append,
2997
+ onError: (code) => setError(describeSpeechError(code)),
2998
+ lang,
2999
+ engine,
3000
+ label: "Dictate",
3001
+ size: "sm",
3002
+ showInterim: true,
3003
+ disabled: disabled || loading
3004
+ }
3005
+ ),
3006
+ onTransform ? /* @__PURE__ */ jsx44(
3007
+ AiButton,
3008
+ {
3009
+ variant: "ai",
3010
+ size: "sm",
3011
+ icon: Wand2,
3012
+ loading,
3013
+ disabled: disabled || value.trim().length === 0,
3014
+ onClick: () => void transform(),
3015
+ children: transformLabel
3016
+ }
3017
+ ) : null,
3018
+ previous !== null && !loading ? /* @__PURE__ */ jsxs26(Button, { type: "button", variant: "ghost", size: "sm", onClick: undo, children: [
3019
+ /* @__PURE__ */ jsx44(Undo2, { className: "h-4 w-4", "aria-hidden": "true" }),
3020
+ "Undo"
3021
+ ] }) : null
3022
+ ] }),
3023
+ error ? (
3024
+ // AlertBox has no `role` prop, so the live region wraps it; same shape
3025
+ // a consuming app's own form errors take.
3026
+ /* @__PURE__ */ jsx44("div", { role: "alert", children: /* @__PURE__ */ jsx44(AlertBox, { variant: "error", children: error }) })
3027
+ ) : null
3028
+ ] });
3029
+ }
3030
+
2613
3031
  // src/patterns/stepper/Stepper.tsx
2614
- import { useEffect as useEffect6, useRef as useRef6 } from "react";
3032
+ import { useEffect as useEffect7, useRef as useRef7 } from "react";
2615
3033
  import { Check as Check5, Plus } from "lucide-react";
2616
- import { jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
3034
+ import { jsx as jsx45, jsxs as jsxs27 } from "react/jsx-runtime";
2617
3035
  function Stepper({
2618
3036
  step,
2619
3037
  maxStep,
@@ -2625,8 +3043,8 @@ function Stepper({
2625
3043
  scrollTestId = "stepper-scroll",
2626
3044
  newTestId
2627
3045
  }) {
2628
- const stepRefs = useRef6(/* @__PURE__ */ new Map());
2629
- useEffect6(() => {
3046
+ const stepRefs = useRef7(/* @__PURE__ */ new Map());
3047
+ useEffect7(() => {
2630
3048
  const activeStep = stepRefs.current.get(step);
2631
3049
  activeStep?.scrollIntoView({ block: "nearest", inline: "center" });
2632
3050
  }, [step]);
@@ -2634,19 +3052,19 @@ function Stepper({
2634
3052
  // onNew is pinned outside the horizontally-scrolling region below (a sibling of
2635
3053
  // it, not a child) so its own edge never scrolls off-screen along with the
2636
3054
  // later steps once the scroller's min-width doesn't fit the viewport.
2637
- /* @__PURE__ */ jsxs23("div", { className: "flex w-full shrink-0 items-center gap-2", children: [
2638
- /* @__PURE__ */ jsx39("nav", { "aria-label": navLabel, className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx39(
3055
+ /* @__PURE__ */ jsxs27("div", { className: "flex w-full shrink-0 items-center gap-2", children: [
3056
+ /* @__PURE__ */ jsx45("nav", { "aria-label": navLabel, className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx45(
2639
3057
  "div",
2640
3058
  {
2641
3059
  "data-testid": scrollTestId,
2642
3060
  className: "flex w-full items-center gap-0 overflow-x-auto px-2 py-1",
2643
- children: /* @__PURE__ */ jsx39("ol", { className: "flex min-w-[700px] flex-1 items-start", children: steps.map(({ n, label, phase }, i) => {
3061
+ children: /* @__PURE__ */ jsx45("ol", { className: "flex min-w-[700px] flex-1 items-start", children: steps.map(({ n, label, phase }, i) => {
2644
3062
  const done = step > n;
2645
3063
  const active = step === n;
2646
3064
  const navigable = n <= maxStep && n !== step;
2647
3065
  const last = i === steps.length - 1;
2648
- return /* @__PURE__ */ jsxs23("li", { className: cn("flex items-start", !last && "flex-1"), children: [
2649
- /* @__PURE__ */ jsxs23(
3066
+ return /* @__PURE__ */ jsxs27("li", { className: cn("flex items-start", !last && "flex-1"), children: [
3067
+ /* @__PURE__ */ jsxs27(
2650
3068
  "button",
2651
3069
  {
2652
3070
  ref: (element) => {
@@ -2662,17 +3080,17 @@ function Stepper({
2662
3080
  navigable ? "cursor-pointer hover:opacity-80" : "cursor-default"
2663
3081
  ),
2664
3082
  children: [
2665
- /* @__PURE__ */ jsx39(
3083
+ /* @__PURE__ */ jsx45(
2666
3084
  "div",
2667
3085
  {
2668
3086
  className: cn(
2669
3087
  "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[11px] font-bold transition-colors",
2670
3088
  done ? "bg-primary/15 text-primary" : active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
2671
3089
  ),
2672
- children: done ? /* @__PURE__ */ jsx39(Check5, { className: "h-3 w-3" }) : n
3090
+ children: done ? /* @__PURE__ */ jsx45(Check5, { className: "h-3 w-3" }) : n
2673
3091
  }
2674
3092
  ),
2675
- /* @__PURE__ */ jsx39(
3093
+ /* @__PURE__ */ jsx45(
2676
3094
  "span",
2677
3095
  {
2678
3096
  className: cn(
@@ -2682,11 +3100,11 @@ function Stepper({
2682
3100
  children: label
2683
3101
  }
2684
3102
  ),
2685
- /* @__PURE__ */ jsx39("span", { className: "text-[9px] tracking-[0.5px] text-muted-foreground/50 uppercase", children: phase })
3103
+ /* @__PURE__ */ jsx45("span", { className: "text-[9px] tracking-[0.5px] text-muted-foreground/50 uppercase", children: phase })
2686
3104
  ]
2687
3105
  }
2688
3106
  ),
2689
- !last && /* @__PURE__ */ jsx39(
3107
+ !last && /* @__PURE__ */ jsx45(
2690
3108
  "div",
2691
3109
  {
2692
3110
  className: cn(
@@ -2699,7 +3117,7 @@ function Stepper({
2699
3117
  }) })
2700
3118
  }
2701
3119
  ) }),
2702
- onNew && /* @__PURE__ */ jsxs23(
3120
+ onNew && /* @__PURE__ */ jsxs27(
2703
3121
  "button",
2704
3122
  {
2705
3123
  "data-testid": newTestId,
@@ -2707,7 +3125,7 @@ function Stepper({
2707
3125
  title: newLabel,
2708
3126
  className: "flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-[12px] font-semibold text-foreground transition-colors hover:bg-muted/50",
2709
3127
  children: [
2710
- /* @__PURE__ */ jsx39(Plus, { className: "h-3.5 w-3.5" }),
3128
+ /* @__PURE__ */ jsx45(Plus, { className: "h-3.5 w-3.5" }),
2711
3129
  newLabel
2712
3130
  ]
2713
3131
  }
@@ -2716,6 +3134,8 @@ function Stepper({
2716
3134
  );
2717
3135
  }
2718
3136
  export {
3137
+ AiButton,
3138
+ AiExplainButton,
2719
3139
  AlertBox,
2720
3140
  Badge,
2721
3141
  Button,
@@ -2730,6 +3150,7 @@ export {
2730
3150
  Combobox,
2731
3151
  ConfirmDialog,
2732
3152
  Dialog,
3153
+ DialogActions,
2733
3154
  DialogContent,
2734
3155
  DialogDescription,
2735
3156
  DialogFooter,
@@ -2766,6 +3187,7 @@ export {
2766
3187
  PopoverAnchor,
2767
3188
  PopoverContent,
2768
3189
  PopoverTrigger,
3190
+ QuestionDialog,
2769
3191
  ResponsivePanel,
2770
3192
  ScrollArea,
2771
3193
  ScrollBar,
@@ -2813,12 +3235,18 @@ export {
2813
3235
  TooltipContent,
2814
3236
  TooltipProvider,
2815
3237
  TooltipTrigger,
3238
+ VoiceInputButton,
3239
+ VoiceTranscript,
2816
3240
  badgeVariants,
2817
3241
  buttonVariants,
2818
3242
  cn,
3243
+ describeSpeechError,
3244
+ getConfirmButtonPlacement,
3245
+ isWindowsPlatform,
2819
3246
  loadingSpinnerIconVariants,
2820
3247
  sheetVariants,
2821
3248
  useMediaQuery,
2822
- useSidebarCollapsed
3249
+ useSidebarCollapsed,
3250
+ useSpeechRecognition
2823
3251
  };
2824
3252
  //# sourceMappingURL=index.js.map