@akira-io/ui 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +15 -10
  2. package/dist/blocks.d.ts +162 -9
  3. package/dist/blocks.js +1410 -36
  4. package/dist/blocks.js.map +1 -1
  5. package/dist/button-Bj6My68b.d.ts +17 -0
  6. package/dist/chunk-2WIIRDHT.js +83 -0
  7. package/dist/chunk-2WIIRDHT.js.map +1 -0
  8. package/dist/{chunk-A2SIZOEA.js → chunk-4SOQAVUP.js} +125 -45
  9. package/dist/chunk-4SOQAVUP.js.map +1 -0
  10. package/dist/chunk-5AJFQ6C4.js +35 -0
  11. package/dist/chunk-5AJFQ6C4.js.map +1 -0
  12. package/dist/chunk-CFDDAFPJ.js +1243 -0
  13. package/dist/chunk-CFDDAFPJ.js.map +1 -0
  14. package/dist/chunk-FWQFB37J.js +45 -0
  15. package/dist/chunk-FWQFB37J.js.map +1 -0
  16. package/dist/chunk-IQMRJXEO.js +59 -0
  17. package/dist/chunk-IQMRJXEO.js.map +1 -0
  18. package/dist/chunk-L2H5GIF7.js +22 -0
  19. package/dist/chunk-L2H5GIF7.js.map +1 -0
  20. package/dist/chunk-ODYCBCKS.js +290 -0
  21. package/dist/chunk-ODYCBCKS.js.map +1 -0
  22. package/dist/{chunk-5MQWJVM2.js → chunk-QHZWW55M.js} +383 -326
  23. package/dist/chunk-QHZWW55M.js.map +1 -0
  24. package/dist/chunk-RPCL7VFQ.js +123 -0
  25. package/dist/chunk-RPCL7VFQ.js.map +1 -0
  26. package/dist/chunk-T2INZOCM.js +165 -0
  27. package/dist/chunk-T2INZOCM.js.map +1 -0
  28. package/dist/chunk-TGF54WAO.js +29 -0
  29. package/dist/chunk-TGF54WAO.js.map +1 -0
  30. package/dist/chunk-XZCC6MTE.js +26 -0
  31. package/dist/chunk-XZCC6MTE.js.map +1 -0
  32. package/dist/{chunk-UXL33CZ4.js → chunk-YOVY3KT6.js} +13 -11
  33. package/dist/chunk-YOVY3KT6.js.map +1 -0
  34. package/dist/code.d.ts +9 -0
  35. package/dist/code.js +650 -0
  36. package/dist/code.js.map +1 -0
  37. package/dist/context-YWbxMH0C.d.ts +383 -0
  38. package/dist/editor.d.ts +128 -0
  39. package/dist/editor.js +450 -0
  40. package/dist/editor.js.map +1 -0
  41. package/dist/index.d.ts +298 -222
  42. package/dist/index.js +1300 -670
  43. package/dist/index.js.map +1 -1
  44. package/dist/inertia.d.ts +47 -2
  45. package/dist/inertia.js +138 -7
  46. package/dist/inertia.js.map +1 -1
  47. package/dist/input-Dpi7pXeR.d.ts +6 -0
  48. package/dist/json-viewer-BARA2RCW.d.ts +51 -0
  49. package/dist/{language-Bft6rBBt.d.ts → language-Dgjr9bm3.d.ts} +1 -1
  50. package/dist/locales/pt.d.ts +26 -4
  51. package/dist/locales/pt.js +127 -1
  52. package/dist/locales/pt.js.map +1 -1
  53. package/dist/password-input-DcXX7SG9.d.ts +51 -0
  54. package/dist/shells.d.ts +45 -7
  55. package/dist/shells.js +96 -60
  56. package/dist/shells.js.map +1 -1
  57. package/dist/sidebar-DqkFRUxQ.d.ts +81 -0
  58. package/dist/types-B-eGPoFD.d.ts +111 -0
  59. package/dist/types-cmBL8zOc.d.ts +51 -0
  60. package/package.json +170 -130
  61. package/theme.css +20 -0
  62. package/themes/nosferry.css +4 -0
  63. package/dist/chunk-42BJBH56.js +0 -786
  64. package/dist/chunk-42BJBH56.js.map +0 -1
  65. package/dist/chunk-5MQWJVM2.js.map +0 -1
  66. package/dist/chunk-A2SIZOEA.js.map +0 -1
  67. package/dist/chunk-POE7OYET.js +0 -96
  68. package/dist/chunk-POE7OYET.js.map +0 -1
  69. package/dist/chunk-UXL33CZ4.js.map +0 -1
  70. package/dist/date-range-filter-QEa8fhUM.d.ts +0 -149
  71. package/dist/sidebar-BDhG9SUU.d.ts +0 -88
  72. package/dist/types-Cno1_Ijx.d.ts +0 -40
  73. package/dist/types-cOieac8G.d.ts +0 -77
package/dist/blocks.js CHANGED
@@ -1,12 +1,23 @@
1
- import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Popover, PopoverTrigger, PopoverContent, Tabs, TabsList, TabsTrigger, TabsContent, Label, Textarea, Switch, Calendar, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-42BJBH56.js';
2
- export { DEFAULT_TOUR_LABELS, TourProvider, resolveSteps, shouldStartTour, stepsForBreakpoint, useTour, useTourController } from './chunk-UXL33CZ4.js';
3
- import { Button, cn, Input, elevatedSurface, recessedSurface } from './chunk-POE7OYET.js';
4
- import { createContext, useState, useCallback, useEffect, useContext } from 'react';
1
+ import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, ConfirmDialog, Popover, PopoverTrigger, PopoverContent, FloatingSheet, FloatingSheetBody, FloatingSheetFooter, Tabs, TabsList, TabsTrigger, TabsContent, Textarea, Switch, FieldError, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, InputOTP, InputOTPGroup, InputOTPSlot, Alert, AlertDescription, Calendar } from './chunk-CFDDAFPJ.js';
2
+ import { CopyButton } from './chunk-2WIIRDHT.js';
3
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Label } from './chunk-T2INZOCM.js';
4
+ import { Card } from './chunk-RPCL7VFQ.js';
5
+ export { DEFAULT_TOUR_LABELS, TourProvider, resolveSteps, shouldStartTour, stepsForBreakpoint, useTour, useTourController } from './chunk-YOVY3KT6.js';
6
+ import { useUiLabels, useUiLocale } from './chunk-5AJFQ6C4.js';
7
+ export { UiLocaleProvider, useUiLabels, useUiLocale } from './chunk-5AJFQ6C4.js';
8
+ import { resolveLink } from './chunk-L2H5GIF7.js';
9
+ import { Input } from './chunk-XZCC6MTE.js';
10
+ import { Button, cn, elevatedSurface, nestedSurfaceReset, recessedSurface, compactRadius, controlRadius, Spinner } from './chunk-ODYCBCKS.js';
11
+ import { createContext, useState, useCallback, useEffect, useContext, useId, useRef } from 'react';
5
12
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
- import { CalendarDays, ChevronLeft, ArrowLeftToLine, X, ArrowRight, ArrowUpRight, ArrowDownRight } from 'lucide-react';
13
+ import { TriangleAlert, CalendarDays, ArrowLeft, ChevronRight, AlertCircle, ShieldOff, EyeOff, Eye, RefreshCw, ChevronLeft, ArrowLeftToLine, X, ArrowRight, ArrowUpRight, ArrowDownRight } from 'lucide-react';
7
14
  import { subYears, subQuarters, subMonths, subWeeks, subDays, endOfYear, endOfQuarter, endOfMonth, endOfDay, startOfYear, startOfQuarter, startOfMonth, startOfDay, isSameYear, format, endOfWeek, startOfWeek } from 'date-fns';
8
15
  import { pt } from 'date-fns/locale';
9
16
 
17
+ var commandPaletteDefaultLabels = {
18
+ placeholder: "Search...",
19
+ noResultsLabel: "No results found"
20
+ };
10
21
  function CommandPalette({
11
22
  open,
12
23
  onOpenChange,
@@ -14,12 +25,16 @@ function CommandPalette({
14
25
  onSelect,
15
26
  query,
16
27
  onQueryChange,
17
- placeholder = "Search...",
18
- noResultsLabel = "No results found",
28
+ placeholder,
29
+ noResultsLabel,
19
30
  emptyState,
20
31
  className
21
32
  }) {
22
- const resolvedEmptyState = emptyState ?? noResultsLabel;
33
+ const labels = useUiLabels("commandPalette", commandPaletteDefaultLabels, {
34
+ placeholder,
35
+ noResultsLabel
36
+ });
37
+ const resolvedEmptyState = emptyState ?? labels.noResultsLabel;
23
38
  return /* @__PURE__ */ jsxs(
24
39
  CommandDialog,
25
40
  {
@@ -30,7 +45,7 @@ function CommandPalette({
30
45
  /* @__PURE__ */ jsx(
31
46
  CommandInput,
32
47
  {
33
- placeholder,
48
+ placeholder: labels.placeholder,
34
49
  value: query,
35
50
  onValueChange: onQueryChange
36
51
  }
@@ -70,6 +85,98 @@ function useCommandPalette(initialOpen = false) {
70
85
  }, [toggle]);
71
86
  return { open, setOpen, toggle };
72
87
  }
88
+ var dangerZoneLabels = {
89
+ title: "Danger zone",
90
+ description: "These actions are permanent and cannot be undone.",
91
+ actionLabel: "Delete",
92
+ confirmTitle: "Confirm Action",
93
+ confirmDescription: "Are you sure you want to continue? This action cannot be undone.",
94
+ confirmText: "Confirm",
95
+ cancelText: "Cancel",
96
+ requiredValueLabel: "Type {{value}} to confirm"
97
+ };
98
+ function DangerZone({
99
+ title,
100
+ description,
101
+ actions,
102
+ processing = false,
103
+ labels,
104
+ footer,
105
+ className,
106
+ slotName = "danger-zone"
107
+ }) {
108
+ const copy = { ...dangerZoneLabels, ...labels };
109
+ const [activeId, setActiveId] = useState(null);
110
+ const active = actions.find((action) => action.id === activeId) ?? null;
111
+ return /* @__PURE__ */ jsxs(
112
+ "section",
113
+ {
114
+ className: cn(
115
+ elevatedSurface,
116
+ nestedSurfaceReset,
117
+ "gap-6 p-6 flex flex-col bg-destructive/5 ring-destructive/30",
118
+ className
119
+ ),
120
+ "data-slot": slotName,
121
+ children: [
122
+ /* @__PURE__ */ jsxs("div", { "data-slot": "danger-zone-header", className: "gap-3 flex", children: [
123
+ /* @__PURE__ */ jsx("span", { className: "size-11 rounded-2xl flex shrink-0 items-center justify-center bg-destructive/10 text-destructive", children: /* @__PURE__ */ jsx(TriangleAlert, { className: "size-5" }) }),
124
+ /* @__PURE__ */ jsxs("div", { children: [
125
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-destructive", children: title ?? copy.title }),
126
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: description ?? copy.description })
127
+ ] })
128
+ ] }),
129
+ /* @__PURE__ */ jsx("ul", { "data-slot": "danger-zone-actions", className: "gap-4 flex flex-col", children: actions.map((action) => /* @__PURE__ */ jsxs(
130
+ "li",
131
+ {
132
+ "data-slot": "danger-zone-action",
133
+ "data-action-id": action.id,
134
+ className: "gap-4 sm:flex-row sm:items-center sm:justify-between flex flex-col",
135
+ children: [
136
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
137
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: action.title }),
138
+ action.description && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: action.description })
139
+ ] }),
140
+ /* @__PURE__ */ jsx(
141
+ Button,
142
+ {
143
+ type: "button",
144
+ variant: "destructive",
145
+ disabled: processing || action.disabled,
146
+ onClick: () => setActiveId(action.id),
147
+ className: "sm:w-auto w-full",
148
+ children: action.actionLabel ?? copy.actionLabel
149
+ }
150
+ )
151
+ ]
152
+ },
153
+ action.id
154
+ )) }),
155
+ footer,
156
+ /* @__PURE__ */ jsx(
157
+ ConfirmDialog,
158
+ {
159
+ open: active !== null,
160
+ onOpenChange: (open) => {
161
+ if (!open) {
162
+ setActiveId(null);
163
+ }
164
+ },
165
+ variant: "destructive",
166
+ processing,
167
+ title: active?.confirmTitle ?? copy.confirmTitle,
168
+ description: active?.confirmDescription ?? copy.confirmDescription,
169
+ confirmText: active?.confirmText ?? copy.confirmText,
170
+ cancelText: active?.cancelText ?? copy.cancelText,
171
+ requiredValue: active?.requiredValue,
172
+ requiredValueLabel: active?.requiredValueLabel ?? copy.requiredValueLabel,
173
+ onConfirm: () => active?.onConfirm()
174
+ }
175
+ )
176
+ ]
177
+ }
178
+ );
179
+ }
73
180
  var DateFilterContext = createContext(null);
74
181
  var DateFilterProvider = DateFilterContext.Provider;
75
182
  function useDateFilter() {
@@ -407,22 +514,24 @@ var DEFAULT_LABELS = {
407
514
  function DateFilter({
408
515
  value,
409
516
  onChange,
410
- presets = DEFAULT_PRESETS,
411
- operators = DEFAULT_OPERATORS,
412
- units = DEFAULT_UNITS,
517
+ presets,
518
+ operators,
519
+ units,
413
520
  labels,
414
521
  children
415
522
  }) {
523
+ const locale = useUiLocale();
524
+ const resolvedLabels = useUiLabels("dateFilter", DEFAULT_LABELS, labels);
416
525
  const [open, setOpen] = useState(false);
417
526
  const [panel, setPanel] = useState("root");
418
527
  const [draft, setDraft] = useState(value);
419
528
  const context = {
420
529
  value,
421
530
  draft,
422
- labels: { ...DEFAULT_LABELS, ...labels },
423
- presets,
424
- operators,
425
- units,
531
+ labels: resolvedLabels,
532
+ presets: presets ?? locale.dateFilterPresets ?? DEFAULT_PRESETS,
533
+ operators: operators ?? locale.dateFilterOperators ?? DEFAULT_OPERATORS,
534
+ units: units ?? locale.dateFilterUnits ?? DEFAULT_UNITS,
426
535
  panel,
427
536
  setDraft,
428
537
  openPanel: (next, seed) => {
@@ -572,9 +681,87 @@ function DateFilterRelative({ children }) {
572
681
  );
573
682
  }
574
683
 
575
- // src/blocks/date-filter/encode.ts
684
+ // src/blocks/date-filter/decode.ts
576
685
  var RANGE_SEPARATOR = "~";
577
686
  var RELATIVE_PREFIX = "previous";
687
+ var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
688
+ var UNITS = ["day", "week", "month", "quarter", "year"];
689
+ var ALL = { mode: "all" };
690
+ function decodeDateFilter(encoded) {
691
+ if (!encoded) {
692
+ return ALL;
693
+ }
694
+ if (encoded.startsWith(`${RELATIVE_PREFIX}:`)) {
695
+ return decodeRelative(encoded);
696
+ }
697
+ if (encoded.includes(RANGE_SEPARATOR)) {
698
+ return decodeFixedRange(encoded);
699
+ }
700
+ if (ISO_DATE.test(encoded)) {
701
+ return isDate(encoded) ? { mode: "fixed", operator: "on", start: encoded } : ALL;
702
+ }
703
+ return { mode: "preset", preset: encoded };
704
+ }
705
+ function decodeFixedRange(encoded) {
706
+ const parts = encoded.split(RANGE_SEPARATOR);
707
+ if (parts.length !== 2) {
708
+ return ALL;
709
+ }
710
+ const [start, end] = parts;
711
+ if (start && end) {
712
+ return isDate(start) && isDate(end) ? { mode: "fixed", operator: "between", start, end } : ALL;
713
+ }
714
+ if (start) {
715
+ return isDate(start) ? { mode: "fixed", operator: "after", start } : ALL;
716
+ }
717
+ if (end) {
718
+ return isDate(end) ? { mode: "fixed", operator: "before", start: end } : ALL;
719
+ }
720
+ return ALL;
721
+ }
722
+ function decodeRelative(encoded) {
723
+ const [, rawAmount, rawUnit, ...rest] = encoded.split(":");
724
+ const amount = Number(rawAmount);
725
+ const unit = toUnit(rawUnit);
726
+ if (!unit || !Number.isInteger(amount) || amount < 1) {
727
+ return ALL;
728
+ }
729
+ const value = { mode: "relative", amount, unit };
730
+ const tail = [...rest];
731
+ if (tail[0] === "current") {
732
+ value.include_current = true;
733
+ tail.shift();
734
+ }
735
+ if (tail.length === 0) {
736
+ return value;
737
+ }
738
+ if (tail.length !== 3 || tail[0] !== "offset") {
739
+ return ALL;
740
+ }
741
+ const offsetAmount = Number(tail[1]);
742
+ const offsetUnit = toUnit(tail[2]);
743
+ if (!offsetUnit || !Number.isInteger(offsetAmount) || offsetAmount < 1) {
744
+ return ALL;
745
+ }
746
+ value.offset_amount = offsetAmount;
747
+ value.offset_unit = offsetUnit;
748
+ return value;
749
+ }
750
+ function toUnit(value) {
751
+ return UNITS.find((unit) => unit === value) ?? null;
752
+ }
753
+ function isDate(value) {
754
+ if (!ISO_DATE.test(value)) {
755
+ return false;
756
+ }
757
+ const [year, month, day] = value.split("-").map(Number);
758
+ const date = new Date(Date.UTC(year, month - 1, day));
759
+ return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
760
+ }
761
+
762
+ // src/blocks/date-filter/encode.ts
763
+ var RANGE_SEPARATOR2 = "~";
764
+ var RELATIVE_PREFIX2 = "previous";
578
765
  function encodeDateFilter(filter) {
579
766
  if (filter.mode === "preset") {
580
767
  return filter.preset ?? null;
@@ -592,21 +779,21 @@ function encodeFixed(filter) {
592
779
  return null;
593
780
  }
594
781
  if (filter.operator === "before") {
595
- return `${RANGE_SEPARATOR}${filter.start}`;
782
+ return `${RANGE_SEPARATOR2}${filter.start}`;
596
783
  }
597
784
  if (filter.operator === "after") {
598
- return `${filter.start}${RANGE_SEPARATOR}`;
785
+ return `${filter.start}${RANGE_SEPARATOR2}`;
599
786
  }
600
787
  if (filter.operator === "on") {
601
788
  return filter.start;
602
789
  }
603
- return filter.end ? `${filter.start}${RANGE_SEPARATOR}${filter.end}` : null;
790
+ return filter.end ? `${filter.start}${RANGE_SEPARATOR2}${filter.end}` : null;
604
791
  }
605
792
  function encodeRelative(filter) {
606
793
  if (!filter.unit || !filter.amount || filter.amount < 1) {
607
794
  return null;
608
795
  }
609
- let value = `${RELATIVE_PREFIX}:${filter.amount}:${filter.unit}`;
796
+ let value = `${RELATIVE_PREFIX2}:${filter.amount}:${filter.unit}`;
610
797
  if (filter.include_current) {
611
798
  value += ":current";
612
799
  }
@@ -615,20 +802,263 @@ function encodeRelative(filter) {
615
802
  }
616
803
  return value;
617
804
  }
805
+ var formOverlayDefaultLabels = {
806
+ cancelLabel: "Cancel",
807
+ saveLabel: "Save",
808
+ savingLabel: "Saving..."
809
+ };
810
+ function FormOverlayActions({
811
+ labels = formOverlayDefaultLabels,
812
+ processing = false,
813
+ intent = "default",
814
+ submit = false,
815
+ className,
816
+ onCancel,
817
+ onSave,
818
+ slotName = "form-overlay-actions"
819
+ }) {
820
+ return /* @__PURE__ */ jsxs(
821
+ "div",
822
+ {
823
+ className: cn("gap-2 flex items-center justify-end", className),
824
+ "data-slot": slotName,
825
+ children: [
826
+ /* @__PURE__ */ jsx(
827
+ Button,
828
+ {
829
+ type: "button",
830
+ variant: "ghost",
831
+ disabled: processing,
832
+ onClick: onCancel,
833
+ children: labels.cancelLabel
834
+ }
835
+ ),
836
+ /* @__PURE__ */ jsx(
837
+ Button,
838
+ {
839
+ type: submit ? "submit" : "button",
840
+ "aria-label": processing ? labels.savingLabel : void 0,
841
+ variant: intent,
842
+ loading: processing,
843
+ loadingLabel: labels.savingLabel,
844
+ onClick: onSave,
845
+ children: processing ? labels.savingLabel : labels.saveLabel
846
+ }
847
+ )
848
+ ]
849
+ }
850
+ );
851
+ }
852
+ function DetailEditSheet({
853
+ open,
854
+ onOpenChange,
855
+ title,
856
+ description,
857
+ processing = false,
858
+ intent,
859
+ labels,
860
+ className,
861
+ children,
862
+ onSave,
863
+ onCancel,
864
+ slotName = "detail-edit-sheet"
865
+ }) {
866
+ const handleSubmit = (event) => {
867
+ event.preventDefault();
868
+ if (processing) {
869
+ return;
870
+ }
871
+ onSave();
872
+ };
873
+ const handleCancel = () => {
874
+ if (processing) {
875
+ return;
876
+ }
877
+ onCancel?.();
878
+ onOpenChange(false);
879
+ };
880
+ return /* @__PURE__ */ jsx(
881
+ FloatingSheet,
882
+ {
883
+ open,
884
+ onOpenChange,
885
+ title,
886
+ description,
887
+ persistent: true,
888
+ className,
889
+ children: /* @__PURE__ */ jsxs(
890
+ "form",
891
+ {
892
+ onSubmit: handleSubmit,
893
+ className: "min-h-0 flex flex-1 flex-col",
894
+ "data-slot": slotName,
895
+ children: [
896
+ /* @__PURE__ */ jsx(FloatingSheetBody, { children }),
897
+ /* @__PURE__ */ jsx(FloatingSheetFooter, { children: /* @__PURE__ */ jsx(
898
+ FormOverlayActions,
899
+ {
900
+ submit: true,
901
+ labels,
902
+ processing,
903
+ intent,
904
+ onCancel: handleCancel
905
+ }
906
+ ) })
907
+ ]
908
+ }
909
+ )
910
+ }
911
+ );
912
+ }
913
+ function FormDialog({
914
+ open,
915
+ onOpenChange,
916
+ title,
917
+ description,
918
+ processing = false,
919
+ intent,
920
+ labels,
921
+ className,
922
+ children,
923
+ onSave,
924
+ onCancel,
925
+ slotName = "form-dialog"
926
+ }) {
927
+ const handleSubmit = (event) => {
928
+ event.preventDefault();
929
+ if (processing) {
930
+ return;
931
+ }
932
+ onSave();
933
+ };
934
+ const handleCancel = () => {
935
+ if (processing) {
936
+ return;
937
+ }
938
+ onCancel?.();
939
+ onOpenChange(false);
940
+ };
941
+ const blockWhileProcessing = (event) => {
942
+ if (processing) {
943
+ event.preventDefault();
944
+ }
945
+ };
946
+ return /* @__PURE__ */ jsx(
947
+ Dialog,
948
+ {
949
+ open,
950
+ onOpenChange: (next) => {
951
+ if (processing && !next) {
952
+ return;
953
+ }
954
+ onOpenChange(next);
955
+ },
956
+ children: /* @__PURE__ */ jsx(
957
+ DialogContent,
958
+ {
959
+ ...description ? {} : { "aria-describedby": void 0 },
960
+ className: cn("gap-0 p-0", className),
961
+ onEscapeKeyDown: blockWhileProcessing,
962
+ onInteractOutside: blockWhileProcessing,
963
+ slotName,
964
+ children: /* @__PURE__ */ jsxs(
965
+ "form",
966
+ {
967
+ "data-slot": "form-dialog-form",
968
+ onSubmit: handleSubmit,
969
+ className: "grid max-h-[calc(100dvh-8rem)] grid-rows-[auto_minmax(0,1fr)_auto]",
970
+ children: [
971
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "text-left", children: [
972
+ /* @__PURE__ */ jsx(DialogTitle, { children: title }),
973
+ description ? /* @__PURE__ */ jsx(DialogDescription, { children: description }) : null
974
+ ] }),
975
+ /* @__PURE__ */ jsx(
976
+ "div",
977
+ {
978
+ "data-slot": "form-dialog-body",
979
+ className: "gap-4 px-6 pb-6 md:px-8 flex flex-col overflow-y-auto",
980
+ children
981
+ }
982
+ ),
983
+ /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(
984
+ FormOverlayActions,
985
+ {
986
+ submit: true,
987
+ labels,
988
+ processing,
989
+ intent,
990
+ onCancel: handleCancel,
991
+ className: "w-full"
992
+ }
993
+ ) })
994
+ ]
995
+ }
996
+ )
997
+ }
998
+ )
999
+ }
1000
+ );
1001
+ }
1002
+ function copyableText(value, copyValue) {
1003
+ if (copyValue !== void 0) {
1004
+ return copyValue.trim();
1005
+ }
1006
+ if (typeof value === "string" || typeof value === "number") {
1007
+ return String(value).trim();
1008
+ }
1009
+ return "";
1010
+ }
618
1011
  function InfoField({
619
1012
  icon: Icon,
620
1013
  label,
621
1014
  value,
1015
+ copyable = false,
1016
+ copyValue,
1017
+ copyLabel,
1018
+ copiedLabel,
622
1019
  iconClassName,
623
- className
1020
+ className,
1021
+ slotName = "info-field"
624
1022
  }) {
625
- return /* @__PURE__ */ jsxs("div", { className: cn("gap-3 flex items-center", className), children: [
626
- /* @__PURE__ */ jsx("div", { className: cn("p-2 rounded-xl bg-muted", iconClassName), children: /* @__PURE__ */ jsx(Icon, { className: "size-5 text-muted-foreground" }) }),
627
- /* @__PURE__ */ jsxs("div", { children: [
628
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium tracking-wider text-muted-foreground uppercase", children: label }),
629
- /* @__PURE__ */ jsx("p", { className: "font-semibold", children: value })
630
- ] })
631
- ] });
1023
+ const text = copyableText(value, copyValue);
1024
+ return /* @__PURE__ */ jsxs(
1025
+ "div",
1026
+ {
1027
+ className: cn("gap-3 flex items-center", className),
1028
+ "data-slot": slotName,
1029
+ children: [
1030
+ /* @__PURE__ */ jsx("div", { className: cn("p-2 rounded-xl bg-muted", iconClassName), children: /* @__PURE__ */ jsx(Icon, { className: "size-5 text-muted-foreground" }) }),
1031
+ /* @__PURE__ */ jsxs("div", { children: [
1032
+ /* @__PURE__ */ jsx(
1033
+ "p",
1034
+ {
1035
+ "data-slot": "info-field-label",
1036
+ className: "text-xs font-medium tracking-wider text-muted-foreground uppercase",
1037
+ children: label
1038
+ }
1039
+ ),
1040
+ /* @__PURE__ */ jsxs(
1041
+ "p",
1042
+ {
1043
+ "data-slot": "info-field-value",
1044
+ className: "gap-1 font-semibold flex items-center",
1045
+ children: [
1046
+ value,
1047
+ copyable && text !== "" && /* @__PURE__ */ jsx(
1048
+ CopyButton,
1049
+ {
1050
+ value: text,
1051
+ copyLabel,
1052
+ copiedLabel
1053
+ }
1054
+ )
1055
+ ]
1056
+ }
1057
+ )
1058
+ ] })
1059
+ ]
1060
+ }
1061
+ );
632
1062
  }
633
1063
  function InfoFieldGroup({ children, className }) {
634
1064
  return /* @__PURE__ */ jsx("div", { className: cn("space-y-4", className), children });
@@ -730,12 +1160,12 @@ function SettingsCard({
730
1160
  control,
731
1161
  children,
732
1162
  inset = false,
733
- className
1163
+ className,
1164
+ slotName = "settings-card"
734
1165
  }) {
735
1166
  return /* @__PURE__ */ jsxs(
736
1167
  "section",
737
1168
  {
738
- "data-slot": "settings-card",
739
1169
  "data-inset": inset || void 0,
740
1170
  className: cn(
741
1171
  elevatedSurface,
@@ -743,6 +1173,7 @@ function SettingsCard({
743
1173
  inset && recessedSurface,
744
1174
  className
745
1175
  ),
1176
+ "data-slot": slotName,
746
1177
  children: [
747
1178
  /* @__PURE__ */ jsxs("div", { className: "gap-4 sm:flex-row sm:items-center sm:justify-between flex flex-col", children: [
748
1179
  /* @__PURE__ */ jsxs("div", { className: "gap-3 flex items-center", children: [
@@ -773,12 +1204,12 @@ function SettingsPanel({
773
1204
  description,
774
1205
  children,
775
1206
  inset = true,
776
- className
1207
+ className,
1208
+ slotName = "settings-panel"
777
1209
  }) {
778
1210
  return /* @__PURE__ */ jsxs(
779
1211
  "div",
780
1212
  {
781
- "data-slot": "settings-panel",
782
1213
  "data-inset": inset || void 0,
783
1214
  className: cn(
784
1215
  elevatedSurface,
@@ -786,6 +1217,7 @@ function SettingsPanel({
786
1217
  inset && recessedSurface,
787
1218
  className
788
1219
  ),
1220
+ "data-slot": slotName,
789
1221
  children: [
790
1222
  (title || description) && /* @__PURE__ */ jsxs("div", { children: [
791
1223
  title && /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground", children: title }),
@@ -827,6 +1259,346 @@ function ToggleRow({
827
1259
  )
828
1260
  ] });
829
1261
  }
1262
+ function SettingsField({
1263
+ id,
1264
+ label,
1265
+ description,
1266
+ error,
1267
+ required,
1268
+ className,
1269
+ children,
1270
+ slotName = "settings-field"
1271
+ }) {
1272
+ const generated = useId();
1273
+ const fieldId = id ?? generated;
1274
+ return /* @__PURE__ */ jsxs(
1275
+ "div",
1276
+ {
1277
+ "data-invalid": error ? true : void 0,
1278
+ className: cn("space-y-2 py-1", className),
1279
+ "data-slot": slotName,
1280
+ children: [
1281
+ /* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
1282
+ label,
1283
+ required && /* @__PURE__ */ jsx(
1284
+ "span",
1285
+ {
1286
+ "data-slot": "settings-field-required",
1287
+ "aria-hidden": "true",
1288
+ className: "text-destructive",
1289
+ children: "*"
1290
+ }
1291
+ )
1292
+ ] }),
1293
+ children(fieldId),
1294
+ description && /* @__PURE__ */ jsx(
1295
+ "p",
1296
+ {
1297
+ "data-slot": "settings-field-description",
1298
+ className: "text-xs font-medium text-muted-foreground",
1299
+ children: description
1300
+ }
1301
+ ),
1302
+ /* @__PURE__ */ jsx(FieldError, { message: error })
1303
+ ]
1304
+ }
1305
+ );
1306
+ }
1307
+ function TextField({
1308
+ type = "text",
1309
+ value,
1310
+ onChange,
1311
+ disabled,
1312
+ placeholder,
1313
+ slotName = "text-field",
1314
+ ...field
1315
+ }) {
1316
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsx(
1317
+ Input,
1318
+ {
1319
+ id: fieldId,
1320
+ type,
1321
+ value,
1322
+ disabled,
1323
+ placeholder,
1324
+ "aria-invalid": field.error ? true : void 0,
1325
+ slotName,
1326
+ onChange: (event) => onChange(event.target.value)
1327
+ }
1328
+ ) });
1329
+ }
1330
+ function NumberField({
1331
+ value,
1332
+ onChange,
1333
+ disabled,
1334
+ placeholder,
1335
+ min,
1336
+ max,
1337
+ step,
1338
+ slotName = "number-field",
1339
+ ...field
1340
+ }) {
1341
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsx(
1342
+ Input,
1343
+ {
1344
+ id: fieldId,
1345
+ type: "number",
1346
+ inputMode: "decimal",
1347
+ value: value === "" ? "" : String(value),
1348
+ min,
1349
+ max,
1350
+ step,
1351
+ disabled,
1352
+ placeholder,
1353
+ "aria-invalid": field.error ? true : void 0,
1354
+ slotName,
1355
+ onChange: (event) => onChange(
1356
+ event.target.value === "" ? "" : event.target.valueAsNumber
1357
+ )
1358
+ }
1359
+ ) });
1360
+ }
1361
+ function DateField({
1362
+ value,
1363
+ onChange,
1364
+ disabled,
1365
+ min,
1366
+ max,
1367
+ slotName = "date-field",
1368
+ ...field
1369
+ }) {
1370
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsx(
1371
+ Input,
1372
+ {
1373
+ id: fieldId,
1374
+ type: "date",
1375
+ value,
1376
+ min,
1377
+ max,
1378
+ disabled,
1379
+ "aria-invalid": field.error ? true : void 0,
1380
+ slotName,
1381
+ onChange: (event) => onChange(event.target.value)
1382
+ }
1383
+ ) });
1384
+ }
1385
+ function SelectField({
1386
+ value,
1387
+ onChange,
1388
+ options,
1389
+ disabled,
1390
+ placeholder,
1391
+ slotName = "select-field",
1392
+ ...field
1393
+ }) {
1394
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsxs(
1395
+ Select,
1396
+ {
1397
+ value,
1398
+ onValueChange: onChange,
1399
+ disabled,
1400
+ children: [
1401
+ /* @__PURE__ */ jsx(
1402
+ SelectTrigger,
1403
+ {
1404
+ id: fieldId,
1405
+ slotName,
1406
+ "aria-invalid": field.error ? true : void 0,
1407
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
1408
+ }
1409
+ ),
1410
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(
1411
+ SelectItem,
1412
+ {
1413
+ value: option.value,
1414
+ disabled: option.disabled,
1415
+ children: option.label
1416
+ },
1417
+ option.value
1418
+ )) })
1419
+ ]
1420
+ }
1421
+ ) });
1422
+ }
1423
+ var SettingsLinkContext = createContext(void 0);
1424
+ function SettingsPage({
1425
+ title,
1426
+ description,
1427
+ control,
1428
+ linkComponent,
1429
+ children,
1430
+ className,
1431
+ slotName = "settings-page"
1432
+ }) {
1433
+ return /* @__PURE__ */ jsx(SettingsLinkContext.Provider, { value: linkComponent, children: /* @__PURE__ */ jsxs(
1434
+ "div",
1435
+ {
1436
+ className: cn(
1437
+ "gap-8 max-w-4xl mx-auto flex w-full flex-col",
1438
+ className
1439
+ ),
1440
+ "data-slot": slotName,
1441
+ children: [
1442
+ title && /* @__PURE__ */ jsx(
1443
+ SectionHeader,
1444
+ {
1445
+ title,
1446
+ description,
1447
+ control
1448
+ }
1449
+ ),
1450
+ children
1451
+ ]
1452
+ }
1453
+ ) });
1454
+ }
1455
+ var settingsLabels = {
1456
+ back: "Back"
1457
+ };
1458
+ function SettingsSection({
1459
+ title,
1460
+ description,
1461
+ control,
1462
+ backHref = "/settings",
1463
+ backLabel,
1464
+ wide = false,
1465
+ linkComponent,
1466
+ children,
1467
+ className,
1468
+ slotName = "settings-section"
1469
+ }) {
1470
+ const labels = useUiLabels("settings", settingsLabels, {
1471
+ back: backLabel
1472
+ });
1473
+ const contextLink = useContext(SettingsLinkContext);
1474
+ const Link = resolveLink(linkComponent ?? contextLink);
1475
+ return /* @__PURE__ */ jsxs(
1476
+ "section",
1477
+ {
1478
+ className: cn(
1479
+ "gap-6 mx-auto flex w-full flex-col",
1480
+ wide ? "max-w-4xl" : "max-w-2xl",
1481
+ className
1482
+ ),
1483
+ "data-slot": slotName,
1484
+ children: [
1485
+ backHref && /* @__PURE__ */ jsxs(
1486
+ Link,
1487
+ {
1488
+ "data-slot": "settings-section-back",
1489
+ href: backHref,
1490
+ className: "gap-2 text-sm font-medium inline-flex w-fit items-center text-muted-foreground transition-colors hover:text-foreground",
1491
+ children: [
1492
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "size-4" }),
1493
+ labels.back
1494
+ ]
1495
+ }
1496
+ ),
1497
+ title && /* @__PURE__ */ jsx(
1498
+ SectionHeader,
1499
+ {
1500
+ title,
1501
+ description,
1502
+ control
1503
+ }
1504
+ ),
1505
+ children
1506
+ ]
1507
+ }
1508
+ );
1509
+ }
1510
+ function SettingsGroup({
1511
+ label,
1512
+ columns = 2,
1513
+ children,
1514
+ className,
1515
+ slotName = "settings-group"
1516
+ }) {
1517
+ return /* @__PURE__ */ jsxs(
1518
+ "div",
1519
+ {
1520
+ className: cn("gap-3 flex flex-col", className),
1521
+ "data-slot": slotName,
1522
+ children: [
1523
+ label && /* @__PURE__ */ jsx("p", { className: "font-bold tracking-widest text-[11px] text-muted-foreground uppercase", children: label }),
1524
+ /* @__PURE__ */ jsx(
1525
+ "div",
1526
+ {
1527
+ "data-slot": "settings-group-grid",
1528
+ className: cn(
1529
+ "gap-4 grid grid-cols-1",
1530
+ columns === 2 && "md:grid-cols-2"
1531
+ ),
1532
+ children
1533
+ }
1534
+ )
1535
+ ]
1536
+ }
1537
+ );
1538
+ }
1539
+ function SettingsEntry({
1540
+ icon: Icon,
1541
+ iconClassName,
1542
+ title,
1543
+ description,
1544
+ href,
1545
+ badge,
1546
+ disabled = false,
1547
+ linkComponent,
1548
+ className,
1549
+ slotName = "settings-entry"
1550
+ }) {
1551
+ const contextLink = useContext(SettingsLinkContext);
1552
+ const Link = resolveLink(linkComponent ?? contextLink);
1553
+ const card = /* @__PURE__ */ jsxs(
1554
+ Card,
1555
+ {
1556
+ interactive: !disabled,
1557
+ padding: "none",
1558
+ "data-disabled": disabled || void 0,
1559
+ className: cn(
1560
+ "gap-4 p-5 flex h-full flex-row items-center",
1561
+ disabled && "opacity-60",
1562
+ className
1563
+ ),
1564
+ children: [
1565
+ /* @__PURE__ */ jsx(
1566
+ "span",
1567
+ {
1568
+ className: cn(
1569
+ "size-11 flex shrink-0 items-center justify-center rounded-full",
1570
+ iconClassName ?? "bg-primary/10 text-primary"
1571
+ ),
1572
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-5" })
1573
+ }
1574
+ ),
1575
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex flex-1 flex-col", children: [
1576
+ /* @__PURE__ */ jsxs("span", { className: "gap-2 flex items-center", children: [
1577
+ /* @__PURE__ */ jsx("span", { className: "font-bold text-foreground", children: title }),
1578
+ badge
1579
+ ] }),
1580
+ description && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: description })
1581
+ ] }),
1582
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 shrink-0 text-muted-foreground" })
1583
+ ]
1584
+ }
1585
+ );
1586
+ if (disabled) {
1587
+ return /* @__PURE__ */ jsx("div", { "data-disabled": "true", "aria-disabled": "true", "data-slot": slotName, children: card });
1588
+ }
1589
+ return /* @__PURE__ */ jsx(
1590
+ Link,
1591
+ {
1592
+ href,
1593
+ className: cn(
1594
+ compactRadius,
1595
+ "outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
1596
+ ),
1597
+ "data-slot": slotName,
1598
+ children: card
1599
+ }
1600
+ );
1601
+ }
830
1602
  function resolveTrend(trend) {
831
1603
  if (trend === null || trend === void 0 || Number.isNaN(trend)) {
832
1604
  return null;
@@ -861,13 +1633,13 @@ function StatCard({
861
1633
  trend,
862
1634
  comparisonLabel,
863
1635
  inset = false,
864
- className
1636
+ className,
1637
+ slotName = "stat-card"
865
1638
  }) {
866
1639
  const resolvedTrend = resolveTrend(trend);
867
1640
  return /* @__PURE__ */ jsxs(
868
1641
  "div",
869
1642
  {
870
- "data-slot": "stat-card",
871
1643
  "data-inset": inset || void 0,
872
1644
  className: cn(
873
1645
  elevatedSurface,
@@ -875,6 +1647,7 @@ function StatCard({
875
1647
  inset && recessedSurface,
876
1648
  className
877
1649
  ),
1650
+ "data-slot": slotName,
878
1651
  children: [
879
1652
  /* @__PURE__ */ jsxs("div", { className: "mb-4 gap-4 flex items-start justify-between", children: [
880
1653
  /* @__PURE__ */ jsx("div", { className: cn("rounded-2xl p-3 shrink-0", iconClassName), children: /* @__PURE__ */ jsx(Icon, { className: "size-6" }) }),
@@ -916,6 +1689,607 @@ function StatsGrid({ children, className }) {
916
1689
  );
917
1690
  }
918
1691
 
919
- export { CommandPalette, DEFAULT_LABELS as DATE_FILTER_LABELS, DEFAULT_OPERATORS as DATE_FILTER_OPERATORS, DEFAULT_PRESETS as DATE_FILTER_PRESETS, DEFAULT_UNITS as DATE_FILTER_UNITS, DateFilter, DateFilterAll, DateFilterContent, DateFilterFixed, DateFilterItem, DateFilterPresets, DateFilterRelative, DateFilterSeparator, DateFilterTrigger, InfoField, InfoFieldGroup, LocalizedFields, SectionHeader, SettingsCard, SettingsPanel, StatCard, StatsGrid, ToggleRow, encodeDateFilter, formatRangePreview, resolveRelativeRange, summariseDateFilter, useCommandPalette, useDateFilter };
1692
+ // src/blocks/two-factor/types.ts
1693
+ var twoFactorLabels = {
1694
+ setupTitle: "Two-factor authentication",
1695
+ setupDescription: "Add a second step to your sign in with an authenticator app.",
1696
+ pendingLabel: "Preparing your setup key",
1697
+ scanTitle: "Scan the code",
1698
+ scanDescription: "Open your authenticator app and scan the code below to add this account.",
1699
+ qrFallbackLabel: "The QR code is not available yet.",
1700
+ manualKeyLabel: "Setup key",
1701
+ manualKeyDescription: "Enter this key by hand if your app cannot scan the code.",
1702
+ manualKeyRevealLabel: "Show setup key",
1703
+ manualKeyHideLabel: "Hide setup key",
1704
+ continueLabel: "Continue",
1705
+ confirmTitle: "Confirm the code",
1706
+ confirmDescription: "Enter the six digit code your authenticator app is showing.",
1707
+ codeLabel: "Authentication code",
1708
+ recoveryCodeLabel: "Recovery code",
1709
+ recoveryCodePlaceholder: "Enter a recovery code",
1710
+ useRecoveryCodeLabel: "Use a recovery code",
1711
+ useCodeLabel: "Use an authentication code",
1712
+ verifyLabel: "Verify",
1713
+ verifyingLabel: "Verifying",
1714
+ errorFallbackLabel: "That did not work. Try again.",
1715
+ cancelLabel: "Cancel",
1716
+ challengeTitle: "Two-factor confirmation",
1717
+ challengeDescription: "Confirm access to your account with the code from your authenticator app.",
1718
+ recoveryTitle: "Recovery codes",
1719
+ recoveryDescription: "Store these codes somewhere safe. Each one signs you in once if you lose your device.",
1720
+ recoveryWarning: "They are shown once and cannot be read again afterwards.",
1721
+ revealLabel: "Show codes",
1722
+ hideLabel: "Hide codes",
1723
+ copyLabel: "Copy",
1724
+ copiedLabel: "Copied",
1725
+ copyFailedLabel: "Copying is unavailable here. Select the codes manually.",
1726
+ regenerateLabel: "Regenerate codes",
1727
+ doneLabel: "Done",
1728
+ disableLabel: "Disable two-factor authentication",
1729
+ disableTitle: "Disable two-factor authentication",
1730
+ disableDescription: "Your account will be protected by your password alone. Recovery codes stop working.",
1731
+ disableConfirmLabel: "Disable",
1732
+ disableCancelLabel: "Keep it on"
1733
+ };
1734
+ function resolveLabels(labels) {
1735
+ return labels ? { ...twoFactorLabels, ...labels } : twoFactorLabels;
1736
+ }
1737
+ function messageList(errors) {
1738
+ if (!errors) {
1739
+ return [];
1740
+ }
1741
+ return (Array.isArray(errors) ? errors : [errors]).filter(
1742
+ (message) => message.length > 0
1743
+ );
1744
+ }
1745
+ function errorMessage(reason, fallback) {
1746
+ return reason instanceof Error && reason.message ? reason.message : fallback;
1747
+ }
1748
+ function TwoFactorVerifyForm({
1749
+ onSubmit,
1750
+ errors,
1751
+ allowRecoveryCode = false,
1752
+ length = 6,
1753
+ autoFocus = false,
1754
+ submitLabel,
1755
+ footer,
1756
+ labels,
1757
+ className,
1758
+ slotName = "two-factor-verify-form"
1759
+ }) {
1760
+ const text = resolveLabels(labels);
1761
+ const fieldId = useId();
1762
+ const [mode, setMode] = useState("code");
1763
+ const [value, setValue] = useState("");
1764
+ const [pending, setPending] = useState(false);
1765
+ const [failure, setFailure] = useState(null);
1766
+ const messages = [...messageList(errors), ...messageList(failure)];
1767
+ const complete = mode === "code" ? value.length === length : value.length > 0;
1768
+ const handleSubmit = async (event) => {
1769
+ event.preventDefault();
1770
+ if (pending || !complete) {
1771
+ return;
1772
+ }
1773
+ setFailure(null);
1774
+ setPending(true);
1775
+ try {
1776
+ await onSubmit(value, mode);
1777
+ } catch (reason) {
1778
+ setFailure(errorMessage(reason, text.errorFallbackLabel));
1779
+ } finally {
1780
+ setPending(false);
1781
+ }
1782
+ };
1783
+ const switchMode = () => {
1784
+ setMode(mode === "code" ? "recovery" : "code");
1785
+ setValue("");
1786
+ setFailure(null);
1787
+ };
1788
+ return /* @__PURE__ */ jsxs(
1789
+ "form",
1790
+ {
1791
+ "data-mode": mode,
1792
+ "data-pending": pending || void 0,
1793
+ onSubmit: handleSubmit,
1794
+ className: cn("gap-5 flex w-full flex-col", className),
1795
+ "data-slot": slotName,
1796
+ children: [
1797
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-col", children: [
1798
+ /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: mode === "code" ? text.codeLabel : text.recoveryCodeLabel }),
1799
+ mode === "code" ? /* @__PURE__ */ jsx(
1800
+ InputOTP,
1801
+ {
1802
+ id: fieldId,
1803
+ "data-slot": "two-factor-code-input",
1804
+ maxLength: length,
1805
+ value,
1806
+ autoFocus,
1807
+ disabled: pending,
1808
+ onChange: setValue,
1809
+ "aria-label": text.codeLabel,
1810
+ children: /* @__PURE__ */ jsx(InputOTPGroup, { children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsx(InputOTPSlot, { index }, index)) })
1811
+ }
1812
+ ) : /* @__PURE__ */ jsx(
1813
+ Input,
1814
+ {
1815
+ id: fieldId,
1816
+ slotName: "two-factor-recovery-input",
1817
+ value,
1818
+ autoComplete: "one-time-code",
1819
+ spellCheck: false,
1820
+ disabled: pending,
1821
+ placeholder: text.recoveryCodePlaceholder,
1822
+ "aria-label": text.recoveryCodeLabel,
1823
+ onChange: (event) => setValue(event.target.value)
1824
+ }
1825
+ )
1826
+ ] }),
1827
+ messages.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", slotName: "two-factor-error", children: [
1828
+ /* @__PURE__ */ jsx(AlertCircle, {}),
1829
+ /* @__PURE__ */ jsx(AlertDescription, { children: messages.map((message) => /* @__PURE__ */ jsx("p", { children: message }, message)) })
1830
+ ] }),
1831
+ /* @__PURE__ */ jsxs("div", { className: "gap-3 flex flex-col", children: [
1832
+ /* @__PURE__ */ jsx(
1833
+ Button,
1834
+ {
1835
+ type: "submit",
1836
+ loading: pending,
1837
+ loadingLabel: text.verifyingLabel,
1838
+ disabled: !complete,
1839
+ children: submitLabel ?? text.verifyLabel
1840
+ }
1841
+ ),
1842
+ allowRecoveryCode && /* @__PURE__ */ jsx(
1843
+ Button,
1844
+ {
1845
+ type: "button",
1846
+ variant: "ghost",
1847
+ size: "sm",
1848
+ disabled: pending,
1849
+ onClick: switchMode,
1850
+ children: mode === "code" ? text.useRecoveryCodeLabel : text.useCodeLabel
1851
+ }
1852
+ ),
1853
+ footer
1854
+ ] })
1855
+ ]
1856
+ }
1857
+ );
1858
+ }
1859
+ function TwoFactorChallenge({
1860
+ onSubmit,
1861
+ errors,
1862
+ allowRecoveryCode = true,
1863
+ title,
1864
+ description,
1865
+ footer,
1866
+ labels,
1867
+ className,
1868
+ slotName = "two-factor-challenge"
1869
+ }) {
1870
+ const text = resolveLabels(labels);
1871
+ return /* @__PURE__ */ jsxs(
1872
+ "section",
1873
+ {
1874
+ className: cn("gap-6 flex w-full flex-col", className),
1875
+ "data-slot": slotName,
1876
+ children: [
1877
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-col", children: [
1878
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold tracking-tight text-foreground", children: title ?? text.challengeTitle }),
1879
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: description ?? text.challengeDescription })
1880
+ ] }),
1881
+ /* @__PURE__ */ jsx(
1882
+ TwoFactorVerifyForm,
1883
+ {
1884
+ autoFocus: true,
1885
+ allowRecoveryCode,
1886
+ errors,
1887
+ labels,
1888
+ footer,
1889
+ onSubmit
1890
+ }
1891
+ )
1892
+ ]
1893
+ }
1894
+ );
1895
+ }
1896
+ function TwoFactorDisableButton({
1897
+ onDisable,
1898
+ disabled = false,
1899
+ variant = "destructive",
1900
+ size = "default",
1901
+ labels,
1902
+ className,
1903
+ slotName = "two-factor-disable"
1904
+ }) {
1905
+ const text = resolveLabels(labels);
1906
+ const [open, setOpen] = useState(false);
1907
+ const [processing, setProcessing] = useState(false);
1908
+ const handleConfirm = async () => {
1909
+ setProcessing(true);
1910
+ try {
1911
+ await onDisable();
1912
+ } finally {
1913
+ setProcessing(false);
1914
+ }
1915
+ };
1916
+ return /* @__PURE__ */ jsxs(
1917
+ "span",
1918
+ {
1919
+ "data-open": open || void 0,
1920
+ className: cn("inline-flex", className),
1921
+ "data-slot": slotName,
1922
+ children: [
1923
+ /* @__PURE__ */ jsxs(
1924
+ Button,
1925
+ {
1926
+ type: "button",
1927
+ variant,
1928
+ size,
1929
+ disabled: disabled || processing,
1930
+ onClick: () => setOpen(true),
1931
+ children: [
1932
+ /* @__PURE__ */ jsx(ShieldOff, {}),
1933
+ text.disableLabel
1934
+ ]
1935
+ }
1936
+ ),
1937
+ /* @__PURE__ */ jsx(
1938
+ ConfirmDialog,
1939
+ {
1940
+ open,
1941
+ onOpenChange: setOpen,
1942
+ title: text.disableTitle,
1943
+ description: text.disableDescription,
1944
+ confirmText: text.disableConfirmLabel,
1945
+ cancelText: text.disableCancelLabel,
1946
+ processing,
1947
+ onConfirm: handleConfirm
1948
+ }
1949
+ )
1950
+ ]
1951
+ }
1952
+ );
1953
+ }
1954
+ function TwoFactorRecoveryCodes({
1955
+ codes,
1956
+ defaultRevealed = false,
1957
+ onRegenerate,
1958
+ showHeading = true,
1959
+ labels,
1960
+ className,
1961
+ slotName = "two-factor-recovery-codes"
1962
+ }) {
1963
+ const text = resolveLabels(labels);
1964
+ const [revealed, setRevealed] = useState(defaultRevealed);
1965
+ const [copyFailed, setCopyFailed] = useState(false);
1966
+ const [regenerating, setRegenerating] = useState(false);
1967
+ const handleRegenerate = async () => {
1968
+ if (!onRegenerate || regenerating) {
1969
+ return;
1970
+ }
1971
+ setRegenerating(true);
1972
+ try {
1973
+ await onRegenerate();
1974
+ } finally {
1975
+ setRegenerating(false);
1976
+ }
1977
+ };
1978
+ return /* @__PURE__ */ jsxs(
1979
+ "div",
1980
+ {
1981
+ "data-revealed": revealed || void 0,
1982
+ className: cn("gap-4 flex w-full flex-col", className),
1983
+ "data-slot": slotName,
1984
+ children: [
1985
+ showHeading && /* @__PURE__ */ jsxs("div", { className: "gap-1 flex flex-col", children: [
1986
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: text.recoveryTitle }),
1987
+ /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: [
1988
+ text.recoveryDescription,
1989
+ " ",
1990
+ text.recoveryWarning
1991
+ ] })
1992
+ ] }),
1993
+ revealed && /* @__PURE__ */ jsx(
1994
+ "ul",
1995
+ {
1996
+ "data-slot": "two-factor-recovery-list",
1997
+ className: cn(
1998
+ recessedSurface,
1999
+ "gap-2 p-4 sm:grid-cols-2 grid"
2000
+ ),
2001
+ children: codes.map((code) => /* @__PURE__ */ jsx(
2002
+ "li",
2003
+ {
2004
+ "data-slot": "two-factor-recovery-code",
2005
+ className: cn(
2006
+ compactRadius,
2007
+ "px-2 py-1 text-sm font-medium font-mono break-all text-foreground"
2008
+ ),
2009
+ children: code
2010
+ },
2011
+ code
2012
+ ))
2013
+ }
2014
+ ),
2015
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-wrap items-center", children: [
2016
+ /* @__PURE__ */ jsxs(
2017
+ Button,
2018
+ {
2019
+ type: "button",
2020
+ variant: "ghost",
2021
+ size: "sm",
2022
+ "aria-pressed": revealed,
2023
+ onClick: () => setRevealed(!revealed),
2024
+ children: [
2025
+ revealed ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {}),
2026
+ revealed ? text.hideLabel : text.revealLabel
2027
+ ]
2028
+ }
2029
+ ),
2030
+ /* @__PURE__ */ jsx(
2031
+ CopyButton,
2032
+ {
2033
+ value: codes.join("\n"),
2034
+ copyLabel: text.copyLabel,
2035
+ copiedLabel: text.copiedLabel,
2036
+ onCopied: () => setCopyFailed(false),
2037
+ onCopyFailed: () => setCopyFailed(true)
2038
+ }
2039
+ ),
2040
+ onRegenerate && /* @__PURE__ */ jsxs(
2041
+ Button,
2042
+ {
2043
+ type: "button",
2044
+ variant: "outline",
2045
+ size: "sm",
2046
+ loading: regenerating,
2047
+ loadingLabel: text.regenerateLabel,
2048
+ onClick: handleRegenerate,
2049
+ children: [
2050
+ /* @__PURE__ */ jsx(RefreshCw, {}),
2051
+ text.regenerateLabel
2052
+ ]
2053
+ }
2054
+ )
2055
+ ] }),
2056
+ copyFailed && /* @__PURE__ */ jsx(
2057
+ "p",
2058
+ {
2059
+ "data-slot": "two-factor-copy-failed",
2060
+ className: "text-xs font-medium text-muted-foreground",
2061
+ children: text.copyFailedLabel
2062
+ }
2063
+ )
2064
+ ]
2065
+ }
2066
+ );
2067
+ }
2068
+ function TwoFactorScanStep({
2069
+ qrCode,
2070
+ qrCodeSvg,
2071
+ manualSetupKey,
2072
+ labels,
2073
+ className,
2074
+ slotName = "two-factor-scan-step"
2075
+ }) {
2076
+ const text = resolveLabels(labels);
2077
+ const [revealed, setRevealed] = useState(false);
2078
+ return /* @__PURE__ */ jsxs(
2079
+ "div",
2080
+ {
2081
+ className: cn("gap-5 flex w-full flex-col", className),
2082
+ "data-slot": slotName,
2083
+ children: [
2084
+ /* @__PURE__ */ jsx(
2085
+ "div",
2086
+ {
2087
+ "data-slot": "two-factor-qr",
2088
+ className: cn(
2089
+ recessedSurface,
2090
+ "p-4 flex items-center justify-center [&_svg]:size-full [&_svg]:h-auto"
2091
+ ),
2092
+ children: qrCode ?? (qrCodeSvg ? /* @__PURE__ */ jsx(
2093
+ "div",
2094
+ {
2095
+ "data-slot": "two-factor-qr-markup",
2096
+ className: cn(controlRadius, "p-3 bg-card"),
2097
+ dangerouslySetInnerHTML: { __html: qrCodeSvg }
2098
+ }
2099
+ ) : /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: text.qrFallbackLabel }))
2100
+ }
2101
+ ),
2102
+ manualSetupKey && /* @__PURE__ */ jsxs(
2103
+ "div",
2104
+ {
2105
+ "data-slot": "two-factor-setup-key",
2106
+ className: "gap-2 flex flex-col",
2107
+ children: [
2108
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: text.manualKeyLabel }),
2109
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: text.manualKeyDescription }),
2110
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-wrap items-center", children: [
2111
+ /* @__PURE__ */ jsx(
2112
+ "code",
2113
+ {
2114
+ "data-slot": "two-factor-setup-key-value",
2115
+ className: cn(
2116
+ compactRadius,
2117
+ "px-3 py-2 text-sm font-medium font-mono bg-muted break-all text-foreground"
2118
+ ),
2119
+ children: revealed ? manualSetupKey : "\u2022".repeat(manualSetupKey.length)
2120
+ }
2121
+ ),
2122
+ /* @__PURE__ */ jsxs(
2123
+ Button,
2124
+ {
2125
+ type: "button",
2126
+ variant: "ghost",
2127
+ size: "sm",
2128
+ "aria-pressed": revealed,
2129
+ onClick: () => setRevealed(!revealed),
2130
+ children: [
2131
+ revealed ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {}),
2132
+ revealed ? text.manualKeyHideLabel : text.manualKeyRevealLabel
2133
+ ]
2134
+ }
2135
+ ),
2136
+ /* @__PURE__ */ jsx(
2137
+ CopyButton,
2138
+ {
2139
+ value: manualSetupKey,
2140
+ copyLabel: text.copyLabel,
2141
+ copiedLabel: text.copiedLabel
2142
+ }
2143
+ )
2144
+ ] })
2145
+ ]
2146
+ }
2147
+ )
2148
+ ]
2149
+ }
2150
+ );
2151
+ }
2152
+ function TwoFactorSetupDialog({
2153
+ open,
2154
+ onOpenChange,
2155
+ enabled = false,
2156
+ qrCode,
2157
+ qrCodeSvg,
2158
+ manualSetupKey,
2159
+ recoveryCodes,
2160
+ errors,
2161
+ onConfirm,
2162
+ onRequestSetupData,
2163
+ onRegenerateRecoveryCodes,
2164
+ onCompleted,
2165
+ labels,
2166
+ className,
2167
+ slotName = "two-factor-setup-dialog"
2168
+ }) {
2169
+ const text = resolveLabels(labels);
2170
+ const [step, setStep] = useState(
2171
+ enabled ? "recovery" : "scan"
2172
+ );
2173
+ const requestSetupData = useRef(onRequestSetupData);
2174
+ const openedBefore = useRef(false);
2175
+ useEffect(() => {
2176
+ requestSetupData.current = onRequestSetupData;
2177
+ }, [onRequestSetupData]);
2178
+ useEffect(() => {
2179
+ if (!open) {
2180
+ openedBefore.current = false;
2181
+ return;
2182
+ }
2183
+ if (openedBefore.current) {
2184
+ return;
2185
+ }
2186
+ openedBefore.current = true;
2187
+ setStep(enabled ? "recovery" : "scan");
2188
+ if (!enabled) {
2189
+ void requestSetupData.current?.();
2190
+ }
2191
+ }, [open, enabled]);
2192
+ const ready = Boolean(qrCode || qrCodeSvg || manualSetupKey);
2193
+ const codes = recoveryCodes ?? [];
2194
+ const handleConfirm = async (code) => {
2195
+ await onConfirm(code);
2196
+ setStep("recovery");
2197
+ };
2198
+ const finish = () => {
2199
+ onOpenChange(false);
2200
+ onCompleted?.();
2201
+ };
2202
+ const heading = {
2203
+ pending: text.setupTitle,
2204
+ scan: text.scanTitle,
2205
+ confirm: text.confirmTitle,
2206
+ recovery: text.recoveryTitle
2207
+ }[step];
2208
+ const description = {
2209
+ pending: text.setupDescription,
2210
+ scan: text.scanDescription,
2211
+ confirm: text.confirmDescription,
2212
+ recovery: `${text.recoveryDescription} ${text.recoveryWarning}`
2213
+ }[step];
2214
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2215
+ DialogContent,
2216
+ {
2217
+ "data-step": step,
2218
+ className: cn("max-w-md p-0", className),
2219
+ slotName,
2220
+ children: [
2221
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "p-6 md:p-8", children: [
2222
+ /* @__PURE__ */ jsx(DialogTitle, { children: heading }),
2223
+ /* @__PURE__ */ jsx(DialogDescription, { children: description })
2224
+ ] }),
2225
+ /* @__PURE__ */ jsxs("div", { className: "gap-5 p-6 md:p-8 pt-0 md:pt-0 flex flex-col", children: [
2226
+ step === "scan" && (ready ? /* @__PURE__ */ jsxs(Fragment, { children: [
2227
+ /* @__PURE__ */ jsx(
2228
+ TwoFactorScanStep,
2229
+ {
2230
+ qrCode,
2231
+ qrCodeSvg,
2232
+ manualSetupKey,
2233
+ labels
2234
+ }
2235
+ ),
2236
+ /* @__PURE__ */ jsx(
2237
+ Button,
2238
+ {
2239
+ type: "button",
2240
+ onClick: () => setStep("confirm"),
2241
+ children: text.continueLabel
2242
+ }
2243
+ )
2244
+ ] }) : /* @__PURE__ */ jsxs(
2245
+ "div",
2246
+ {
2247
+ "data-slot": "two-factor-pending",
2248
+ className: "gap-3 py-6 text-sm font-medium flex items-center justify-center text-muted-foreground",
2249
+ children: [
2250
+ /* @__PURE__ */ jsx(Spinner, { label: text.pendingLabel }),
2251
+ text.pendingLabel
2252
+ ]
2253
+ }
2254
+ )),
2255
+ step === "confirm" && /* @__PURE__ */ jsx(
2256
+ TwoFactorVerifyForm,
2257
+ {
2258
+ autoFocus: true,
2259
+ errors,
2260
+ labels,
2261
+ onSubmit: handleConfirm,
2262
+ footer: /* @__PURE__ */ jsx(
2263
+ Button,
2264
+ {
2265
+ type: "button",
2266
+ variant: "ghost",
2267
+ size: "sm",
2268
+ onClick: () => setStep("scan"),
2269
+ children: text.cancelLabel
2270
+ }
2271
+ )
2272
+ }
2273
+ ),
2274
+ step === "recovery" && /* @__PURE__ */ jsxs(Fragment, { children: [
2275
+ /* @__PURE__ */ jsx(
2276
+ TwoFactorRecoveryCodes,
2277
+ {
2278
+ codes,
2279
+ defaultRevealed: true,
2280
+ showHeading: false,
2281
+ labels,
2282
+ onRegenerate: onRegenerateRecoveryCodes
2283
+ }
2284
+ ),
2285
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: finish, children: text.doneLabel })
2286
+ ] })
2287
+ ] })
2288
+ ]
2289
+ }
2290
+ ) });
2291
+ }
2292
+
2293
+ export { CommandPalette, DEFAULT_LABELS as DATE_FILTER_LABELS, DEFAULT_OPERATORS as DATE_FILTER_OPERATORS, DEFAULT_PRESETS as DATE_FILTER_PRESETS, DEFAULT_UNITS as DATE_FILTER_UNITS, DangerZone, DateField, DateFilter, DateFilterAll, DateFilterContent, DateFilterFixed, DateFilterItem, DateFilterPresets, DateFilterRelative, DateFilterSeparator, DateFilterTrigger, DetailEditSheet, FormDialog, FormOverlayActions, InfoField, InfoFieldGroup, LocalizedFields, NumberField, SectionHeader, SelectField, SettingsCard, SettingsEntry, SettingsField, SettingsGroup, SettingsPage, SettingsPanel, SettingsSection, StatCard, StatsGrid, TextField, ToggleRow, TwoFactorChallenge, TwoFactorDisableButton, TwoFactorRecoveryCodes, TwoFactorScanStep, TwoFactorSetupDialog, TwoFactorVerifyForm, dangerZoneLabels, decodeDateFilter, encodeDateFilter, formOverlayDefaultLabels, formatRangePreview, resolveRelativeRange, settingsLabels, summariseDateFilter, twoFactorLabels, useCommandPalette, useDateFilter };
920
2294
  //# sourceMappingURL=blocks.js.map
921
2295
  //# sourceMappingURL=blocks.js.map