@kyro-cms/admin 0.9.1 → 0.9.3

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 (37) hide show
  1. package/dist/index.cjs +1196 -1727
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +4 -3
  4. package/dist/index.d.ts +4 -3
  5. package/dist/index.js +891 -1422
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/components/ActionBar.tsx +25 -174
  9. package/src/components/Admin.tsx +1 -3
  10. package/src/components/AuditLogsPage.tsx +2 -13
  11. package/src/components/AutoForm.tsx +160 -265
  12. package/src/components/DetailView.tsx +38 -66
  13. package/src/components/FieldRenderer.tsx +1 -1
  14. package/src/components/ListView.tsx +26 -198
  15. package/src/components/MediaGallery.tsx +117 -175
  16. package/src/components/RestPlayground.tsx +54 -47
  17. package/src/components/fields/BlocksField.tsx +8 -10
  18. package/src/components/fields/RelationshipBlockField.tsx +2 -3
  19. package/src/components/fields/RelationshipField.tsx +2 -3
  20. package/src/components/fix_imports.cjs +23 -0
  21. package/src/components/fix_imports2.cjs +19 -0
  22. package/src/components/replace_svgs.cjs +63 -0
  23. package/src/components/ui/Dropdown.tsx +7 -2
  24. package/src/components/ui/Modal.tsx +24 -27
  25. package/src/components/ui/PromptModal.tsx +2 -10
  26. package/src/components/ui/SlidePanel.tsx +2 -10
  27. package/src/components/ui/SplitButton.tsx +107 -0
  28. package/src/components/ui/Toaster.tsx +0 -1
  29. package/src/components/ui/icons.tsx +1 -0
  30. package/src/components/users/UsersList.tsx +8 -85
  31. package/src/hooks/useAutoFormState.ts +89 -161
  32. package/src/hooks/useQueue.ts +60 -0
  33. package/src/layouts/AdminLayout.astro +22 -2
  34. package/src/layouts/AuthLayout.astro +66 -18
  35. package/src/lib/autoform-store.ts +6 -2
  36. package/src/lib/globals.ts +5 -3
  37. package/src/pages/auth/register.astro +5 -1
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var React54 = require('react');
3
+ var React56 = require('react');
4
4
  var zustand = require('zustand');
5
5
  var middleware = require('zustand/middleware');
6
- var jsxRuntime = require('react/jsx-runtime');
7
6
  var lucideReact = require('lucide-react');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
8
  var reactDom = require('react-dom');
9
9
  var react = require('@tiptap/react');
10
10
  var StarterKit = require('@tiptap/starter-kit');
@@ -41,7 +41,7 @@ var worker_threads = require('worker_threads');
41
41
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
42
42
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
43
43
 
44
- var React54__default = /*#__PURE__*/_interopDefault(React54);
44
+ var React56__default = /*#__PURE__*/_interopDefault(React56);
45
45
  var StarterKit__default = /*#__PURE__*/_interopDefault(StarterKit);
46
46
  var Link4__default = /*#__PURE__*/_interopDefault(Link4);
47
47
  var Image3__default = /*#__PURE__*/_interopDefault(Image3);
@@ -486,7 +486,7 @@ function PageHeader({
486
486
  children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 12H5M12 19l-7-7 7-7" }) })
487
487
  }
488
488
  ),
489
- breadcrumbs?.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React54__default.default.Fragment, { children: [
489
+ breadcrumbs?.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React56__default.default.Fragment, { children: [
490
490
  i > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-20 text-[10px]", children: "/" }),
491
491
  crumb.href || crumb.onClick ? /* @__PURE__ */ jsxRuntime.jsx(
492
492
  "a",
@@ -510,7 +510,7 @@ function PageHeader({
510
510
  ] }),
511
511
  description && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
512
512
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[var(--kyro-text-secondary)] font-medium opacity-60 line-clamp-1", children: description }),
513
- metadata && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: metadata.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(React54__default.default.Fragment, { children: [
513
+ metadata && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: metadata.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(React56__default.default.Fragment, { children: [
514
514
  i === 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-20 ml-1", children: "\xB7" }),
515
515
  item
516
516
  ] }, i)) }),
@@ -576,6 +576,89 @@ function CountBadge({ count, max = 99 }) {
576
576
  if (count === 0) return null;
577
577
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 text-xs font-medium bg-gray-200 text-gray-700 rounded-full", children: count > max ? `${max}+` : count });
578
578
  }
579
+ function Button({
580
+ variant = "secondary",
581
+ size = "md",
582
+ loading = false,
583
+ children,
584
+ className = "",
585
+ disabled,
586
+ ...props
587
+ }) {
588
+ const baseClass = "kyro-btn";
589
+ const variantClass = `kyro-btn-${variant}`;
590
+ const sizeClass = `kyro-btn-${size}`;
591
+ return /* @__PURE__ */ jsxRuntime.jsxs(
592
+ "button",
593
+ {
594
+ type: "button",
595
+ className: `${baseClass} ${variantClass} ${sizeClass} ${className}`,
596
+ disabled: disabled || loading,
597
+ ...props,
598
+ children: [
599
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "kyro-btn-loading", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "kyro-btn-spinner", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", strokeDasharray: "60 30" }) }) }) : null,
600
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: loading ? "kyro-btn-text-hidden" : "", children })
601
+ ]
602
+ }
603
+ );
604
+ }
605
+ function Pagination({ page, totalPages, totalDocs, limit, onPageChange, onLimitChange }) {
606
+ if (totalPages <= 1) return null;
607
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-t border-[var(--kyro-border)]", children: [
608
+ totalDocs !== void 0 && limit ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-[var(--kyro-text-secondary)] font-medium", children: [
609
+ "Showing ",
610
+ (page - 1) * limit + 1,
611
+ " to ",
612
+ Math.min(page * limit, totalDocs),
613
+ " of ",
614
+ totalDocs
615
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
616
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
617
+ onLimitChange && /* @__PURE__ */ jsxRuntime.jsxs(
618
+ "select",
619
+ {
620
+ value: limit,
621
+ onChange: (e) => onLimitChange(Number(e.target.value)),
622
+ className: "text-xs border border-[var(--kyro-border)] rounded-lg px-2 py-1 bg-[var(--kyro-bg)] text-[var(--kyro-text-secondary)]",
623
+ children: [
624
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: 10, children: "10/page" }),
625
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: 25, children: "25/page" }),
626
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: 50, children: "50/page" }),
627
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: 100, children: "100/page" })
628
+ ]
629
+ }
630
+ ),
631
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-[var(--kyro-text-secondary)] font-medium", children: [
632
+ "Page ",
633
+ page,
634
+ " of ",
635
+ totalPages
636
+ ] }),
637
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1", children: [
638
+ /* @__PURE__ */ jsxRuntime.jsx(
639
+ Button,
640
+ {
641
+ variant: "ghost",
642
+ size: "sm",
643
+ disabled: page <= 1,
644
+ onClick: () => onPageChange(page - 1),
645
+ children: "\u2190 Previous"
646
+ }
647
+ ),
648
+ /* @__PURE__ */ jsxRuntime.jsx(
649
+ Button,
650
+ {
651
+ variant: "ghost",
652
+ size: "sm",
653
+ disabled: page >= totalPages,
654
+ onClick: () => onPageChange(page + 1),
655
+ children: "Next \u2192"
656
+ }
657
+ )
658
+ ] })
659
+ ] })
660
+ ] });
661
+ }
579
662
 
580
663
  // src/lib/resolve-field-value.ts
581
664
  function resolveFieldValue(fields2, formData, fieldName) {
@@ -647,14 +730,14 @@ function ListView({
647
730
  window.location.href = `${adminPath}/${collectionSlug}/${id}`;
648
731
  }
649
732
  };
650
- const [docs, setDocs] = React54.useState(initialDocs);
651
- const [totalDocs, setTotalDocs] = React54.useState(initialTotal);
652
- const [loading, setLoading] = React54.useState(false);
653
- const [page, setPage] = React54.useState(1);
654
- const [limit, setLimit] = React54.useState(10);
655
- const [selectedIds, setSelectedIds] = React54.useState(/* @__PURE__ */ new Set());
656
- const [search, setSearch] = React54.useState("");
657
- const [filters, setFilters] = React54.useState([]);
733
+ const [docs, setDocs] = React56.useState(initialDocs);
734
+ const [totalDocs, setTotalDocs] = React56.useState(initialTotal);
735
+ const [loading, setLoading] = React56.useState(false);
736
+ const [page, setPage] = React56.useState(1);
737
+ const [limit, setLimit] = React56.useState(10);
738
+ const [selectedIds, setSelectedIds] = React56.useState(/* @__PURE__ */ new Set());
739
+ const [search, setSearch] = React56.useState("");
740
+ const [filters, setFilters] = React56.useState([]);
658
741
  const { confirm, alert } = useUIStore();
659
742
  const addFilter = () => {
660
743
  setFilters([...filters, { field: "", operator: "equals", value: "" }]);
@@ -670,9 +753,9 @@ function ListView({
670
753
  const updateFilter = (index, updates) => {
671
754
  setFilters(filters.map((f, i) => i === index ? { ...f, ...updates } : f));
672
755
  };
673
- const [sort, setSort] = React54.useState(null);
674
- const [showFilters, setShowFilters] = React54.useState(false);
675
- const [showColumns, setShowColumns] = React54.useState(false);
756
+ const [sort, setSort] = React56.useState(null);
757
+ const [showFilters, setShowFilters] = React56.useState(false);
758
+ const [showColumns, setShowColumns] = React56.useState(false);
676
759
  function flattenFields(fields2) {
677
760
  const result = [];
678
761
  for (const field3 of fields2 || []) {
@@ -691,12 +774,12 @@ function ListView({
691
774
  }
692
775
  return result;
693
776
  }
694
- const allFields = React54.useMemo(
777
+ const allFields = React56.useMemo(
695
778
  () => flattenFields(collection.fields),
696
779
  [collection.fields]
697
780
  );
698
781
  const titleField = typeof collection.admin?.useAsTitle === "string" ? collection.admin.useAsTitle : allFields.find((f) => f.type !== "group" && typeof f.name === "string")?.name;
699
- const [visibleColumns, setVisibleColumns] = React54.useState(() => {
782
+ const [visibleColumns, setVisibleColumns] = React56.useState(() => {
700
783
  let cols;
701
784
  if (collection.admin?.defaultColumns) {
702
785
  cols = [...collection.admin?.defaultColumns || []];
@@ -711,7 +794,7 @@ function ListView({
711
794
  }
712
795
  return new Set(cols);
713
796
  });
714
- const toggleColumn = React54.useCallback((fieldName) => {
797
+ const toggleColumn = React56.useCallback((fieldName) => {
715
798
  setVisibleColumns((prev) => {
716
799
  const next = new Set(prev);
717
800
  if (next.has(fieldName)) {
@@ -730,7 +813,7 @@ function ListView({
730
813
  }
731
814
  return fieldName;
732
815
  }
733
- const handleSort = React54.useCallback((fieldName) => {
816
+ const handleSort = React56.useCallback((fieldName) => {
734
817
  const resolvedField = resolveSortField(fieldName);
735
818
  setSort((prev) => {
736
819
  if (prev && prev.field === resolvedField) {
@@ -742,12 +825,12 @@ function ListView({
742
825
  return { field: resolvedField, direction: "asc" };
743
826
  });
744
827
  }, []);
745
- const displayFields = React54.useMemo(
828
+ const displayFields = React56.useMemo(
746
829
  () => {
747
830
  const fields2 = allFields.filter((f) => !!f.name && visibleColumns.has(f.name));
748
- if (visibleColumns.has("publishStatus")) {
831
+ if (visibleColumns.has("status")) {
749
832
  fields2.push({
750
- name: "publishStatus",
833
+ name: "status",
751
834
  type: "select",
752
835
  label: "Status",
753
836
  options: [
@@ -772,7 +855,7 @@ function ListView({
772
855
  const val = resolveFieldValue(collection.fields, doc, field3.name);
773
856
  return val ?? null;
774
857
  }
775
- const fetchDocs = React54.useCallback(async () => {
858
+ const fetchDocs = React56.useCallback(async () => {
776
859
  setLoading(true);
777
860
  try {
778
861
  const params = new URLSearchParams({
@@ -797,13 +880,13 @@ function ListView({
797
880
  setLoading(false);
798
881
  }
799
882
  }, [collectionSlug, page, limit, search, sort, filters]);
800
- React54.useEffect(() => {
883
+ React56.useEffect(() => {
801
884
  if (docs.length === 0 && initialTotal === 0) {
802
885
  fetchDocs();
803
886
  }
804
887
  }, []);
805
- const isFirstRender = React54.useRef(true);
806
- React54.useEffect(() => {
888
+ const isFirstRender = React56.useRef(true);
889
+ React56.useEffect(() => {
807
890
  if (isFirstRender.current) {
808
891
  isFirstRender.current = false;
809
892
  return;
@@ -886,24 +969,7 @@ function ListView({
886
969
  ),
887
970
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "surface-tile p-4 flex flex-col lg:flex-row gap-4 items-start lg:items-center", children: [
888
971
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 max-w-md", children: [
889
- /* @__PURE__ */ jsxRuntime.jsx(
890
- "svg",
891
- {
892
- className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--kyro-text-secondary)]",
893
- fill: "none",
894
- stroke: "currentColor",
895
- viewBox: "0 0 24 24",
896
- children: /* @__PURE__ */ jsxRuntime.jsx(
897
- "path",
898
- {
899
- strokeLinecap: "round",
900
- strokeLinejoin: "round",
901
- strokeWidth: "2",
902
- d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
903
- }
904
- )
905
- }
906
- ),
972
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "w-4 h-4" }),
907
973
  /* @__PURE__ */ jsxRuntime.jsx(
908
974
  "input",
909
975
  {
@@ -923,24 +989,7 @@ function ListView({
923
989
  onClick: () => setShowFilters(!showFilters),
924
990
  className: `flex items-center gap-2 px-4 py-2 rounded-xl font-bold text-sm transition-all ${showFilters || filters.length > 0 ? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)]" : "bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)]"}`,
925
991
  children: [
926
- /* @__PURE__ */ jsxRuntime.jsx(
927
- "svg",
928
- {
929
- className: "w-4 h-4",
930
- fill: "none",
931
- stroke: "currentColor",
932
- viewBox: "0 0 24 24",
933
- children: /* @__PURE__ */ jsxRuntime.jsx(
934
- "path",
935
- {
936
- strokeLinecap: "round",
937
- strokeLinejoin: "round",
938
- strokeWidth: "2",
939
- d: "M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
940
- }
941
- )
942
- }
943
- ),
992
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: "w-4 h-4" }),
944
993
  "Filters",
945
994
  filters.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 px-1.5 py-0.5 bg-[var(--kyro-sidebar-text-active)] text-[var(--kyro-sidebar-active)] rounded-full text-xs", children: filters.length })
946
995
  ]
@@ -954,24 +1003,7 @@ function ListView({
954
1003
  onClick: () => setShowColumns(!showColumns),
955
1004
  className: "flex items-center gap-2 px-4 py-2 rounded-xl font-bold text-sm bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] transition-all",
956
1005
  children: [
957
- /* @__PURE__ */ jsxRuntime.jsx(
958
- "svg",
959
- {
960
- className: "w-4 h-4",
961
- fill: "none",
962
- stroke: "currentColor",
963
- viewBox: "0 0 24 24",
964
- children: /* @__PURE__ */ jsxRuntime.jsx(
965
- "path",
966
- {
967
- strokeLinecap: "round",
968
- strokeLinejoin: "round",
969
- strokeWidth: "2",
970
- d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"
971
- }
972
- )
973
- }
974
- ),
1006
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Columns3, { className: "w-4 h-4" }),
975
1007
  "Columns"
976
1008
  ]
977
1009
  }
@@ -1023,24 +1055,7 @@ function ListView({
1023
1055
  onClick: addFilter,
1024
1056
  className: "flex items-center gap-2 px-3 py-1.5 text-sm font-bold text-[var(--kyro-sidebar-active)] hover:bg-[var(--kyro-surface-accent)] rounded-lg transition-all",
1025
1057
  children: [
1026
- /* @__PURE__ */ jsxRuntime.jsx(
1027
- "svg",
1028
- {
1029
- className: "w-4 h-4",
1030
- fill: "none",
1031
- stroke: "currentColor",
1032
- viewBox: "0 0 24 24",
1033
- children: /* @__PURE__ */ jsxRuntime.jsx(
1034
- "path",
1035
- {
1036
- strokeLinecap: "round",
1037
- strokeLinejoin: "round",
1038
- strokeWidth: "2",
1039
- d: "M12 5v14M5 12h14"
1040
- }
1041
- )
1042
- }
1043
- ),
1058
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-4 h-4" }),
1044
1059
  "Add Filter"
1045
1060
  ]
1046
1061
  }
@@ -1091,24 +1106,7 @@ function ListView({
1091
1106
  type: "button",
1092
1107
  onClick: () => removeFilter(index),
1093
1108
  className: "p-2 text-[var(--kyro-text-muted)] hover:text-red-500 transition-colors",
1094
- children: /* @__PURE__ */ jsxRuntime.jsx(
1095
- "svg",
1096
- {
1097
- className: "w-4 h-4",
1098
- fill: "none",
1099
- stroke: "currentColor",
1100
- viewBox: "0 0 24 24",
1101
- children: /* @__PURE__ */ jsxRuntime.jsx(
1102
- "path",
1103
- {
1104
- strokeLinecap: "round",
1105
- strokeLinejoin: "round",
1106
- strokeWidth: "2",
1107
- d: "M6 18L18 6M6 6l12 12"
1108
- }
1109
- )
1110
- }
1111
- )
1109
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
1112
1110
  }
1113
1111
  )
1114
1112
  ] }, index)),
@@ -1128,24 +1126,7 @@ function ListView({
1128
1126
  onClick: handleBulkDelete,
1129
1127
  className: "flex items-center gap-2 px-4 py-2 bg-red-500 text-white rounded-lg font-bold text-sm hover:bg-red-600 transition-all",
1130
1128
  children: [
1131
- /* @__PURE__ */ jsxRuntime.jsx(
1132
- "svg",
1133
- {
1134
- className: "w-4 h-4",
1135
- fill: "none",
1136
- stroke: "currentColor",
1137
- viewBox: "0 0 24 24",
1138
- children: /* @__PURE__ */ jsxRuntime.jsx(
1139
- "path",
1140
- {
1141
- strokeLinecap: "round",
1142
- strokeLinejoin: "round",
1143
- strokeWidth: "2",
1144
- d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
1145
- }
1146
- )
1147
- }
1148
- ),
1129
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-4 h-4" }),
1149
1130
  "Delete Selected"
1150
1131
  ]
1151
1132
  }
@@ -1162,24 +1143,7 @@ function ListView({
1162
1143
  ] })
1163
1144
  ] }),
1164
1145
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "surface-tile overflow-hidden", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { variant: "table-row", count: 8 }) }) : docs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-8", children: [
1165
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(
1166
- "svg",
1167
- {
1168
- className: "w-8 h-8 text-[var(--kyro-text-muted)]",
1169
- fill: "none",
1170
- stroke: "currentColor",
1171
- viewBox: "0 0 24 24",
1172
- children: /* @__PURE__ */ jsxRuntime.jsx(
1173
- "path",
1174
- {
1175
- strokeLinecap: "round",
1176
- strokeLinejoin: "round",
1177
- strokeWidth: "1.5",
1178
- d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
1179
- }
1180
- )
1181
- }
1182
- ) }),
1146
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Archive, { className: "w-4 h-4" }) }),
1183
1147
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-[var(--kyro-text-primary)] text-base", children: "No documents found" }),
1184
1148
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[var(--kyro-text-secondary)] mt-1", children: hasActiveFilters ? "Try adjusting your filters or search query." : `Get started by creating your first ${(collection.singularLabel || collection.label || collectionSlug).toLowerCase()}.` }),
1185
1149
  !hasActiveFilters && canCreate && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1189,24 +1153,7 @@ function ListView({
1189
1153
  onClick: handleCreate,
1190
1154
  className: "mt-4 kyro-btn kyro-btn-md kyro-btn-primary shadow-md flex items-center gap-2",
1191
1155
  children: [
1192
- /* @__PURE__ */ jsxRuntime.jsx(
1193
- "svg",
1194
- {
1195
- className: "w-3.5 h-3.5",
1196
- fill: "none",
1197
- stroke: "currentColor",
1198
- viewBox: "0 0 24 24",
1199
- children: /* @__PURE__ */ jsxRuntime.jsx(
1200
- "path",
1201
- {
1202
- strokeLinecap: "round",
1203
- strokeLinejoin: "round",
1204
- strokeWidth: "2.5",
1205
- d: "M12 5v14M5 12h14"
1206
- }
1207
- )
1208
- }
1209
- ),
1156
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-4 h-4" }),
1210
1157
  "Create",
1211
1158
  " ",
1212
1159
  String(collection.singularLabel || collection.label || collectionSlug)
@@ -1231,24 +1178,7 @@ function ListView({
1231
1178
  onClick: () => handleSort(field3.name),
1232
1179
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1233
1180
  checkTabbedValue(displayFields, field3.type) ?? (field3.label || field3.name),
1234
- sort && sort.field === field3.name && /* @__PURE__ */ jsxRuntime.jsx(
1235
- "svg",
1236
- {
1237
- className: `w-3 h-3 ${sort.direction === "desc" ? "rotate-180" : ""}`,
1238
- fill: "none",
1239
- stroke: "currentColor",
1240
- viewBox: "0 0 24 24",
1241
- children: /* @__PURE__ */ jsxRuntime.jsx(
1242
- "path",
1243
- {
1244
- strokeLinecap: "round",
1245
- strokeLinejoin: "round",
1246
- strokeWidth: "2",
1247
- d: "M5 15l7-7 7 7"
1248
- }
1249
- )
1250
- }
1251
- )
1181
+ sort && sort.field === field3.name && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "w-4 h-4" })
1252
1182
  ] })
1253
1183
  },
1254
1184
  field3.name
@@ -1311,24 +1241,7 @@ function ListView({
1311
1241
  onClick: () => handleEdit(doc.id),
1312
1242
  className: "flex items-center gap-2 px-3 py-1.5 hover:bg-[var(--kyro-surface-accent)] rounded-lg text-sm font-bold text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] transition-all",
1313
1243
  title: canUpdate ? "Edit" : "View",
1314
- children: /* @__PURE__ */ jsxRuntime.jsx(
1315
- "svg",
1316
- {
1317
- className: "w-4 h-4",
1318
- fill: "none",
1319
- stroke: "currentColor",
1320
- viewBox: "0 0 24 24",
1321
- children: /* @__PURE__ */ jsxRuntime.jsx(
1322
- "path",
1323
- {
1324
- strokeLinecap: "round",
1325
- strokeLinejoin: "round",
1326
- strokeWidth: "2",
1327
- d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
1328
- }
1329
- )
1330
- }
1331
- )
1244
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "w-4 h-4" })
1332
1245
  }
1333
1246
  ),
1334
1247
  canDelete && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1338,24 +1251,7 @@ function ListView({
1338
1251
  onClick: () => handleDeleteSingle(doc.id),
1339
1252
  className: "inline-flex items-center justify-center w-8 h-8 rounded-md text-[var(--kyro-text-muted)] hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-500/10 transition-colors",
1340
1253
  title: "Delete",
1341
- children: /* @__PURE__ */ jsxRuntime.jsx(
1342
- "svg",
1343
- {
1344
- className: "w-4 h-4",
1345
- fill: "none",
1346
- stroke: "currentColor",
1347
- viewBox: "0 0 24 24",
1348
- children: /* @__PURE__ */ jsxRuntime.jsx(
1349
- "path",
1350
- {
1351
- strokeLinecap: "round",
1352
- strokeLinejoin: "round",
1353
- strokeWidth: "2",
1354
- d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
1355
- }
1356
- )
1357
- }
1358
- )
1254
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-4 h-4" })
1359
1255
  }
1360
1256
  )
1361
1257
  ] })
@@ -1366,60 +1262,20 @@ function ListView({
1366
1262
  doc.id
1367
1263
  )) })
1368
1264
  ] }) }) }),
1369
- totalDocs > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col lg:flex-row items-center justify-between gap-4 px-2", children: [
1370
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
1371
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-[var(--kyro-text-secondary)] font-medium", children: [
1372
- "Showing",
1373
- " ",
1374
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--kyro-text-primary)] font-bold", children: (page - 1) * limit + 1 }),
1375
- " ",
1376
- "to",
1377
- " ",
1378
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--kyro-text-primary)] font-bold", children: Math.min(page * limit, totalDocs) }),
1379
- " ",
1380
- "of",
1381
- " ",
1382
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--kyro-text-primary)] font-bold", children: totalDocs })
1383
- ] }),
1384
- /* @__PURE__ */ jsxRuntime.jsxs(
1385
- "select",
1386
- {
1387
- value: limit,
1388
- onChange: (e) => {
1389
- setLimit(Number(e.target.value));
1390
- setPage(1);
1391
- },
1392
- className: "px-2 py-1 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-lg text-sm font-medium text-[var(--kyro-text-primary)]",
1393
- children: [
1394
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: 10, children: "10 / page" }),
1395
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: 25, children: "25 / page" }),
1396
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: 50, children: "50 / page" }),
1397
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: 100, children: "100 / page" })
1398
- ]
1399
- }
1400
- )
1401
- ] }),
1402
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1403
- page > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1404
- "button",
1405
- {
1406
- type: "button",
1407
- onClick: () => setPage(page - 1),
1408
- className: "px-4 py-2 border border-[var(--kyro-border)] rounded-lg text-sm font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] transition-colors",
1409
- children: "\u2190 Previous"
1410
- }
1411
- ),
1412
- page < totalPages && /* @__PURE__ */ jsxRuntime.jsx(
1413
- "button",
1414
- {
1415
- type: "button",
1416
- onClick: () => setPage(page + 1),
1417
- className: "px-4 py-2 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-lg text-sm font-bold hover:opacity-90 transition-all",
1418
- children: "Next \u2192"
1419
- }
1420
- )
1421
- ] })
1422
- ] })
1265
+ /* @__PURE__ */ jsxRuntime.jsx(
1266
+ Pagination,
1267
+ {
1268
+ page,
1269
+ totalPages,
1270
+ totalDocs,
1271
+ limit,
1272
+ onPageChange: setPage,
1273
+ onLimitChange: (newLimit) => {
1274
+ setLimit(newLimit);
1275
+ setPage(1);
1276
+ }
1277
+ }
1278
+ )
1423
1279
  ] });
1424
1280
  }
1425
1281
  function formatCellValue(value, type) {
@@ -1493,26 +1349,26 @@ function UploadField({
1493
1349
  onChange,
1494
1350
  disabled
1495
1351
  }) {
1496
- const inputRef = React54.useRef(null);
1497
- const urlInputRef = React54.useRef(null);
1498
- const [uploading, setUploading] = React54.useState(false);
1499
- const [showPicker, setShowPicker] = React54.useState(false);
1500
- const [isPickerFullscreen, setIsPickerFullscreen] = React54.useState(false);
1501
- const [mediaItems, setMediaItems] = React54.useState([]);
1502
- const [folders, setFolders] = React54.useState([]);
1503
- const [selectedFolder, setSelectedFolder] = React54.useState("");
1504
- const [mediaLoading, setMediaLoading] = React54.useState(false);
1505
- const [pickerSearch, setPickerSearch] = React54.useState("");
1506
- const [showUrlInput, setShowUrlInput] = React54.useState(false);
1507
- const [urlValue, setUrlValue] = React54.useState("");
1508
- const [urlError, setUrlError] = React54.useState("");
1509
- const [selectedItems, setSelectedItems] = React54.useState([]);
1352
+ const inputRef = React56.useRef(null);
1353
+ const urlInputRef = React56.useRef(null);
1354
+ const [uploading, setUploading] = React56.useState(false);
1355
+ const [showPicker, setShowPicker] = React56.useState(false);
1356
+ const [isPickerFullscreen, setIsPickerFullscreen] = React56.useState(false);
1357
+ const [mediaItems, setMediaItems] = React56.useState([]);
1358
+ const [folders, setFolders] = React56.useState([]);
1359
+ const [selectedFolder, setSelectedFolder] = React56.useState("");
1360
+ const [mediaLoading, setMediaLoading] = React56.useState(false);
1361
+ const [pickerSearch, setPickerSearch] = React56.useState("");
1362
+ const [showUrlInput, setShowUrlInput] = React56.useState(false);
1363
+ const [urlValue, setUrlValue] = React56.useState("");
1364
+ const [urlError, setUrlError] = React56.useState("");
1365
+ const [selectedItems, setSelectedItems] = React56.useState([]);
1510
1366
  const fieldLabel = field3?.label || field3?.name || "File";
1511
1367
  const maxCount = field3.maxCount ?? (field3.hasMany ? 999 : 1);
1512
1368
  const isMultiple = maxCount > 1;
1513
1369
  const currentValue = Array.isArray(value) ? value : value ? [value] : [];
1514
1370
  const canAddMore = currentValue.length < maxCount;
1515
- React54.useEffect(() => {
1371
+ React56.useEffect(() => {
1516
1372
  const fetchMissingDetails = async () => {
1517
1373
  const idsToFetch = currentValue.filter((item) => typeof item === "string").map((id) => id);
1518
1374
  const objectIdsToFetch = currentValue.filter(
@@ -1546,7 +1402,7 @@ function UploadField({
1546
1402
  };
1547
1403
  fetchMissingDetails();
1548
1404
  }, [value]);
1549
- React54.useEffect(() => {
1405
+ React56.useEffect(() => {
1550
1406
  if (showPicker) {
1551
1407
  loadFolders();
1552
1408
  loadMedia();
@@ -1672,7 +1528,7 @@ function UploadField({
1672
1528
  newValue.splice(index, 1);
1673
1529
  onChange(isMultiple ? newValue : newValue[0] || null);
1674
1530
  };
1675
- const filteredMedia = React54.useMemo(() => {
1531
+ const filteredMedia = React56.useMemo(() => {
1676
1532
  return mediaItems.filter((item) => {
1677
1533
  return !pickerSearch || item.filename?.toLowerCase().includes(pickerSearch.toLowerCase()) || item.title?.toLowerCase().includes(pickerSearch.toLowerCase());
1678
1534
  });
@@ -2079,12 +1935,12 @@ function SlidePanel({
2079
1935
  showOverlay = false,
2080
1936
  accentClass
2081
1937
  }) {
2082
- const panelRef = React54.useRef(null);
2083
- const [hydrated, setHydrated] = React54.useState(false);
2084
- React54.useEffect(() => {
1938
+ const panelRef = React56.useRef(null);
1939
+ const [hydrated, setHydrated] = React56.useState(false);
1940
+ React56.useEffect(() => {
2085
1941
  setHydrated(true);
2086
1942
  }, []);
2087
- React54.useEffect(() => {
1943
+ React56.useEffect(() => {
2088
1944
  const handleEscape = (e) => {
2089
1945
  if (e.key === "Escape") onClose();
2090
1946
  };
@@ -2129,18 +1985,7 @@ function SlidePanel({
2129
1985
  type: "button",
2130
1986
  onClick: onClose,
2131
1987
  className: "p-1.5 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] rounded-lg transition-colors",
2132
- children: /* @__PURE__ */ jsxRuntime.jsx(
2133
- "svg",
2134
- {
2135
- width: "16",
2136
- height: "16",
2137
- viewBox: "0 0 24 24",
2138
- fill: "none",
2139
- stroke: "currentColor",
2140
- strokeWidth: "2",
2141
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
2142
- }
2143
- )
1988
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
2144
1989
  }
2145
1990
  )
2146
1991
  ] }),
@@ -2161,54 +2006,158 @@ function SlidePanel({
2161
2006
  document.body
2162
2007
  );
2163
2008
  }
2164
- function PromptModal({
2009
+ function ModalContent({ children }) {
2010
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[var(--kyro-text-secondary)]", children });
2011
+ }
2012
+ function ModalActions({ children }) {
2013
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-3 mt-6", children });
2014
+ }
2015
+ function Modal({
2165
2016
  open,
2166
2017
  onClose,
2167
- onSubmit,
2168
2018
  title,
2169
- placeholder = "",
2170
- defaultValue = ""
2019
+ children,
2020
+ footer,
2021
+ size = "md",
2022
+ variant = "default"
2171
2023
  }) {
2172
- const [value, setValue] = React54.useState(defaultValue);
2173
- const handleSubmit = (e) => {
2174
- e.preventDefault();
2175
- if (value.trim()) {
2176
- onSubmit(value.trim());
2177
- setValue("");
2178
- onClose();
2024
+ React56.useEffect(() => {
2025
+ const handleEscape = (e) => {
2026
+ if (e.key === "Escape") onClose();
2027
+ };
2028
+ if (open) {
2029
+ document.addEventListener("keydown", handleEscape);
2030
+ document.body.style.overflow = "hidden";
2179
2031
  }
2180
- };
2032
+ return () => {
2033
+ document.removeEventListener("keydown", handleEscape);
2034
+ document.body.style.overflow = "";
2035
+ };
2036
+ }, [open, onClose]);
2181
2037
  if (!open) return null;
2038
+ const sizeClasses = {
2039
+ sm: "max-w-sm",
2040
+ md: "max-w-md",
2041
+ lg: "max-w-lg",
2042
+ full: "w-full h-full max-w-none rounded-none border-0"
2043
+ };
2044
+ const isLightbox = variant === "lightbox";
2182
2045
  return reactDom.createPortal(
2183
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center", children: [
2046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center p-4", children: [
2184
2047
  /* @__PURE__ */ jsxRuntime.jsx(
2185
2048
  "div",
2186
2049
  {
2187
- className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
2050
+ className: `absolute inset-0 transition-all duration-500 ${isLightbox ? "bg-black/95 backdrop-blur-none" : "bg-[var(--kyro-black)]/40 backdrop-blur-md"}`,
2188
2051
  onClick: onClose
2189
2052
  }
2190
2053
  ),
2191
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-lg mx-4 bg-[var(--kyro-surface)] rounded-lg shadow-2xl animate-in fade-in zoom-in-95 duration-200 border border-[var(--kyro-border)]", children: [
2192
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--kyro-border)]", children: [
2193
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-[var(--kyro-text-primary)]", children: title }),
2194
- /* @__PURE__ */ jsxRuntime.jsx(
2195
- "button",
2196
- {
2197
- type: "button",
2198
- onClick: onClose,
2199
- className: "p-1 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-colors",
2200
- children: /* @__PURE__ */ jsxRuntime.jsx(
2201
- "svg",
2202
- {
2203
- width: "20",
2204
- height: "20",
2205
- viewBox: "0 0 24 24",
2206
- fill: "none",
2207
- stroke: "currentColor",
2208
- strokeWidth: "2",
2209
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
2054
+ /* @__PURE__ */ jsxRuntime.jsxs(
2055
+ "div",
2056
+ {
2057
+ className: `relative ${sizeClasses[size]} ${isLightbox ? "bg-transparent text-white" : "bg-[var(--kyro-surface)]"} ${!isLightbox && size !== "full" ? "rounded-[var(--kyro-radius-lg)]" : ""} shadow-2xl animate-in fade-in zoom-in-95 duration-300 ${!isLightbox ? "border" : ""} ${variant === "danger" ? "border-red-500/30" : "border-[var(--kyro-border)]"} flex flex-col overflow-hidden`,
2058
+ children: [
2059
+ !isLightbox && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-8 py-6 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]/50 backdrop-blur-md", children: [
2060
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-bold text-[var(--kyro-text-primary)]", children: title }),
2061
+ /* @__PURE__ */ jsxRuntime.jsx(
2062
+ "button",
2063
+ {
2064
+ type: "button",
2065
+ onClick: onClose,
2066
+ className: "p-2 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] rounded-xl hover:bg-[var(--kyro-surface)] transition-all duration-200",
2067
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
2210
2068
  }
2211
2069
  )
2070
+ ] }),
2071
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-1 overflow-auto ${isLightbox ? "" : "px-8 py-8"}`, children }),
2072
+ footer && !isLightbox && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-3 px-8 py-6 border-t border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]/50", children: footer })
2073
+ ]
2074
+ }
2075
+ )
2076
+ ] }),
2077
+ document.body
2078
+ );
2079
+ }
2080
+ function ConfirmModal({
2081
+ open,
2082
+ onClose,
2083
+ onConfirm,
2084
+ title,
2085
+ message,
2086
+ confirmLabel = "Confirm",
2087
+ cancelLabel = "Cancel",
2088
+ variant = "default",
2089
+ loading = false
2090
+ }) {
2091
+ return /* @__PURE__ */ jsxRuntime.jsx(
2092
+ Modal,
2093
+ {
2094
+ open,
2095
+ onClose,
2096
+ title,
2097
+ size: "sm",
2098
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2099
+ /* @__PURE__ */ jsxRuntime.jsx(
2100
+ "button",
2101
+ {
2102
+ type: "button",
2103
+ onClick: onClose,
2104
+ disabled: loading,
2105
+ className: "kyro-btn kyro-btn-md kyro-btn-secondary",
2106
+ children: cancelLabel
2107
+ }
2108
+ ),
2109
+ /* @__PURE__ */ jsxRuntime.jsx(
2110
+ "button",
2111
+ {
2112
+ type: "button",
2113
+ onClick: onConfirm,
2114
+ disabled: loading,
2115
+ className: `kyro-btn kyro-btn-md ${variant === "danger" ? "kyro-btn-danger" : "kyro-btn-primary"}`,
2116
+ children: loading ? "Loading..." : confirmLabel
2117
+ }
2118
+ )
2119
+ ] }),
2120
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[var(--kyro-text-secondary)]", children: message })
2121
+ }
2122
+ );
2123
+ }
2124
+ function PromptModal({
2125
+ open,
2126
+ onClose,
2127
+ onSubmit,
2128
+ title,
2129
+ placeholder = "",
2130
+ defaultValue = ""
2131
+ }) {
2132
+ const [value, setValue] = React56.useState(defaultValue);
2133
+ const handleSubmit = (e) => {
2134
+ e.preventDefault();
2135
+ if (value.trim()) {
2136
+ onSubmit(value.trim());
2137
+ setValue("");
2138
+ onClose();
2139
+ }
2140
+ };
2141
+ if (!open) return null;
2142
+ return reactDom.createPortal(
2143
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center", children: [
2144
+ /* @__PURE__ */ jsxRuntime.jsx(
2145
+ "div",
2146
+ {
2147
+ className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
2148
+ onClick: onClose
2149
+ }
2150
+ ),
2151
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-lg mx-4 bg-[var(--kyro-surface)] rounded-lg shadow-2xl animate-in fade-in zoom-in-95 duration-200 border border-[var(--kyro-border)]", children: [
2152
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--kyro-border)]", children: [
2153
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-[var(--kyro-text-primary)]", children: title }),
2154
+ /* @__PURE__ */ jsxRuntime.jsx(
2155
+ "button",
2156
+ {
2157
+ type: "button",
2158
+ onClick: onClose,
2159
+ className: "p-1 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-colors",
2160
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
2212
2161
  }
2213
2162
  )
2214
2163
  ] }),
@@ -2293,35 +2242,35 @@ function MediaGallery({
2293
2242
  const canUpload = permissions?.media?.create !== false;
2294
2243
  const canDelete = permissions?.media?.delete !== false;
2295
2244
  const canUpdate = permissions?.media?.update !== false;
2296
- const [items, setItems] = React54.useState([]);
2297
- const [loading, setLoading] = React54.useState(true);
2298
- const [folders, setFolders] = React54.useState([]);
2299
- const [currentFolder, setCurrentFolder] = React54.useState("");
2300
- const [search, setSearch] = React54.useState("");
2301
- const [filter, setFilter] = React54.useState("all");
2302
- const [view, setView] = React54.useState("grid");
2303
- const [panelItem, setPanelItem] = React54.useState(null);
2304
- const [showPreview, setShowPreview] = React54.useState(false);
2305
- const [showCrop, setShowCrop] = React54.useState(false);
2306
- const [uploading, setUploading] = React54.useState(false);
2307
- const [uploadProgress, setUploadProgress] = React54.useState(
2245
+ const [items, setItems] = React56.useState([]);
2246
+ const [loading, setLoading] = React56.useState(true);
2247
+ const [folders, setFolders] = React56.useState([]);
2248
+ const [currentFolder, setCurrentFolder] = React56.useState("");
2249
+ const [search, setSearch] = React56.useState("");
2250
+ const [filter, setFilter] = React56.useState("all");
2251
+ const [view, setView] = React56.useState("grid");
2252
+ const [panelItem, setPanelItem] = React56.useState(null);
2253
+ const [showPreview, setShowPreview] = React56.useState(false);
2254
+ const [showCrop, setShowCrop] = React56.useState(false);
2255
+ const [uploading, setUploading] = React56.useState(false);
2256
+ const [uploadProgress, setUploadProgress] = React56.useState(
2308
2257
  {}
2309
2258
  );
2310
- const [showNewFolderModal, setShowNewFolderModal] = React54.useState(false);
2311
- const [storageConfigured, setStorageConfigured] = React54.useState(null);
2312
- const [storageChecked, setStorageChecked] = React54.useState(false);
2313
- const [showStorageConfigModal, setShowStorageConfigModal] = React54.useState(false);
2314
- const [page, setPage] = React54.useState(1);
2315
- const [total, setTotal] = React54.useState(0);
2316
- const [totalPages, setTotalPages] = React54.useState(1);
2259
+ const [showNewFolderModal, setShowNewFolderModal] = React56.useState(false);
2260
+ const [storageConfigured, setStorageConfigured] = React56.useState(null);
2261
+ const [storageChecked, setStorageChecked] = React56.useState(false);
2262
+ const [showStorageConfigModal, setShowStorageConfigModal] = React56.useState(false);
2263
+ const [page, setPage] = React56.useState(1);
2264
+ const [total, setTotal] = React56.useState(0);
2265
+ const [totalPages, setTotalPages] = React56.useState(1);
2317
2266
  const limit = 40;
2318
- const [selectedIds, setSelectedIds] = React54.useState(/* @__PURE__ */ new Set());
2267
+ const [selectedIds, setSelectedIds] = React56.useState(/* @__PURE__ */ new Set());
2319
2268
  const { confirm, alert } = useUIStore();
2320
- const [isDragging, setIsDragging] = React54.useState(false);
2321
- const fileInputRef = React54.useRef(null);
2322
- const [crop, setCrop] = React54.useState();
2323
- const imgRef = React54.useRef(null);
2324
- const loadMedia = React54.useCallback(async () => {
2269
+ const [isDragging, setIsDragging] = React56.useState(false);
2270
+ const fileInputRef = React56.useRef(null);
2271
+ const [crop, setCrop] = React56.useState();
2272
+ const imgRef = React56.useRef(null);
2273
+ const loadMedia = React56.useCallback(async () => {
2325
2274
  setLoading(true);
2326
2275
  try {
2327
2276
  const params = new URLSearchParams({
@@ -2344,7 +2293,7 @@ function MediaGallery({
2344
2293
  setLoading(false);
2345
2294
  }
2346
2295
  }, [page, currentFolder, search, filter]);
2347
- const loadFolders = React54.useCallback(async () => {
2296
+ const loadFolders = React56.useCallback(async () => {
2348
2297
  try {
2349
2298
  const result = await apiGet(withCacheBust("/api/media/folders"));
2350
2299
  setFolders(Array.isArray(result) ? result : result.folders || []);
@@ -2352,7 +2301,7 @@ function MediaGallery({
2352
2301
  console.error("Failed to load folders:", error);
2353
2302
  }
2354
2303
  }, []);
2355
- const checkStorage = React54.useCallback(async () => {
2304
+ const checkStorage = React56.useCallback(async () => {
2356
2305
  try {
2357
2306
  const res = await apiGet("/api/globals/storage-settings");
2358
2307
  const isConfigured = !!res?.data?.provider;
@@ -2361,23 +2310,23 @@ function MediaGallery({
2361
2310
  setStorageConfigured(false);
2362
2311
  }
2363
2312
  }, []);
2364
- React54.useEffect(() => {
2313
+ React56.useEffect(() => {
2365
2314
  if (!pickerMode) checkStorage();
2366
2315
  }, [checkStorage, pickerMode]);
2367
- React54.useEffect(() => {
2316
+ React56.useEffect(() => {
2368
2317
  if (pickerMode) return;
2369
2318
  if (storageConfigured === false && !storageChecked) {
2370
2319
  setStorageChecked(true);
2371
2320
  setShowStorageConfigModal(true);
2372
2321
  }
2373
2322
  }, [pickerMode, storageConfigured, storageChecked]);
2374
- React54.useEffect(() => {
2323
+ React56.useEffect(() => {
2375
2324
  loadMedia();
2376
2325
  }, [loadMedia]);
2377
- React54.useEffect(() => {
2326
+ React56.useEffect(() => {
2378
2327
  loadFolders();
2379
2328
  }, [loadFolders]);
2380
- React54.useEffect(() => {
2329
+ React56.useEffect(() => {
2381
2330
  if (pickerMode) return;
2382
2331
  const handlePaste = (e) => {
2383
2332
  const files = e.clipboardData?.files;
@@ -2551,7 +2500,7 @@ function MediaGallery({
2551
2500
  setUploading(false);
2552
2501
  }
2553
2502
  };
2554
- const stats = React54.useMemo(() => {
2503
+ const stats = React56.useMemo(() => {
2555
2504
  return items.reduce(
2556
2505
  (acc, item) => {
2557
2506
  acc.totalSize += item.fileSize || 0;
@@ -2588,24 +2537,7 @@ function MediaGallery({
2588
2537
  ] }) }),
2589
2538
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-3 flex-wrap lg:flex-nowrap ${pickerMode ? "w-full" : ""}`, children: [
2590
2539
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group flex-1 min-w-[200px]", children: [
2591
- /* @__PURE__ */ jsxRuntime.jsx(
2592
- "svg",
2593
- {
2594
- className: "absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--kyro-text-secondary)] opacity-40 group-focus-within:opacity-100 transition-opacity",
2595
- fill: "none",
2596
- stroke: "currentColor",
2597
- viewBox: "0 0 24 24",
2598
- children: /* @__PURE__ */ jsxRuntime.jsx(
2599
- "path",
2600
- {
2601
- strokeLinecap: "round",
2602
- strokeLinejoin: "round",
2603
- strokeWidth: "2.5",
2604
- d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
2605
- }
2606
- )
2607
- }
2608
- ),
2540
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "w-4 h-4" }),
2609
2541
  /* @__PURE__ */ jsxRuntime.jsx(
2610
2542
  "input",
2611
2543
  {
@@ -2772,45 +2704,11 @@ function MediaGallery({
2772
2704
  {
2773
2705
  onClick: (e) => handleSelectOne(item.id, e),
2774
2706
  className: `kyro-btn-primary p-1.5 rounded-lg transition-all ${selectedIds.has(item.id) ? "" : "bg-white/10 text-white hover:bg-white/20"}`,
2775
- children: /* @__PURE__ */ jsxRuntime.jsx(
2776
- "svg",
2777
- {
2778
- className: "w-3 h-3",
2779
- fill: "none",
2780
- stroke: "currentColor",
2781
- viewBox: "0 0 24 24",
2782
- children: /* @__PURE__ */ jsxRuntime.jsx(
2783
- "path",
2784
- {
2785
- strokeLinecap: "round",
2786
- strokeLinejoin: "round",
2787
- strokeWidth: "3",
2788
- d: "M5 13l4 4L19 7"
2789
- }
2790
- )
2791
- }
2792
- )
2707
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" })
2793
2708
  }
2794
2709
  ) })
2795
2710
  ] }) }),
2796
- selectedIds.has(item.id) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 w-6 h-6 rounded-lg bg-[var(--kyro-primary)] text-white flex items-center justify-center shadow-lg border-2 border-white/20 animate-in zoom-in duration-300", children: /* @__PURE__ */ jsxRuntime.jsx(
2797
- "svg",
2798
- {
2799
- className: "w-3 h-3",
2800
- fill: "none",
2801
- stroke: "currentColor",
2802
- viewBox: "0 0 24 24",
2803
- children: /* @__PURE__ */ jsxRuntime.jsx(
2804
- "path",
2805
- {
2806
- strokeLinecap: "round",
2807
- strokeLinejoin: "round",
2808
- strokeWidth: "3",
2809
- d: "M5 13l4 4L19 7"
2810
- }
2811
- )
2812
- }
2813
- ) })
2711
+ selectedIds.has(item.id) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 w-6 h-6 rounded-lg bg-[var(--kyro-primary)] text-white flex items-center justify-center shadow-lg border-2 border-white/20 animate-in zoom-in duration-300", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" }) })
2814
2712
  ]
2815
2713
  },
2816
2714
  item.id
@@ -2890,34 +2788,14 @@ function MediaGallery({
2890
2788
  item.id
2891
2789
  )) })
2892
2790
  ] }) }) }),
2893
- totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 border-t border-[var(--kyro-border)] bg-[var(--kyro-surface)]/50 backdrop-blur-md flex items-center justify-between", children: [
2894
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] font-bold tracking-widest text-[var(--kyro-text-secondary)] opacity-50", children: [
2895
- "Page ",
2791
+ /* @__PURE__ */ jsxRuntime.jsx(
2792
+ Pagination,
2793
+ {
2896
2794
  page,
2897
- " of ",
2898
- totalPages
2899
- ] }),
2900
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2901
- /* @__PURE__ */ jsxRuntime.jsx(
2902
- "button",
2903
- {
2904
- disabled: page === 1,
2905
- onClick: () => setPage(page - 1),
2906
- className: "px-4 py-2 border border-[var(--kyro-border)] rounded-xl text-xs font-bold text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] disabled:opacity-30 transition-all",
2907
- children: "Previous"
2908
- }
2909
- ),
2910
- /* @__PURE__ */ jsxRuntime.jsx(
2911
- "button",
2912
- {
2913
- disabled: page === totalPages,
2914
- onClick: () => setPage(page + 1),
2915
- className: "px-6 py-2 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-xl text-xs font-bold shadow-lg hover:opacity-90 disabled:opacity-30 transition-all",
2916
- children: "Next"
2917
- }
2918
- )
2919
- ] })
2920
- ] })
2795
+ totalPages,
2796
+ onPageChange: setPage
2797
+ }
2798
+ )
2921
2799
  ] })
2922
2800
  ] }),
2923
2801
  !pickerMode && uploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed bottom-12 left-1/2 -translate-x-1/2 z-[60] w-full max-w-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-[2rem] shadow-2xl p-6 ring-1 ring-white/10 animate-in slide-in-from-bottom-12 duration-700", children: [
@@ -3143,86 +3021,100 @@ function MediaGallery({
3143
3021
  ] })
3144
3022
  }
3145
3023
  ),
3146
- showPreview && panelItem && reactDom.createPortal(
3147
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[9999] bg-black/95 flex flex-col animate-in fade-in duration-500", children: [
3148
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-6", children: [
3149
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
3150
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white font-bold text-lg tracking-tight", children: panelItem.filename }),
3151
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/40 text-[10px] font-bold tracking-widest mt-1", children: [
3152
- formatFileSize(panelItem.fileSize),
3153
- " \xB7 ",
3154
- panelItem.mimeType
3155
- ] })
3156
- ] }),
3157
- /* @__PURE__ */ jsxRuntime.jsx(
3158
- "button",
3159
- {
3160
- onClick: () => setShowPreview(false),
3161
- className: "p-3 bg-white/10 hover:bg-white/20 text-white rounded-2xl transition-all active:scale-90",
3162
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-6 h-6" })
3163
- }
3164
- )
3165
- ] }),
3166
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full flex items-center justify-center p-12", children: panelItem.type === "image" ? /* @__PURE__ */ jsxRuntime.jsx(
3167
- "img",
3168
- {
3169
- src: getAbsoluteUrl(panelItem.url),
3170
- alt: "",
3171
- className: "max-h-full max-w-full object-contain shadow-2xl rounded-lg animate-in zoom-in-95 duration-500"
3172
- }
3173
- ) : panelItem.type === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
3174
- "video",
3175
- {
3176
- src: getAbsoluteUrl(panelItem.url),
3177
- controls: true,
3178
- autoPlay: true,
3179
- className: "max-h-full max-w-full rounded-lg shadow-2xl"
3180
- }
3181
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-white text-center", children: [
3182
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.File, { className: "w-24 h-24 mx-auto mb-6 opacity-20" }),
3183
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-bold opacity-50", children: "Preview not available for this file type" })
3184
- ] }) })
3185
- ] }),
3186
- document.body
3187
- ),
3188
- !pickerMode && showCrop && panelItem && reactDom.createPortal(
3189
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[9999] bg-black/95 flex flex-col p-8", children: [
3190
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-8", children: [
3191
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-white font-bold text-2xl tracking-tighter", children: "Crop Image" }),
3192
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
3193
- /* @__PURE__ */ jsxRuntime.jsx(
3194
- "button",
3195
- {
3196
- type: "button",
3197
- onClick: () => setShowCrop(false),
3198
- className: "px-4 py-2 border border-white/20 text-white/80 hover:bg-white/10 rounded-lg font-bold text-sm transition-colors",
3199
- children: "Cancel"
3200
- }
3201
- ),
3024
+ showPreview && panelItem && /* @__PURE__ */ jsxRuntime.jsxs(
3025
+ Modal,
3026
+ {
3027
+ open: showPreview,
3028
+ onClose: () => setShowPreview(false),
3029
+ title: "",
3030
+ size: "full",
3031
+ variant: "lightbox",
3032
+ children: [
3033
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-6", children: [
3034
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
3035
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white font-bold text-lg tracking-tight", children: panelItem.filename }),
3036
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/40 text-[10px] font-bold tracking-widest mt-1", children: [
3037
+ formatFileSize(panelItem.fileSize),
3038
+ " \xB7 ",
3039
+ panelItem.mimeType
3040
+ ] })
3041
+ ] }),
3202
3042
  /* @__PURE__ */ jsxRuntime.jsx(
3203
3043
  "button",
3204
3044
  {
3205
- type: "button",
3206
- disabled: uploading,
3207
- onClick: onCropComplete,
3208
- className: "px-4 py-2 bg-[var(--kyro-sidebar-active)] hover:opacity-90 text-[var(--kyro-sidebar-text-active)] rounded-lg font-bold text-sm transition-colors",
3209
- children: uploading ? "Saving..." : "Save Crop"
3045
+ onClick: () => setShowPreview(false),
3046
+ className: "p-3 bg-white/10 hover:bg-white/20 text-white rounded-2xl transition-all active:scale-90",
3047
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-6 h-6" })
3210
3048
  }
3211
3049
  )
3212
- ] })
3213
- ] }),
3214
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full flex items-center justify-center overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ReactCrop__default.default, { crop, onChange: (c) => setCrop(c), children: /* @__PURE__ */ jsxRuntime.jsx(
3215
- "img",
3216
- {
3217
- ref: imgRef,
3218
- src: panelItem.url,
3219
- alt: "Crop preview",
3220
- className: "max-h-[70vh] object-contain",
3221
- onLoad: onImageLoad
3222
- }
3223
- ) }) })
3224
- ] }),
3225
- document.body
3050
+ ] }),
3051
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full flex items-center justify-center p-12", children: panelItem.type === "image" ? /* @__PURE__ */ jsxRuntime.jsx(
3052
+ "img",
3053
+ {
3054
+ src: getAbsoluteUrl(panelItem.url),
3055
+ alt: "",
3056
+ className: "max-h-full max-w-full object-contain shadow-2xl rounded-lg animate-in zoom-in-95 duration-500"
3057
+ }
3058
+ ) : panelItem.type === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
3059
+ "video",
3060
+ {
3061
+ src: getAbsoluteUrl(panelItem.url),
3062
+ controls: true,
3063
+ autoPlay: true,
3064
+ className: "max-h-full max-w-full rounded-lg shadow-2xl"
3065
+ }
3066
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-white text-center", children: [
3067
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.File, { className: "w-24 h-24 mx-auto mb-6 opacity-20" }),
3068
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-bold opacity-50", children: "Preview not available for this file type" })
3069
+ ] }) })
3070
+ ]
3071
+ }
3072
+ ),
3073
+ !pickerMode && showCrop && panelItem && /* @__PURE__ */ jsxRuntime.jsx(
3074
+ Modal,
3075
+ {
3076
+ open: showCrop,
3077
+ onClose: () => setShowCrop(false),
3078
+ title: "",
3079
+ size: "full",
3080
+ variant: "lightbox",
3081
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full p-8", children: [
3082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-8", children: [
3083
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-white font-bold text-2xl tracking-tighter", children: "Crop Image" }),
3084
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
3085
+ /* @__PURE__ */ jsxRuntime.jsx(
3086
+ "button",
3087
+ {
3088
+ type: "button",
3089
+ onClick: () => setShowCrop(false),
3090
+ className: "px-4 py-2 border border-white/20 text-white/80 hover:bg-white/10 rounded-lg font-bold text-sm transition-colors",
3091
+ children: "Cancel"
3092
+ }
3093
+ ),
3094
+ /* @__PURE__ */ jsxRuntime.jsx(
3095
+ "button",
3096
+ {
3097
+ type: "button",
3098
+ disabled: uploading,
3099
+ onClick: onCropComplete,
3100
+ className: "px-4 py-2 bg-[var(--kyro-sidebar-active)] hover:opacity-90 text-[var(--kyro-sidebar-text-active)] rounded-lg font-bold text-sm transition-colors",
3101
+ children: uploading ? "Saving..." : "Save Crop"
3102
+ }
3103
+ )
3104
+ ] })
3105
+ ] }),
3106
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full flex items-center justify-center overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ReactCrop__default.default, { crop, onChange: (c) => setCrop(c), children: /* @__PURE__ */ jsxRuntime.jsx(
3107
+ "img",
3108
+ {
3109
+ ref: imgRef,
3110
+ src: panelItem.url,
3111
+ alt: "Crop preview",
3112
+ className: "max-h-[70vh] object-contain",
3113
+ onLoad: onImageLoad
3114
+ }
3115
+ ) }) })
3116
+ ] })
3117
+ }
3226
3118
  ),
3227
3119
  !pickerMode && /* @__PURE__ */ jsxRuntime.jsx(
3228
3120
  PromptModal,
@@ -3234,61 +3126,49 @@ function MediaGallery({
3234
3126
  placeholder: "Folder name"
3235
3127
  }
3236
3128
  ),
3237
- !pickerMode && showStorageConfigModal && reactDom.createPortal(
3238
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-[9999] bg-black/80 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-2xl p-8 max-w-md w-full shadow-2xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
3239
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 mx-auto mb-4 rounded-full bg-[var(--kyro-sidebar-active)] flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
3240
- "svg",
3241
- {
3242
- className: "w-8 h-8 text-[var(--kyro-sidebar-text-active)]",
3243
- fill: "none",
3244
- stroke: "currentColor",
3245
- viewBox: "0 0 24 24",
3246
- children: /* @__PURE__ */ jsxRuntime.jsx(
3247
- "path",
3129
+ !pickerMode && /* @__PURE__ */ jsxRuntime.jsx(
3130
+ Modal,
3131
+ {
3132
+ open: showStorageConfigModal,
3133
+ onClose: () => setShowStorageConfigModal(false),
3134
+ title: "Storage Not Configured",
3135
+ size: "md",
3136
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
3137
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 mx-auto mb-4 rounded-full bg-[var(--kyro-sidebar-active)] flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Server, { className: "w-8 h-8 text-[var(--kyro-sidebar-text-active)]" }) }),
3138
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[var(--kyro-text-secondary)] mb-6 text-sm", children: "Before uploading media, you need to configure your storage settings. Choose where files should be stored and how URLs are generated." }),
3139
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
3140
+ /* @__PURE__ */ jsxRuntime.jsx(
3141
+ "a",
3142
+ {
3143
+ href: "/settings/storage-settings",
3144
+ className: "flex-1 px-4 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-xl font-bold text-center hover:opacity-90 transition-colors",
3145
+ children: "Configure Storage"
3146
+ }
3147
+ ),
3148
+ /* @__PURE__ */ jsxRuntime.jsx(
3149
+ "button",
3248
3150
  {
3249
- strokeLinecap: "round",
3250
- strokeLinejoin: "round",
3251
- strokeWidth: 2,
3252
- d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2H5a2 2 0 00-2 2v1m2 2a2 2 0 11-4 0 2 2 0 014 0zm2 2h.008v.008H5v-.008z"
3151
+ type: "button",
3152
+ onClick: () => {
3153
+ apiPost("/api/globals/storage-settings", {
3154
+ provider: "local",
3155
+ local: {
3156
+ uploadDir: "./public/uploads",
3157
+ baseUrl: "/uploads"
3158
+ }
3159
+ }).then(() => {
3160
+ setShowStorageConfigModal(false);
3161
+ setStorageConfigured(true);
3162
+ window.location.reload();
3163
+ });
3164
+ },
3165
+ className: "flex-1 px-4 py-3 border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] rounded-xl font-bold hover:bg-[var(--kyro-surface-accent)] transition-colors",
3166
+ children: "Use Defaults"
3253
3167
  }
3254
3168
  )
3255
- }
3256
- ) }),
3257
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-bold text-[var(--kyro-text-primary)] mb-2", children: "Storage Not Configured" }),
3258
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[var(--kyro-text-secondary)] mb-6 text-sm", children: "Before uploading media, you need to configure your storage settings. Choose where files should be stored and how URLs are generated." }),
3259
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
3260
- /* @__PURE__ */ jsxRuntime.jsx(
3261
- "a",
3262
- {
3263
- href: "/settings/storage-settings",
3264
- className: "flex-1 px-4 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-xl font-bold text-center hover:opacity-90 transition-colors",
3265
- children: "Configure Storage"
3266
- }
3267
- ),
3268
- /* @__PURE__ */ jsxRuntime.jsx(
3269
- "button",
3270
- {
3271
- type: "button",
3272
- onClick: () => {
3273
- apiPost("/api/globals/storage-settings", {
3274
- provider: "local",
3275
- local: {
3276
- uploadDir: "./public/uploads",
3277
- baseUrl: "/uploads"
3278
- }
3279
- }).then(() => {
3280
- setShowStorageConfigModal(false);
3281
- setStorageConfigured(true);
3282
- window.location.reload();
3283
- });
3284
- },
3285
- className: "flex-1 px-4 py-3 border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] rounded-xl font-bold hover:bg-[var(--kyro-surface-accent)] transition-colors",
3286
- children: "Use Defaults"
3287
- }
3288
- )
3169
+ ] })
3289
3170
  ] })
3290
- ] }) }) }),
3291
- document.body
3171
+ }
3292
3172
  ),
3293
3173
  !pickerMode && /* @__PURE__ */ jsxRuntime.jsx(
3294
3174
  "input",
@@ -3325,9 +3205,9 @@ var MenuBar = ({
3325
3205
  setIsExpanded,
3326
3206
  onOpenMediaPicker
3327
3207
  }) => {
3328
- const [activeDropdown, setActiveDropdown] = React54.useState(null);
3329
- const menuBarRef = React54.useRef(null);
3330
- React54.useEffect(() => {
3208
+ const [activeDropdown, setActiveDropdown] = React56.useState(null);
3209
+ const menuBarRef = React56.useRef(null);
3210
+ React56.useEffect(() => {
3331
3211
  function handleClickOutside(event) {
3332
3212
  if (menuBarRef.current && !menuBarRef.current.contains(event.target)) {
3333
3213
  setActiveDropdown(null);
@@ -3787,14 +3667,14 @@ function RichTextField({
3787
3667
  error,
3788
3668
  disabled
3789
3669
  }) {
3790
- const [isExpanded, setIsExpanded] = React54.useState(false);
3791
- const [panelWidth, setPanelWidth] = React54.useState(0);
3792
- const [isMediaPickerOpen, setIsMediaPickerOpen] = React54.useState(false);
3793
- const [isMounted, setIsMounted] = React54.useState(false);
3794
- React54.useEffect(() => {
3670
+ const [isExpanded, setIsExpanded] = React56.useState(false);
3671
+ const [panelWidth, setPanelWidth] = React56.useState(0);
3672
+ const [isMediaPickerOpen, setIsMediaPickerOpen] = React56.useState(false);
3673
+ const [isMounted, setIsMounted] = React56.useState(false);
3674
+ React56.useEffect(() => {
3795
3675
  setIsMounted(true);
3796
3676
  }, []);
3797
- React54.useEffect(() => {
3677
+ React56.useEffect(() => {
3798
3678
  if (!isExpanded) {
3799
3679
  setPanelWidth(0);
3800
3680
  return;
@@ -3860,7 +3740,7 @@ function RichTextField({
3860
3740
  }
3861
3741
  }
3862
3742
  });
3863
- React54.useEffect(() => {
3743
+ React56.useEffect(() => {
3864
3744
  if (editor && value && JSON.stringify(value) !== JSON.stringify(editor.getJSON())) {
3865
3745
  editor.commands.setContent(value);
3866
3746
  }
@@ -4129,9 +4009,9 @@ function mergeThemes(base, overrides) {
4129
4009
  fields: base.fields ? { ...base.fields, ...overrides.fields } : overrides.fields
4130
4010
  };
4131
4011
  }
4132
- var ThemeContext = React54.createContext(null);
4012
+ var ThemeContext = React56.createContext(null);
4133
4013
  function useTheme() {
4134
- const context = React54.useContext(ThemeContext);
4014
+ const context = React56.useContext(ThemeContext);
4135
4015
  if (!context) {
4136
4016
  return {
4137
4017
  mode: "light",
@@ -4242,16 +4122,16 @@ function ThemeProvider({
4242
4122
  light: lightOverrides,
4243
4123
  dark: darkOverrides
4244
4124
  }) {
4245
- const [mode, setMode] = React54.useState(defaultMode);
4246
- const [baseLight, setBaseLight] = React54.useState(
4125
+ const [mode, setMode] = React56.useState(defaultMode);
4126
+ const [baseLight, setBaseLight] = React56.useState(
4247
4127
  lightOverrides || {}
4248
4128
  );
4249
- const [baseDark, setBaseDark] = React54.useState(
4129
+ const [baseDark, setBaseDark] = React56.useState(
4250
4130
  darkOverrides || {}
4251
4131
  );
4252
4132
  const lightTheme = mergeThemes(LIGHT_THEME, baseLight);
4253
4133
  const darkTheme = mergeThemes(DARK_THEME, baseDark);
4254
- const getResolvedTheme = React54.useCallback(() => {
4134
+ const getResolvedTheme = React56.useCallback(() => {
4255
4135
  if (mode === "system") {
4256
4136
  if (typeof window !== "undefined") {
4257
4137
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? darkTheme : lightTheme;
@@ -4260,13 +4140,13 @@ function ThemeProvider({
4260
4140
  }
4261
4141
  return mode === "dark" ? darkTheme : lightTheme;
4262
4142
  }, [mode, lightTheme, darkTheme]);
4263
- const [theme, setTheme] = React54.useState(getResolvedTheme());
4264
- React54.useEffect(() => {
4143
+ const [theme, setTheme] = React56.useState(getResolvedTheme());
4144
+ React56.useEffect(() => {
4265
4145
  const resolved = getResolvedTheme();
4266
4146
  setTheme(resolved);
4267
4147
  applyThemeToDOM(resolved);
4268
4148
  }, [getResolvedTheme]);
4269
- React54.useEffect(() => {
4149
+ React56.useEffect(() => {
4270
4150
  if (mode !== "system") return;
4271
4151
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
4272
4152
  const handler = () => {
@@ -4277,11 +4157,11 @@ function ThemeProvider({
4277
4157
  mediaQuery.addEventListener("change", handler);
4278
4158
  return () => mediaQuery.removeEventListener("change", handler);
4279
4159
  }, [mode, getResolvedTheme]);
4280
- const updateTheme = React54.useCallback((overrides) => {
4160
+ const updateTheme = React56.useCallback((overrides) => {
4281
4161
  setBaseLight((prev) => ({ ...prev, ...overrides }));
4282
4162
  setBaseDark((prev) => ({ ...prev, ...overrides }));
4283
4163
  }, []);
4284
- const getCssVar = React54.useCallback((key) => `var(--kyro-${key})`, []);
4164
+ const getCssVar = React56.useCallback((key) => `var(--kyro-${key})`, []);
4285
4165
  return /* @__PURE__ */ jsxRuntime.jsx(
4286
4166
  ThemeContext.Provider,
4287
4167
  {
@@ -4300,7 +4180,7 @@ function ThemeProvider({
4300
4180
  }
4301
4181
  var LightThemeProvider = (props) => /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { defaultMode: "light", ...props });
4302
4182
  var DarkThemeProvider = (props) => /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { defaultMode: "dark", ...props });
4303
- var CodeMirrorEditor = React54.lazy(
4183
+ var CodeMirrorEditor = React56.lazy(
4304
4184
  () => import('@uiw/react-codemirror').then((mod) => ({ default: mod.default }))
4305
4185
  );
4306
4186
  var LANGUAGES = [
@@ -4352,16 +4232,16 @@ var CodeField = ({
4352
4232
  error,
4353
4233
  disabled
4354
4234
  }) => {
4355
- const [isMounted, setIsMounted] = React54.useState(false);
4356
- const [isDark, setIsDark] = React54.useState(false);
4357
- const [extensions, setExtensions] = React54.useState([]);
4358
- const [loading, setLoading] = React54.useState(false);
4359
- const [copied, setCopied] = React54.useState(false);
4360
- const [isFullScreen, setIsFullScreen] = React54.useState(false);
4235
+ const [isMounted, setIsMounted] = React56.useState(false);
4236
+ const [isDark, setIsDark] = React56.useState(false);
4237
+ const [extensions, setExtensions] = React56.useState([]);
4238
+ const [loading, setLoading] = React56.useState(false);
4239
+ const [copied, setCopied] = React56.useState(false);
4240
+ const [isFullScreen, setIsFullScreen] = React56.useState(false);
4361
4241
  const { theme } = useTheme();
4362
4242
  const accent = theme.colors?.accent || theme.colors?.primary || "#6366f1";
4363
4243
  const language = field3.language?.toLowerCase() || "javascript";
4364
- React54.useEffect(() => {
4244
+ React56.useEffect(() => {
4365
4245
  setIsMounted(true);
4366
4246
  setIsDark(document.documentElement.classList.contains("dark"));
4367
4247
  const observer = new MutationObserver(() => {
@@ -4373,7 +4253,7 @@ var CodeField = ({
4373
4253
  });
4374
4254
  return () => observer.disconnect();
4375
4255
  }, []);
4376
- React54.useEffect(() => {
4256
+ React56.useEffect(() => {
4377
4257
  if (!isMounted) return;
4378
4258
  const loadExtensions = async () => {
4379
4259
  setLoading(true);
@@ -4390,16 +4270,16 @@ var CodeField = ({
4390
4270
  };
4391
4271
  loadExtensions();
4392
4272
  }, [language, isMounted]);
4393
- const handleChange = React54.useCallback(
4273
+ const handleChange = React56.useCallback(
4394
4274
  (val) => onChange?.(val),
4395
4275
  [onChange]
4396
4276
  );
4397
- const handleCopy = React54.useCallback(() => {
4277
+ const handleCopy = React56.useCallback(() => {
4398
4278
  navigator.clipboard.writeText(value);
4399
4279
  setCopied(true);
4400
4280
  setTimeout(() => setCopied(false), 1500);
4401
4281
  }, [value]);
4402
- const toggleFullScreen = React54.useCallback(() => {
4282
+ const toggleFullScreen = React56.useCallback(() => {
4403
4283
  setIsFullScreen((prev) => !prev);
4404
4284
  document.body.style.overflow = !isFullScreen ? "hidden" : "";
4405
4285
  }, [isFullScreen]);
@@ -4489,7 +4369,7 @@ var CodeField = ({
4489
4369
  }
4490
4370
  ),
4491
4371
  /* @__PURE__ */ jsxRuntime.jsx(
4492
- React54.Suspense,
4372
+ React56.Suspense,
4493
4373
  {
4494
4374
  fallback: /* @__PURE__ */ jsxRuntime.jsx(
4495
4375
  "div",
@@ -4609,18 +4489,18 @@ var MarkdownField = ({
4609
4489
  error,
4610
4490
  disabled
4611
4491
  }) => {
4612
- const [showPreview, setShowPreview] = React54.useState(false);
4613
- const [isMounted, setIsMounted] = React54.useState(false);
4614
- React54.useEffect(() => {
4492
+ const [showPreview, setShowPreview] = React56.useState(false);
4493
+ const [isMounted, setIsMounted] = React56.useState(false);
4494
+ React56.useEffect(() => {
4615
4495
  setIsMounted(true);
4616
4496
  }, []);
4617
- const handleChange = React54.useCallback(
4497
+ const handleChange = React56.useCallback(
4618
4498
  (e) => {
4619
4499
  onChange?.(e.target.value);
4620
4500
  },
4621
4501
  [onChange]
4622
4502
  );
4623
- const wordCount = React54.useMemo(() => {
4503
+ const wordCount = React56.useMemo(() => {
4624
4504
  if (!value) return 0;
4625
4505
  return value.trim().split(/\s+/).filter(Boolean).length;
4626
4506
  }, [value]);
@@ -4725,8 +4605,8 @@ function SecretField({
4725
4605
  error,
4726
4606
  disabled
4727
4607
  }) {
4728
- const [copied, setCopied] = React54.useState(false);
4729
- const [regenerating, setRegenerating] = React54.useState(false);
4608
+ const [copied, setCopied] = React56.useState(false);
4609
+ const [regenerating, setRegenerating] = React56.useState(false);
4730
4610
  const fullValue = value ?? "";
4731
4611
  const displayValue = fullValue.length > 8 ? fullValue.slice(0, -8) + "*".repeat(8) : fullValue;
4732
4612
  const handleCopy = async () => {
@@ -4882,6 +4762,7 @@ var useAutoFormStore = zustand.create()(
4882
4762
  loadingDiffs: false,
4883
4763
  isAutoSaving: false,
4884
4764
  autoSaveStatus: "idle",
4765
+ backgroundProcessing: false,
4885
4766
  // Auto-save state
4886
4767
  lastAutoSaveTime: 0,
4887
4768
  lastSavedAt: null,
@@ -4957,6 +4838,7 @@ var useAutoFormStore = zustand.create()(
4957
4838
  setLoadingDiffs: (loading) => set({ loadingDiffs: loading }),
4958
4839
  setIsAutoSaving: (saving) => set({ isAutoSaving: saving }),
4959
4840
  setAutoSaveStatus: (status) => set({ autoSaveStatus: status }),
4841
+ setBackgroundProcessing: (processing) => set({ backgroundProcessing: processing }),
4960
4842
  setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
4961
4843
  setLastSavedAt: (time) => set({ lastSavedAt: time }),
4962
4844
  setRetryCount: (count) => set({ retryCount: count }),
@@ -5453,6 +5335,14 @@ function SelectField({
5453
5335
  }
5454
5336
  );
5455
5337
  }
5338
+ function EmptyState({ icon, title, description, action }) {
5339
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3 justify-center py-16 px-8", children: [
5340
+ icon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center mb-4", children: icon }),
5341
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-[var(--kyro-text-primary)] text-base", children: title }),
5342
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[var(--kyro-text-secondary)] mt-1", children: description }),
5343
+ action
5344
+ ] });
5345
+ }
5456
5346
  function getLabel(opt) {
5457
5347
  const mainTabs = opt?.mainTabs;
5458
5348
  return opt?.title || mainTabs?.title || opt?.name || opt?.label || opt?.email || opt?.filename || opt?.slug || "Untitled";
@@ -5464,22 +5354,22 @@ function RelationshipField({
5464
5354
  error,
5465
5355
  disabled
5466
5356
  }) {
5467
- const [isOpen, setIsOpen] = React54.useState(false);
5468
- const [search, setSearch] = React54.useState("");
5469
- const [options, setOptions] = React54.useState([]);
5470
- const [loading, setLoading] = React54.useState(false);
5471
- const [selectedDocs, setSelectedDocs] = React54.useState([]);
5472
- const fetchedIdsRef = React54.useRef(/* @__PURE__ */ new Set());
5473
- const containerRef = React54.useRef(null);
5474
- const onChangeRef = React54.useRef(() => {
5357
+ const [isOpen, setIsOpen] = React56.useState(false);
5358
+ const [search, setSearch] = React56.useState("");
5359
+ const [options, setOptions] = React56.useState([]);
5360
+ const [loading, setLoading] = React56.useState(false);
5361
+ const [selectedDocs, setSelectedDocs] = React56.useState([]);
5362
+ const fetchedIdsRef = React56.useRef(/* @__PURE__ */ new Set());
5363
+ const containerRef = React56.useRef(null);
5364
+ const onChangeRef = React56.useRef(() => {
5475
5365
  });
5476
5366
  onChangeRef.current = onChange || (() => {
5477
5367
  });
5478
5368
  const isMultiple = field3.hasMany;
5479
5369
  const relationTo = Array.isArray(field3.relationTo) ? field3.relationTo : [field3.relationTo];
5480
5370
  const isPolymorphic = relationTo.length > 1;
5481
- const [activeRelation, setActiveRelation] = React54.useState(relationTo[0]);
5482
- const extractIds = React54.useCallback(() => {
5371
+ const [activeRelation, setActiveRelation] = React56.useState(relationTo[0]);
5372
+ const extractIds = React56.useCallback(() => {
5483
5373
  if (!value) return [];
5484
5374
  const items = isMultiple ? Array.isArray(value) ? value : [] : value ? [value] : [];
5485
5375
  return items.map((item) => {
@@ -5489,7 +5379,7 @@ function RelationshipField({
5489
5379
  return String(item);
5490
5380
  }).filter(Boolean);
5491
5381
  }, [value, isMultiple]);
5492
- const fetchSelectedDocs = React54.useCallback((ids) => {
5382
+ const fetchSelectedDocs = React56.useCallback((ids) => {
5493
5383
  if (ids.length === 0) return;
5494
5384
  ids.forEach((id) => {
5495
5385
  if (fetchedIdsRef.current.has(id)) return;
@@ -5516,11 +5406,11 @@ function RelationshipField({
5516
5406
  });
5517
5407
  });
5518
5408
  }, [isPolymorphic, value, activeRelation, isMultiple]);
5519
- React54.useEffect(() => {
5409
+ React56.useEffect(() => {
5520
5410
  const ids = extractIds();
5521
5411
  fetchSelectedDocs(ids);
5522
5412
  }, [extractIds, fetchSelectedDocs]);
5523
- const fetchOptions = React54.useCallback((query = "") => {
5413
+ const fetchOptions = React56.useCallback((query = "") => {
5524
5414
  setLoading(true);
5525
5415
  const searchFields = ["title", "name", "label", "email"];
5526
5416
  const url = `/api/${activeRelation}?${buildSearchQuery(query, searchFields)}`;
@@ -5537,13 +5427,13 @@ function RelationshipField({
5537
5427
  setLoading(false);
5538
5428
  });
5539
5429
  }, [activeRelation]);
5540
- React54.useEffect(() => {
5430
+ React56.useEffect(() => {
5541
5431
  if (isOpen) {
5542
5432
  setOptions([]);
5543
5433
  fetchOptions(search);
5544
5434
  }
5545
5435
  }, [isOpen, activeRelation]);
5546
- React54.useEffect(() => {
5436
+ React56.useEffect(() => {
5547
5437
  const handleClickOutside = (event) => {
5548
5438
  if (containerRef.current && !containerRef.current.contains(event.target)) {
5549
5439
  setIsOpen(false);
@@ -5668,7 +5558,7 @@ function RelationshipField({
5668
5558
  }
5669
5559
  ) })
5670
5560
  ] }),
5671
- isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-20 w-full mt-1 border border-[var(--kyro-border)] rounded-lg shadow-lg bg-[var(--kyro-surface)] max-h-64 overflow-auto", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-sm text-[var(--kyro-text-muted)]", children: "No results found" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
5561
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-20 w-full mt-1 border border-[var(--kyro-border)] rounded-lg shadow-lg bg-[var(--kyro-surface)] max-h-64 overflow-auto", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: "No results found" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
5672
5562
  "button",
5673
5563
  {
5674
5564
  type: "button",
@@ -5691,7 +5581,7 @@ function RelationshipField({
5691
5581
  ] });
5692
5582
  }
5693
5583
  var RelationshipField_default = RelationshipField;
5694
- var BlocksContext = React54.createContext(null);
5584
+ var BlocksContext = React56.createContext(null);
5695
5585
  function createBlocksStore(allowedBlocks = [], dynamicCategories = []) {
5696
5586
  return vanilla.createStore((set, get) => ({
5697
5587
  blocks: [],
@@ -5846,7 +5736,7 @@ function getDefaultData(type) {
5846
5736
  return defaults[type] || {};
5847
5737
  }
5848
5738
  function useBlockById(id) {
5849
- const store = React54.useContext(BlocksContext);
5739
+ const store = React56.useContext(BlocksContext);
5850
5740
  if (!store) return void 0;
5851
5741
  return zustand.useStore(store, (state) => {
5852
5742
  const findRecursive = (blocksList) => {
@@ -5871,7 +5761,7 @@ function useBlockById(id) {
5871
5761
  });
5872
5762
  }
5873
5763
  function useBlockActions() {
5874
- const store = React54.useContext(BlocksContext);
5764
+ const store = React56.useContext(BlocksContext);
5875
5765
  if (!store) {
5876
5766
  throw new Error("useBlockActions must be used within a BlocksContext.Provider");
5877
5767
  }
@@ -6255,7 +6145,7 @@ var ListField = ({
6255
6145
  onChange,
6256
6146
  compact = false
6257
6147
  }) => {
6258
- const [inputValue, setInputValue] = React54__default.default.useState("");
6148
+ const [inputValue, setInputValue] = React56__default.default.useState("");
6259
6149
  const handleAdd = () => {
6260
6150
  if (inputValue.trim()) {
6261
6151
  onChange([...items, inputValue.trim()]);
@@ -6511,7 +6401,7 @@ var AccordionField = ({
6511
6401
  onChange,
6512
6402
  compact = false
6513
6403
  }) => {
6514
- const [openIndex, setOpenIndex] = React54__default.default.useState(0);
6404
+ const [openIndex, setOpenIndex] = React56__default.default.useState(0);
6515
6405
  const handleTitleChange = (index, value) => {
6516
6406
  const newItems = [...items];
6517
6407
  newItems[index] = { ...newItems[index], title: value };
@@ -6937,7 +6827,7 @@ function ArrayLayout({
6937
6827
  const firstField = fields2[0];
6938
6828
  const labelField = firstField?.name || "user";
6939
6829
  const isRelationship = firstField?.type === "relationship";
6940
- const [openIndex, setOpenIndex] = React54__default.default.useState(0);
6830
+ const [openIndex, setOpenIndex] = React56__default.default.useState(0);
6941
6831
  function getItemLabel(item) {
6942
6832
  for (const key of ["label", "title", "name"]) {
6943
6833
  const val = item[key];
@@ -7292,7 +7182,7 @@ var FieldRenderer = ({
7292
7182
  {
7293
7183
  field: field3,
7294
7184
  value,
7295
- onChange,
7185
+ onChange: onChangeKeystroke,
7296
7186
  disabled,
7297
7187
  error
7298
7188
  }
@@ -7477,11 +7367,11 @@ var ChildBlocksTree = ({
7477
7367
  depth = 0,
7478
7368
  maxDepth = MAX_DEPTH
7479
7369
  }) => {
7480
- const [showAddModal, setShowAddModal] = React54.useState(false);
7481
- const [expandedIds, setExpandedIds] = React54.useState(/* @__PURE__ */ new Set());
7482
- const [editingBlockId, setEditingBlockId] = React54.useState(null);
7483
- const [confirmDeleteId, setConfirmDeleteId] = React54.useState(null);
7484
- const store = React54.useContext(BlocksContext);
7370
+ const [showAddModal, setShowAddModal] = React56.useState(false);
7371
+ const [expandedIds, setExpandedIds] = React56.useState(/* @__PURE__ */ new Set());
7372
+ const [editingBlockId, setEditingBlockId] = React56.useState(null);
7373
+ const [confirmDeleteId, setConfirmDeleteId] = React56.useState(null);
7374
+ const store = React56.useContext(BlocksContext);
7485
7375
  if (!store) throw new Error("ChildBlocksTree must be used within a BlocksContext");
7486
7376
  const dynamicCategories = zustand.useStore(store, (s) => s.dynamicCategories);
7487
7377
  const allowedBlocks = zustand.useStore(store, (s) => s.allowedBlocks);
@@ -7698,11 +7588,11 @@ var NestedChildBlocks = ({
7698
7588
  depth,
7699
7589
  maxDepth
7700
7590
  }) => {
7701
- const [showAddModal, setShowAddModal] = React54.useState(false);
7702
- const [expandedIds, setExpandedIds] = React54.useState(/* @__PURE__ */ new Set());
7703
- const [editingBlockId, setEditingBlockId] = React54.useState(null);
7704
- const [confirmDeleteId, setConfirmDeleteId] = React54.useState(null);
7705
- const store = React54.useContext(BlocksContext);
7591
+ const [showAddModal, setShowAddModal] = React56.useState(false);
7592
+ const [expandedIds, setExpandedIds] = React56.useState(/* @__PURE__ */ new Set());
7593
+ const [editingBlockId, setEditingBlockId] = React56.useState(null);
7594
+ const [confirmDeleteId, setConfirmDeleteId] = React56.useState(null);
7595
+ const store = React56.useContext(BlocksContext);
7706
7596
  if (!store) throw new Error("NestedChildBlocks must be used within a BlocksContext");
7707
7597
  const dynamicCategories = zustand.useStore(store, (s) => s.dynamicCategories);
7708
7598
  const allowedBlocks = zustand.useStore(store, (s) => s.allowedBlocks);
@@ -8206,13 +8096,13 @@ var RelationshipBlockField = ({
8206
8096
  onChange,
8207
8097
  compact = false
8208
8098
  }) => {
8209
- const [isOpen, setIsOpen] = React54.useState(false);
8210
- const [search, setSearch] = React54.useState("");
8211
- const [options, setOptions] = React54.useState([]);
8212
- const [loading, setLoading] = React54.useState(false);
8213
- const [collections2, setCollections] = React54.useState([]);
8214
- const [loadingCollections, setLoadingCollections] = React54.useState(true);
8215
- React54.useEffect(() => {
8099
+ const [isOpen, setIsOpen] = React56.useState(false);
8100
+ const [search, setSearch] = React56.useState("");
8101
+ const [options, setOptions] = React56.useState([]);
8102
+ const [loading, setLoading] = React56.useState(false);
8103
+ const [collections2, setCollections] = React56.useState([]);
8104
+ const [loadingCollections, setLoadingCollections] = React56.useState(true);
8105
+ React56.useEffect(() => {
8216
8106
  apiGet("/api/collections").then((data) => {
8217
8107
  setCollections(
8218
8108
  (data.collections || []).map((c) => c.slug || c.name || c)
@@ -8228,7 +8118,7 @@ var RelationshipBlockField = ({
8228
8118
  setLoading(false);
8229
8119
  }).catch(() => setLoading(false));
8230
8120
  };
8231
- React54.useEffect(() => {
8121
+ React56.useEffect(() => {
8232
8122
  if (isOpen) fetchOptions(search);
8233
8123
  }, [isOpen, search, relationTo, labelField]);
8234
8124
  const getLabel2 = (opt) => {
@@ -8302,7 +8192,7 @@ var RelationshipBlockField = ({
8302
8192
  ),
8303
8193
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-1/2 -translate-y-1/2", children: loading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LoaderCircle, { className: "w-4 h-4 text-[var(--kyro-text-muted)] animate-spin" }) })
8304
8194
  ] }),
8305
- isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-20 w-full mt-1 border border-[var(--kyro-border)] rounded-lg shadow-lg bg-[var(--kyro-surface)] max-h-48 overflow-auto", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 text-center text-sm text-[var(--kyro-text-muted)]", children: "No results found" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
8195
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-20 w-full mt-1 border border-[var(--kyro-border)] rounded-lg shadow-lg bg-[var(--kyro-surface)] max-h-48 overflow-auto", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: "No results found" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
8306
8196
  "button",
8307
8197
  {
8308
8198
  type: "button",
@@ -8558,7 +8448,7 @@ var SortableBlockComponent = ({
8558
8448
  } = sortable.useSortable({ id: block3.id });
8559
8449
  const { removeBlock } = useBlockActions();
8560
8450
  const isEditing = editingBlockId === block3.id;
8561
- const [showDeleteConfirm, setShowDeleteConfirm] = React54.useState(false);
8451
+ const [showDeleteConfirm, setShowDeleteConfirm] = React56.useState(false);
8562
8452
  const style = {
8563
8453
  transform: utilities.CSS.Transform.toString(transform),
8564
8454
  transition,
@@ -8752,7 +8642,7 @@ var SortableBlockComponent = ({
8752
8642
  )
8753
8643
  ] });
8754
8644
  };
8755
- var SortableBlock = React54__default.default.memo(SortableBlockComponent);
8645
+ var SortableBlock = React56__default.default.memo(SortableBlockComponent);
8756
8646
  var BlocksField = ({
8757
8647
  field: field3,
8758
8648
  value,
@@ -8763,9 +8653,9 @@ var BlocksField = ({
8763
8653
  documentStatus,
8764
8654
  justSaved
8765
8655
  }) => {
8766
- const [isDrawerOpen, setIsDrawerOpen] = React54.useState(false);
8767
- const [isDropdownOpen, setIsDropdownOpen] = React54.useState(false);
8768
- const dropdownRef = React54.useRef(null);
8656
+ const [isDrawerOpen, setIsDrawerOpen] = React56.useState(false);
8657
+ const [isDropdownOpen, setIsDropdownOpen] = React56.useState(false);
8658
+ const dropdownRef = React56.useRef(null);
8769
8659
  const pickerMode = field3.admin?.pickerMode || "drawer";
8770
8660
  const allowedBlocks = field3.blocks || [];
8771
8661
  const groupedBlocks = allowedBlocks.reduce((acc, block3) => {
@@ -8795,18 +8685,18 @@ var BlocksField = ({
8795
8685
  title,
8796
8686
  blocks: blocks3
8797
8687
  }));
8798
- const storeRef = React54.useRef(null);
8688
+ const storeRef = React56.useRef(null);
8799
8689
  if (!storeRef.current) {
8800
8690
  storeRef.current = createBlocksStore(allowedBlocks, dynamicCategories);
8801
8691
  }
8802
8692
  const store = storeRef.current;
8803
8693
  const blocks2 = zustand.useStore(store, (s) => s.blocks);
8804
- const [activeDrag, setActiveDrag] = React54.useState(null);
8805
- const [editingBlockId, setEditingBlockId] = React54.useState(null);
8806
- const prevBlocksLengthRef = React54.useRef(blocks2.length);
8807
- const prevBlockIdsRef = React54.useRef(new Set(blocks2.map((b) => b.id)));
8808
- const isInitializedRef = React54.useRef(false);
8809
- React54.useEffect(() => {
8694
+ const [activeDrag, setActiveDrag] = React56.useState(null);
8695
+ const [editingBlockId, setEditingBlockId] = React56.useState(null);
8696
+ const prevBlocksLengthRef = React56.useRef(blocks2.length);
8697
+ const prevBlockIdsRef = React56.useRef(new Set(blocks2.map((b) => b.id)));
8698
+ const isInitializedRef = React56.useRef(false);
8699
+ React56.useEffect(() => {
8810
8700
  if (isInitializedRef.current) {
8811
8701
  if (blocks2.length > prevBlocksLengthRef.current) {
8812
8702
  const newBlock = blocks2.find((b) => b.id && !prevBlockIdsRef.current.has(b.id));
@@ -8818,7 +8708,7 @@ var BlocksField = ({
8818
8708
  prevBlocksLengthRef.current = blocks2.length;
8819
8709
  prevBlockIdsRef.current = new Set(blocks2.map((b) => b.id));
8820
8710
  }, [blocks2]);
8821
- React54.useEffect(() => {
8711
+ React56.useEffect(() => {
8822
8712
  if (onBlocksChange) {
8823
8713
  store.getState().setOnBlocksChange(onBlocksChange);
8824
8714
  }
@@ -8827,14 +8717,11 @@ var BlocksField = ({
8827
8717
  });
8828
8718
  };
8829
8719
  }, [onBlocksChange, store]);
8830
- const lastValueRef = React54.useRef(null);
8831
- React54.useEffect(() => {
8720
+ const lastValueRef = React56.useRef(null);
8721
+ React56.useEffect(() => {
8832
8722
  const valueArray = Array.isArray(value) ? value : [];
8833
8723
  const lastValueArray = lastValueRef.current || [];
8834
- const valueIds = valueArray.map((b) => b.id).join(",");
8835
- const lastValueIds = lastValueArray.map((b) => b.id).join(",");
8836
- if (valueIds !== lastValueIds) {
8837
- console.log("BlocksField sync: value=", value, "valueIds=", valueIds, "lastValueIds=", lastValueIds);
8724
+ if (JSON.stringify(valueArray) !== JSON.stringify(lastValueArray)) {
8838
8725
  const valueArrayCopy = [...valueArray];
8839
8726
  prevBlocksLengthRef.current = valueArrayCopy.length;
8840
8727
  prevBlockIdsRef.current = new Set(valueArrayCopy.map((b) => b.id));
@@ -8843,27 +8730,27 @@ var BlocksField = ({
8843
8730
  isInitializedRef.current = true;
8844
8731
  } else if (valueArray.length === 0 && !isInitializedRef.current) {
8845
8732
  isInitializedRef.current = true;
8733
+ lastValueRef.current = [];
8846
8734
  }
8847
8735
  }, [value, field3.name, store]);
8848
- const onChangeRef = React54.useRef(onChange);
8736
+ const onChangeRef = React56.useRef(onChange);
8849
8737
  onChangeRef.current = onChange;
8850
- React54.useEffect(() => {
8738
+ React56.useEffect(() => {
8851
8739
  if (!onChangeRef.current) return;
8852
8740
  const lastValue = lastValueRef.current;
8853
8741
  if (!lastValue) return;
8854
- const currentIds = blocks2.map((b) => b.id).join(",");
8855
- const lastIds = lastValue.map((b) => b.id).join(",");
8856
- if (currentIds !== lastIds) {
8742
+ if (JSON.stringify(blocks2) !== JSON.stringify(lastValue)) {
8743
+ lastValueRef.current = [...blocks2];
8857
8744
  onChangeRef.current(blocks2);
8858
8745
  }
8859
8746
  }, [blocks2]);
8860
- const handleAddBlock = React54.useCallback(
8747
+ const handleAddBlock = React56.useCallback(
8861
8748
  (blockType) => {
8862
8749
  store.getState().addBlock(blockType);
8863
8750
  },
8864
8751
  [store]
8865
8752
  );
8866
- const duplicateBlock = React54.useCallback(
8753
+ const duplicateBlock = React56.useCallback(
8867
8754
  (blockId) => {
8868
8755
  const blockIndex = blocks2.findIndex((b) => b.id === blockId);
8869
8756
  if (blockIndex === -1) return;
@@ -8928,7 +8815,7 @@ var BlocksField = ({
8928
8815
  };
8929
8816
  const activeBlock = activeDrag ? blockCategories.flatMap((cat) => cat.blocks).find((b) => `drawer-${b.type}` === activeDrag.id) || blocks2.find((b) => b.id === activeDrag.id) : null;
8930
8817
  activeBlock ? "label" in activeBlock ? activeBlock.label : activeBlock.type : "Block";
8931
- React54.useEffect(() => {
8818
+ React56.useEffect(() => {
8932
8819
  if (!isDropdownOpen) return;
8933
8820
  const handleClick = (e) => {
8934
8821
  if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
@@ -9088,12 +8975,45 @@ function normalizeUploadFields(value) {
9088
8975
  }
9089
8976
  return value;
9090
8977
  }
9091
- function useAutoFormState({
9092
- config,
9093
- initialData,
9094
- collectionSlug,
9095
- globalSlug,
9096
- onChange,
8978
+ function useQueue() {
8979
+ const queue = React56.useRef([]);
8980
+ const isProcessing = React56.useRef(false);
8981
+ const queueTask = React56.useCallback((fn, options) => {
8982
+ queue.current.push(fn);
8983
+ async function processQueue() {
8984
+ if (isProcessing.current) return;
8985
+ if (typeof options?.beforeProcess === "function") {
8986
+ const shouldContinue = options.beforeProcess();
8987
+ if (shouldContinue === false) return;
8988
+ }
8989
+ while (queue.current.length > 0) {
8990
+ const latestTask = queue.current.pop();
8991
+ queue.current = [];
8992
+ isProcessing.current = true;
8993
+ try {
8994
+ await latestTask();
8995
+ } catch (err) {
8996
+ console.error("Error in queued function:", err);
8997
+ } finally {
8998
+ isProcessing.current = false;
8999
+ if (typeof options?.afterProcess === "function") {
9000
+ options.afterProcess();
9001
+ }
9002
+ }
9003
+ }
9004
+ }
9005
+ processQueue();
9006
+ }, []);
9007
+ return { queueTask };
9008
+ }
9009
+
9010
+ // src/hooks/useAutoFormState.ts
9011
+ function useAutoFormState({
9012
+ config,
9013
+ initialData,
9014
+ collectionSlug,
9015
+ globalSlug,
9016
+ onChange,
9097
9017
  onActionSuccess,
9098
9018
  onActionError
9099
9019
  }) {
@@ -9121,24 +9041,27 @@ function useAutoFormState({
9121
9041
  } = store;
9122
9042
  const versionsEnabled = !!config.versions;
9123
9043
  const currentContextKey = globalSlug || initialData?.id || collectionSlug;
9124
- const needsResetRef = React54.useRef(false);
9044
+ const needsResetRef = React56.useRef(false);
9125
9045
  if (!globalSlug && currentContextKey && formData && Object.keys(formData).length > 0 && formData.id !== currentContextKey) {
9126
9046
  needsResetRef.current = true;
9127
9047
  }
9128
- React54.useEffect(() => {
9048
+ React56.useEffect(() => {
9129
9049
  if (needsResetRef.current) {
9130
9050
  needsResetRef.current = false;
9131
9051
  resetForm();
9132
9052
  }
9133
9053
  }, [resetForm]);
9134
- const localSaveTimerRef = React54.useRef(null);
9135
- const serverSaveTimerRef = React54.useRef(null);
9136
- const retryTimerRef = React54.useRef(null);
9137
- const isOnlineRef = React54.useRef(typeof navigator !== "undefined" ? navigator.onLine : true);
9138
- const lastAutoSaveTimeRef = React54.useRef(0);
9139
- const autoSaveSkipRef = React54.useRef(false);
9140
- const restorePromptedRef = React54.useRef(null);
9141
- const getDocumentKey = React54.useCallback(
9054
+ const localSaveTimerRef = React56.useRef(null);
9055
+ const serverSaveTimerRef = React56.useRef(null);
9056
+ const retryTimerRef = React56.useRef(null);
9057
+ const isOnlineRef = React56.useRef(typeof navigator !== "undefined" ? navigator.onLine : true);
9058
+ const lastAutoSaveTimeRef = React56.useRef(0);
9059
+ const autoSaveSkipRef = React56.useRef(false);
9060
+ const restorePromptedRef = React56.useRef(null);
9061
+ const previousFormDataRef = React56.useRef("");
9062
+ const astroSyncDataRef = React56.useRef("");
9063
+ const { queueTask } = useQueue();
9064
+ const getDocumentKey = React56.useCallback(
9142
9065
  (id) => {
9143
9066
  if (globalSlug) return `global:${globalSlug}`;
9144
9067
  if (collectionSlug && id) return `${collectionSlug}:${id}`;
@@ -9146,7 +9069,7 @@ function useAutoFormState({
9146
9069
  },
9147
9070
  [collectionSlug, globalSlug]
9148
9071
  );
9149
- const persistBrowserDraft = React54.useCallback(
9072
+ const persistBrowserDraft = React56.useCallback(
9150
9073
  (documentKey, data, options) => {
9151
9074
  setDraftCache(documentKey, {
9152
9075
  data,
@@ -9157,24 +9080,7 @@ function useAutoFormState({
9157
9080
  },
9158
9081
  [lastSavedData.updatedAt, setDraftCache]
9159
9082
  );
9160
- const clearDraftArtifacts = React54.useCallback(async () => {
9161
- const state = useAutoFormStore.getState();
9162
- const documentKey = getDocumentKey(state.formData.id);
9163
- if (documentKey) {
9164
- clearDraftCache(documentKey);
9165
- }
9166
- const draftUrl = globalSlug ? resolveApi(`/api/globals/${globalSlug}/draft`) : collectionSlug && state.formData.id ? resolveApi(`/api/${collectionSlug}/${state.formData.id}/draft`) : null;
9167
- if (draftUrl && versionsEnabled) {
9168
- try {
9169
- await fetchWithAuth(draftUrl, {
9170
- method: "DELETE"
9171
- });
9172
- } catch (err) {
9173
- console.error("Failed to clear draft snapshot:", err);
9174
- }
9175
- }
9176
- }, [clearDraftCache, collectionSlug, globalSlug, getDocumentKey]);
9177
- const fetchVersions = React54.useCallback(async () => {
9083
+ const fetchVersions = React56.useCallback(async () => {
9178
9084
  const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}/versions`) : collectionSlug && formData.id ? resolveApi(`/api/${collectionSlug}/${formData.id}/versions`) : null;
9179
9085
  if (!url) return;
9180
9086
  setLoadingVersions(true);
@@ -9188,7 +9094,7 @@ function useAutoFormState({
9188
9094
  setLoadingVersions(false);
9189
9095
  }
9190
9096
  }, [formData.id, collectionSlug, globalSlug, setLoadingVersions, setVersions]);
9191
- const performLocalAutoSave = React54.useCallback(() => {
9097
+ const performLocalAutoSave = React56.useCallback(() => {
9192
9098
  const state = useAutoFormStore.getState();
9193
9099
  const latestFormData = state.formData;
9194
9100
  if (autoSaveSkipRef.current || !collectionSlug || !latestFormData.id) return;
@@ -9198,7 +9104,7 @@ function useAutoFormState({
9198
9104
  persistBrowserDraft(documentKey, latestFormData);
9199
9105
  }
9200
9106
  }, [collectionSlug, getDocumentKey, persistBrowserDraft]);
9201
- const performServerAutoSave = React54.useCallback(async (options) => {
9107
+ const doAutosaveFetch = React56.useCallback(async (options) => {
9202
9108
  const state = useAutoFormStore.getState();
9203
9109
  const latestFormData = state.formData;
9204
9110
  const currentLastSaved = state.lastSavedData;
@@ -9215,29 +9121,22 @@ function useAutoFormState({
9215
9121
  }
9216
9122
  setIsAutoSaving(true);
9217
9123
  setAutoSaveStatus("saving");
9124
+ state.setBackgroundProcessing(true);
9218
9125
  try {
9219
- const draftUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
9220
- const draftUrl = globalSlug ? resolveApi(`/api/globals/${globalSlug}/draft`) : resolveApi(`/api/${collectionSlug}/${latestFormData.id}/draft`);
9221
- const delta = state.getDirtyData();
9222
- const idempotencyKey = `${documentKey || "global"}:${Date.now()}`;
9223
- const response = await fetchWithAuth(
9224
- draftUrl,
9225
- {
9226
- method: "PUT",
9227
- headers: {
9228
- "Content-Type": "application/json",
9229
- "X-Idempotency-Key": idempotencyKey
9230
- },
9231
- keepalive: options?.keepalive,
9232
- body: JSON.stringify({
9233
- delta: normalizeUploadFields(delta),
9234
- baseUpdatedAt: currentLastSaved.updatedAt ?? null,
9235
- draftUpdatedAt
9236
- })
9237
- }
9238
- );
9126
+ const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}?autosave=true`) : resolveApi(`/api/${collectionSlug}/${latestFormData.id}?autosave=true`);
9127
+ const response = await fetchWithAuth(url, {
9128
+ method: "PATCH",
9129
+ headers: {
9130
+ "Content-Type": "application/json",
9131
+ "X-Draft": "true"
9132
+ },
9133
+ keepalive: options?.keepalive,
9134
+ body: JSON.stringify({
9135
+ ...normalizeUploadFields(latestFormData),
9136
+ baseUpdatedAt: currentLastSaved.updatedAt ?? null
9137
+ })
9138
+ });
9239
9139
  if (response.ok) {
9240
- const result = await response.json();
9241
9140
  lastAutoSaveTimeRef.current = Date.now();
9242
9141
  state.setRetryCount(0);
9243
9142
  state.setLastSavedAt(Date.now());
@@ -9246,8 +9145,8 @@ function useAutoFormState({
9246
9145
  setDraftCache(documentKey, {
9247
9146
  data: latestFormData,
9248
9147
  baseUpdatedAt: currentLastSaved.updatedAt ?? null,
9249
- draftUpdatedAt: result.data?.draftUpdatedAt || draftUpdatedAt,
9250
- lastSyncedAt: result.data?.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
9148
+ draftUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9149
+ lastSyncedAt: (await response.clone().json()).data?.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
9251
9150
  });
9252
9151
  }
9253
9152
  setAutoSaveStatus("success");
@@ -9256,24 +9155,27 @@ function useAutoFormState({
9256
9155
  setAutoSaveStatus("idle");
9257
9156
  }
9258
9157
  }, 2e3);
9158
+ } else if (response.status === 409) {
9159
+ setAutoSaveStatus("conflict");
9259
9160
  } else {
9260
9161
  throw new Error(`Draft auto-save failed with status ${response.status}`);
9261
9162
  }
9262
9163
  } catch (err) {
9263
9164
  console.error("Auto-save failed:", err);
9264
- const state2 = useAutoFormStore.getState();
9265
- const currentRetryCount = state2.retryCount;
9165
+ const currentState = useAutoFormStore.getState();
9166
+ const currentRetryCount = currentState.retryCount;
9266
9167
  if (currentRetryCount < 5) {
9267
- state2.setRetryCount(currentRetryCount + 1);
9168
+ currentState.setRetryCount(currentRetryCount + 1);
9268
9169
  setAutoSaveStatus("retrying");
9269
9170
  const delay = Math.min(1e3 * Math.pow(2, currentRetryCount), 6e4);
9270
9171
  if (retryTimerRef.current) clearTimeout(retryTimerRef.current);
9271
- retryTimerRef.current = setTimeout(() => performServerAutoSave(options), delay);
9172
+ retryTimerRef.current = setTimeout(() => performAutosave(options), delay);
9272
9173
  } else {
9273
9174
  setAutoSaveStatus("offline");
9274
9175
  }
9275
9176
  } finally {
9276
9177
  setIsAutoSaving(false);
9178
+ useAutoFormStore.getState().setBackgroundProcessing(false);
9277
9179
  }
9278
9180
  }, [
9279
9181
  collectionSlug,
@@ -9285,8 +9187,20 @@ function useAutoFormState({
9285
9187
  setIsAutoSaving,
9286
9188
  versionsEnabled
9287
9189
  ]);
9288
- const saveDocument = React54.useCallback(
9289
- async (dataOverride) => {
9190
+ const performAutosave = React56.useCallback((options) => {
9191
+ queueTask(
9192
+ () => doAutosaveFetch(options),
9193
+ {
9194
+ beforeProcess: () => {
9195
+ return true;
9196
+ },
9197
+ afterProcess: () => {
9198
+ }
9199
+ }
9200
+ );
9201
+ }, [doAutosaveFetch, queueTask]);
9202
+ const saveDocument = React56.useCallback(
9203
+ async (dataOverride, isDraft = true) => {
9290
9204
  const state = useAutoFormStore.getState();
9291
9205
  const payload = dataOverride || state.formData;
9292
9206
  const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}`) : resolveApi(`/api/${collectionSlug}/${payload.id}`);
@@ -9294,7 +9208,10 @@ function useAutoFormState({
9294
9208
  url,
9295
9209
  {
9296
9210
  method: "PATCH",
9297
- headers: { "Content-Type": "application/json" },
9211
+ headers: {
9212
+ "Content-Type": "application/json",
9213
+ "X-Draft": String(isDraft)
9214
+ },
9298
9215
  body: JSON.stringify({
9299
9216
  ...normalizeUploadFields(payload),
9300
9217
  baseUpdatedAt: state.lastSavedData.updatedAt ?? null
@@ -9308,53 +9225,26 @@ function useAutoFormState({
9308
9225
  },
9309
9226
  [collectionSlug, globalSlug, setAutoSaveStatus]
9310
9227
  );
9311
- const publishDocument = React54.useCallback(async () => {
9312
- const state = useAutoFormStore.getState();
9313
- const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}/publish`) : resolveApi(`/api/${collectionSlug}/${state.formData.id}/publish`);
9314
- const response = await fetchWithAuth(
9315
- url,
9316
- {
9317
- method: "POST",
9318
- headers: { "Content-Type": "application/json" },
9319
- body: JSON.stringify({
9320
- baseUpdatedAt: state.lastSavedData.updatedAt ?? null
9321
- })
9322
- }
9323
- );
9324
- if (response.status === 409) {
9325
- setAutoSaveStatus("conflict");
9326
- }
9327
- return response;
9328
- }, [collectionSlug, globalSlug, setAutoSaveStatus]);
9329
- const unpublishDocument = React54.useCallback(async () => {
9330
- const state = useAutoFormStore.getState();
9331
- const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}/unpublish`) : resolveApi(`/api/${collectionSlug}/${state.formData.id}/unpublish`);
9332
- const response = await fetchWithAuth(
9333
- url,
9334
- {
9335
- method: "POST",
9336
- headers: { "Content-Type": "application/json" }
9337
- }
9338
- );
9339
- return response;
9340
- }, [collectionSlug, globalSlug]);
9341
- React54.useEffect(() => {
9228
+ React56.useEffect(() => {
9342
9229
  const handleToggle = () => {
9343
9230
  setSidebarCollapsed(!sidebarCollapsed);
9344
9231
  };
9345
9232
  window.addEventListener("toggle-sidebar", handleToggle);
9346
9233
  return () => window.removeEventListener("toggle-sidebar", handleToggle);
9347
9234
  }, [sidebarCollapsed, setSidebarCollapsed]);
9348
- const lastLoadedSlugRef = React54.useRef(null);
9349
- const initialDataLoadedRef = React54.useRef(false);
9350
- React54.useEffect(() => {
9235
+ const lastLoadedSlugRef = React56.useRef(null);
9236
+ const lastInitialDataRef = React56.useRef("");
9237
+ const initialDataLoadedRef = React56.useRef(false);
9238
+ React56.useEffect(() => {
9351
9239
  const currentSlug = globalSlug || initialData?.id;
9352
- if (initialDataLoadedRef.current && lastLoadedSlugRef.current === currentSlug) return;
9240
+ const serialized = JSON.stringify(initialData);
9241
+ if (initialDataLoadedRef.current && lastLoadedSlugRef.current === currentSlug && lastInitialDataRef.current === serialized) return;
9353
9242
  loadDocument(initialData || {}, initialData || {});
9354
9243
  initialDataLoadedRef.current = true;
9355
9244
  lastLoadedSlugRef.current = currentSlug;
9245
+ lastInitialDataRef.current = serialized;
9356
9246
  }, [collectionSlug, formData.id, globalSlug, initialData, loadDocument]);
9357
- React54.useEffect(() => {
9247
+ React56.useEffect(() => {
9358
9248
  if (!collectionSlug || !initialData?.id) return;
9359
9249
  const documentKey = getDocumentKey(initialData.id);
9360
9250
  if (!documentKey) return;
@@ -9363,24 +9253,9 @@ function useAutoFormState({
9363
9253
  const maybeRestoreDraft = async () => {
9364
9254
  if (!versionsEnabled) return;
9365
9255
  const browserDraft = getDraftCache(documentKey);
9366
- let serverDraft = null;
9367
- try {
9368
- const response = await fetchWithAuth(
9369
- resolveApi(`/api/${collectionSlug}/${initialData.id}/draft`)
9370
- );
9371
- if (response.ok) {
9372
- const result = await response.json();
9373
- serverDraft = result.data || null;
9374
- }
9375
- } catch (err) {
9376
- console.error("Failed to fetch server draft:", err);
9377
- }
9378
- const drafts = [browserDraft, serverDraft].filter(Boolean);
9379
- const candidate = drafts.sort(
9380
- (a, b) => new Date(b.draftUpdatedAt).getTime() - new Date(a.draftUpdatedAt).getTime()
9381
- )[0];
9382
- if (!candidate) return;
9383
- if (JSON.stringify(candidate.data) === JSON.stringify(initialData)) {
9256
+ if (!browserDraft) return;
9257
+ if (JSON.stringify(browserDraft.data) === JSON.stringify(initialData)) {
9258
+ clearDraftCache(documentKey);
9384
9259
  return;
9385
9260
  }
9386
9261
  restorePromptedRef.current = documentKey;
@@ -9392,20 +9267,12 @@ function useAutoFormState({
9392
9267
  onConfirm: async () => {
9393
9268
  if (cancelled) return;
9394
9269
  const currentFormData = useAutoFormStore.getState().formData;
9395
- const mergedData = { ...currentFormData, ...candidate.data };
9270
+ const mergedData = { ...currentFormData, ...browserDraft.data };
9396
9271
  setFormData(mergedData);
9397
9272
  onActionSuccess?.("Recovered autosaved draft");
9398
9273
  },
9399
9274
  onCancel: async () => {
9400
9275
  clearDraftCache(documentKey);
9401
- try {
9402
- await fetchWithAuth(
9403
- resolveApi(`/api/${collectionSlug}/${initialData.id}/draft`),
9404
- { method: "DELETE" }
9405
- );
9406
- } catch (err) {
9407
- console.error("Failed to discard server draft:", err);
9408
- }
9409
9276
  }
9410
9277
  });
9411
9278
  };
@@ -9421,7 +9288,8 @@ function useAutoFormState({
9421
9288
  getDraftCache,
9422
9289
  initialData,
9423
9290
  onActionSuccess,
9424
- setFormData
9291
+ setFormData,
9292
+ versionsEnabled
9425
9293
  ]);
9426
9294
  function findFieldDeep(fields2, name) {
9427
9295
  for (const f of fields2) {
@@ -9439,7 +9307,7 @@ function useAutoFormState({
9439
9307
  }
9440
9308
  return void 0;
9441
9309
  }
9442
- React54.useEffect(() => {
9310
+ React56.useEffect(() => {
9443
9311
  const fields2 = config.fields;
9444
9312
  const metaTitleField = findFieldDeep(fields2, "metaTitle");
9445
9313
  if (!metaTitleField) return;
@@ -9449,7 +9317,7 @@ function useAutoFormState({
9449
9317
  setField("metaTitle", titleStr);
9450
9318
  }
9451
9319
  }, [formData, config.fields, setField]);
9452
- React54.useEffect(() => {
9320
+ React56.useEffect(() => {
9453
9321
  const fields2 = config.fields;
9454
9322
  const slugField = fields2.find(
9455
9323
  (f) => f.name === "slug" && f.admin?.autoGenerate
@@ -9464,25 +9332,30 @@ function useAutoFormState({
9464
9332
  }
9465
9333
  }
9466
9334
  }, [formData, isSlugLocked, config.fields, setField]);
9467
- React54.useEffect(() => {
9335
+ React56.useEffect(() => {
9468
9336
  if (sidebarCollapsed) return;
9469
9337
  if (!globalSlug && (!collectionSlug || !formData.id)) return;
9470
9338
  const state = useAutoFormStore.getState();
9471
9339
  if (!state.hasDirtyFields()) return;
9472
9340
  if (getLastChangeSource() !== "keystroke") return;
9473
9341
  setChangeSource("other");
9342
+ const serialized = JSON.stringify(formData);
9343
+ if (serialized === previousFormDataRef.current) return;
9474
9344
  if (localSaveTimerRef.current) clearTimeout(localSaveTimerRef.current);
9475
- if (serverSaveTimerRef.current) clearTimeout(serverSaveTimerRef.current);
9476
9345
  localSaveTimerRef.current = setTimeout(performLocalAutoSave, 1500);
9477
- serverSaveTimerRef.current = setTimeout(performServerAutoSave, 8e3);
9478
- }, [formData, sidebarCollapsed, collectionSlug, globalSlug, performLocalAutoSave, performServerAutoSave]);
9479
- React54.useEffect(() => {
9346
+ if (serverSaveTimerRef.current) clearTimeout(serverSaveTimerRef.current);
9347
+ serverSaveTimerRef.current = setTimeout(() => {
9348
+ previousFormDataRef.current = serialized;
9349
+ performAutosave();
9350
+ }, 8e3);
9351
+ }, [formData, sidebarCollapsed, collectionSlug, globalSlug, performLocalAutoSave, performAutosave]);
9352
+ React56.useEffect(() => {
9480
9353
  if (!globalSlug && (!collectionSlug || !formData.id)) return;
9481
9354
  const flushDraft = () => {
9482
9355
  if (autoSaveSkipRef.current) return;
9483
9356
  const state = useAutoFormStore.getState();
9484
9357
  if (!state.hasDirtyFields()) return;
9485
- void performServerAutoSave({ keepalive: true });
9358
+ void performAutosave({ keepalive: true });
9486
9359
  };
9487
9360
  const handleVisibilityChange = () => {
9488
9361
  if (document.hidden) {
@@ -9512,32 +9385,31 @@ function useAutoFormState({
9512
9385
  window.removeEventListener("offline", handleOffline);
9513
9386
  document.removeEventListener("visibilitychange", handleVisibilityChange);
9514
9387
  };
9515
- }, [collectionSlug, globalSlug, formData.id, performServerAutoSave]);
9516
- React54.useEffect(() => {
9388
+ }, [collectionSlug, globalSlug, formData.id, performAutosave]);
9389
+ React56.useEffect(() => {
9390
+ const serialized = JSON.stringify(formData);
9391
+ if (serialized === astroSyncDataRef.current) return;
9392
+ astroSyncDataRef.current = serialized;
9517
9393
  const hiddenInput = document.getElementById("form-data");
9518
9394
  if (hiddenInput) {
9519
- hiddenInput.value = JSON.stringify(formData);
9395
+ hiddenInput.value = serialized;
9520
9396
  }
9521
9397
  onChange?.(formData);
9522
9398
  }, [formData, onChange]);
9523
- React54.useEffect(() => {
9399
+ React56.useEffect(() => {
9524
9400
  if (globalSlug || formData.id) fetchVersions();
9525
9401
  }, [formData.id, globalSlug, fetchVersions]);
9526
9402
  const documentStatus = (() => {
9527
9403
  if (!formData.id && !globalSlug) return "draft";
9528
9404
  if (!versionsEnabled) return "published";
9529
- if (formData.hasDraft) return "draft";
9530
- return formData.publishStatus || "published";
9405
+ return formData.status || "published";
9531
9406
  })();
9532
- const hasUnpublishedChanges = (!!formData.id || !!globalSlug) && (documentStatus !== "published" || !!formData.hasDraft);
9407
+ const hasUnpublishedChanges = (!!formData.id || !!globalSlug) && documentStatus === "draft";
9533
9408
  return {
9534
9409
  ...store,
9535
9410
  fetchVersions,
9536
- performAutoSave: performServerAutoSave,
9411
+ performAutoSave: performAutosave,
9537
9412
  saveDocument,
9538
- publishDocument,
9539
- unpublishDocument,
9540
- clearDraftArtifacts,
9541
9413
  autoSaveSkipRef,
9542
9414
  lastAutoSaveTimeRef,
9543
9415
  documentStatus,
@@ -9545,129 +9417,120 @@ function useAutoFormState({
9545
9417
  versionsEnabled
9546
9418
  };
9547
9419
  }
9548
- function ModalContent({ children }) {
9549
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[var(--kyro-text-secondary)]", children });
9550
- }
9551
- function ModalActions({ children }) {
9552
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-3 mt-6", children });
9553
- }
9554
- function Modal({
9555
- open,
9556
- onClose,
9557
- title,
9420
+ function Dropdown({
9421
+ trigger,
9558
9422
  children,
9559
- footer,
9560
- size = "md",
9561
- variant = "default"
9423
+ align = "right",
9424
+ direction = "up"
9562
9425
  }) {
9563
- React54.useEffect(() => {
9564
- const handleEscape = (e) => {
9565
- if (e.key === "Escape") onClose();
9426
+ const [open, setOpen] = React56.useState(false);
9427
+ const ref = React56.useRef(null);
9428
+ React56.useEffect(() => {
9429
+ const handleClickOutside = (e) => {
9430
+ if (ref.current && !ref.current.contains(e.target)) {
9431
+ setOpen(false);
9432
+ }
9566
9433
  };
9567
9434
  if (open) {
9568
- document.addEventListener("keydown", handleEscape);
9569
- document.body.style.overflow = "hidden";
9435
+ document.addEventListener("mousedown", handleClickOutside);
9570
9436
  }
9571
- return () => {
9572
- document.removeEventListener("keydown", handleEscape);
9573
- document.body.style.overflow = "";
9574
- };
9575
- }, [open, onClose]);
9576
- if (!open) return null;
9577
- const sizeClasses = {
9578
- sm: "max-w-sm",
9579
- md: "max-w-md",
9580
- lg: "max-w-lg"
9581
- };
9582
- return reactDom.createPortal(
9583
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center p-4", children: [
9584
- /* @__PURE__ */ jsxRuntime.jsx(
9585
- "div",
9586
- {
9587
- className: "absolute inset-0 bg-[var(--kyro-black)]/40 backdrop-blur-md transition-all duration-500",
9588
- onClick: onClose
9589
- }
9590
- ),
9591
- /* @__PURE__ */ jsxRuntime.jsxs(
9592
- "div",
9593
- {
9594
- className: `relative w-full ${sizeClasses[size]} bg-[var(--kyro-surface)] rounded-[var(--kyro-radius-lg)] shadow-2xl animate-in fade-in zoom-in-95 duration-300 border ${variant === "danger" ? "border-red-500/30" : "border-[var(--kyro-border)]"} overflow-hidden`,
9595
- children: [
9596
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-8 py-6 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]/50 backdrop-blur-md", children: [
9597
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-bold text-[var(--kyro-text-primary)]", children: title }),
9598
- /* @__PURE__ */ jsxRuntime.jsx(
9599
- "button",
9600
- {
9601
- type: "button",
9602
- onClick: onClose,
9603
- className: "p-2 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] rounded-xl hover:bg-[var(--kyro-surface)] transition-all duration-200",
9604
- children: /* @__PURE__ */ jsxRuntime.jsx(
9605
- "svg",
9606
- {
9607
- width: "20",
9608
- height: "20",
9609
- viewBox: "0 0 24 24",
9610
- fill: "none",
9611
- stroke: "currentColor",
9612
- strokeWidth: "2.5",
9613
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
9614
- }
9615
- )
9616
- }
9617
- )
9618
- ] }),
9619
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-8 py-8", children }),
9620
- footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-3 px-8 py-6 border-t border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]/50", children: footer })
9621
- ]
9622
- }
9623
- )
9624
- ] }),
9625
- document.body
9626
- );
9437
+ return () => document.removeEventListener("mousedown", handleClickOutside);
9438
+ }, [open]);
9439
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref, children: [
9440
+ /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: () => setOpen(!open), className: "cursor-pointer", children: trigger }),
9441
+ open && /* @__PURE__ */ jsxRuntime.jsx(
9442
+ "div",
9443
+ {
9444
+ className: `absolute z-[100] min-w-[200px] py-2 bg-[var(--kyro-surface)] rounded-2xl shadow-2xl border border-[var(--kyro-border)] animate-in fade-in zoom-in-95 duration-100 ${align === "right" ? "right-0" : "left-0"} ${direction === "down" ? "top-full mt-2" : "bottom-full mb-2"}`,
9445
+ onClick: () => setOpen(false),
9446
+ children
9447
+ }
9448
+ )
9449
+ ] });
9627
9450
  }
9628
- function ConfirmModal({
9629
- open,
9630
- onClose,
9631
- onConfirm,
9632
- title,
9633
- message,
9634
- confirmLabel = "Confirm",
9635
- cancelLabel = "Cancel",
9636
- variant = "default",
9637
- loading = false
9451
+ function DropdownItem({
9452
+ children,
9453
+ onClick,
9454
+ icon,
9455
+ danger,
9456
+ disabled,
9457
+ className = ""
9638
9458
  }) {
9639
- return /* @__PURE__ */ jsxRuntime.jsx(
9640
- Modal,
9459
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1.5", children: /* @__PURE__ */ jsxRuntime.jsxs(
9460
+ "button",
9641
9461
  {
9642
- open,
9643
- onClose,
9644
- title,
9645
- size: "sm",
9646
- footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9647
- /* @__PURE__ */ jsxRuntime.jsx(
9648
- "button",
9649
- {
9650
- type: "button",
9651
- onClick: onClose,
9652
- disabled: loading,
9653
- className: "kyro-btn kyro-btn-md kyro-btn-secondary",
9654
- children: cancelLabel
9655
- }
9656
- ),
9657
- /* @__PURE__ */ jsxRuntime.jsx(
9658
- "button",
9659
- {
9660
- type: "button",
9661
- onClick: onConfirm,
9662
- disabled: loading,
9663
- className: `kyro-btn kyro-btn-md ${variant === "danger" ? "kyro-btn-danger" : "kyro-btn-primary"}`,
9664
- children: loading ? "Loading..." : confirmLabel
9665
- }
9666
- )
9667
- ] }),
9668
- children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[var(--kyro-text-secondary)]", children: message })
9462
+ type: "button",
9463
+ onClick,
9464
+ disabled,
9465
+ className: `w-full flex items-center gap-3 px-3 py-2.5 text-[11px] font-medium tracking-wide text-left transition-all rounded-xl ${danger ? "text-red-500 hover:bg-red-500/10" : "text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"} ${disabled ? "opacity-50 cursor-not-allowed" : ""} ${className}`,
9466
+ children: [
9467
+ icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 opacity-70", children: icon }),
9468
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children })
9469
+ ]
9669
9470
  }
9670
- );
9471
+ ) });
9472
+ }
9473
+ function DropdownSeparator() {
9474
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--kyro-border)] opacity-50" });
9475
+ }
9476
+ function SplitButton({
9477
+ status,
9478
+ saveStatus,
9479
+ hasChanges,
9480
+ onPublish,
9481
+ children,
9482
+ disabled,
9483
+ direction = "down"
9484
+ }) {
9485
+ const isPublishedIdle = status === "published" && !hasChanges && saveStatus !== "saving" && saveStatus !== "error";
9486
+ const isDisabled = disabled || saveStatus === "saving" || isPublishedIdle;
9487
+ const btnBase = "kyro-btn kyro-btn-sm text-[11px] font-regular tracking-widest transition-all duration-300 rounded-lg";
9488
+ const getBtnClass = () => {
9489
+ if (saveStatus === "saving") return `${btnBase} bg-[var(--kyro-primary)]/70 border-[var(--kyro-primary)]/70 text-[var(--kyro-sidebar-text-active)] cursor-wait`;
9490
+ if (saveStatus === "saved") return `${btnBase} bg-[var(--kyro-success)] border-[var(--kyro-success)] text-[var(--kyro-sidebar-text-active)]`;
9491
+ if (saveStatus === "error") return `${btnBase} bg-[var(--kyro-error)] border-[var(--kyro-error)] text-[var(--kyro-sidebar-text-active)]`;
9492
+ if (isPublishedIdle) return `${btnBase} bg-[var(--kyro-gray-200)] border-[var(--kyro-gray-200)] text-[var(--kyro-text-muted)] cursor-not-allowed`;
9493
+ return `${btnBase} bg-[var(--kyro-primary)] border-[var(--kyro-primary)] hover:bg-[var(--kyro-primary-hover)]`;
9494
+ };
9495
+ const chevronBase = "kyro-btn kyro-btn-md px-2 rounded-l-none border-l-[1px] border-white/20 transition-all duration-300";
9496
+ const getChevronClass = () => {
9497
+ if (saveStatus === "saving") return `${chevronBase} bg-[var(--kyro-primary)]/70 text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-primary)]/70`;
9498
+ if (saveStatus === "saved") return `${chevronBase} bg-[var(--kyro-success)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-success)]`;
9499
+ if (saveStatus === "error") return `${chevronBase} bg-[var(--kyro-error)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-error)]`;
9500
+ if (isPublishedIdle) return `${chevronBase} bg-[var(--kyro-gray-200)] text-[var(--kyro-text-muted)] border-[var(--kyro-gray-200)]`;
9501
+ return `${chevronBase} bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-primary)] hover:bg-[var(--kyro-primary-hover)]`;
9502
+ };
9503
+ const getLabel2 = () => {
9504
+ if (saveStatus === "saving") return "Publishing...";
9505
+ if (saveStatus === "saved") return "Published \u2713";
9506
+ if (saveStatus === "error") return "Retry";
9507
+ if (isPublishedIdle) return "Published";
9508
+ return "Publish Changes";
9509
+ };
9510
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center", children: [
9511
+ /* @__PURE__ */ jsxRuntime.jsxs(
9512
+ "button",
9513
+ {
9514
+ type: "button",
9515
+ onClick: onPublish,
9516
+ disabled: isDisabled,
9517
+ className: `${getBtnClass()} ${!children ? "rounded-r-lg border-r border-[var(--kyro-border)]" : ""}`,
9518
+ children: [
9519
+ saveStatus === "saving" && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm", className: "inline mr-1.5" }),
9520
+ isPublishedIdle && /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", className: "inline mr-1", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }),
9521
+ getLabel2()
9522
+ ]
9523
+ }
9524
+ ),
9525
+ children && /* @__PURE__ */ jsxRuntime.jsx(
9526
+ Dropdown,
9527
+ {
9528
+ trigger: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: getChevronClass(), disabled: saveStatus === "saving", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) }) }),
9529
+ direction,
9530
+ children
9531
+ }
9532
+ )
9533
+ ] });
9671
9534
  }
9672
9535
  var SeoPreview = ({ title, description, slug }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-lg p-6 max-w-2xl shadow-sm transition-colors duration-300", children: [
9673
9536
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
@@ -9689,7 +9552,7 @@ function TabsLayout({
9689
9552
  onTabDataChange,
9690
9553
  renderField
9691
9554
  }) {
9692
- const [activeTab, setActiveTab] = React54.useState(0);
9555
+ const [activeTab, setActiveTab] = React56.useState(0);
9693
9556
  const fieldTabs = field3.tabs || [];
9694
9557
  const currentTab = fieldTabs[activeTab] || fieldTabs[0];
9695
9558
  const tabData = field3.name ? formData[field3.name] || {} : formData;
@@ -9776,8 +9639,6 @@ function AutoForm({
9776
9639
  setAutoSaveStatus,
9777
9640
  fetchVersions,
9778
9641
  saveDocument,
9779
- publishDocument,
9780
- clearDraftArtifacts,
9781
9642
  autoSaveSkipRef,
9782
9643
  lastAutoSaveTimeRef,
9783
9644
  documentStatus,
@@ -9792,10 +9653,16 @@ function AutoForm({
9792
9653
  onActionSuccess,
9793
9654
  onActionError
9794
9655
  });
9795
- const menuRef = React54.useRef(null);
9796
- const scheduleRef = React54.useRef(null);
9797
- const [showSchedulePicker, setShowSchedulePicker] = React54.useState(false);
9656
+ const menuRef = React56.useRef(null);
9657
+ const scheduleRef = React56.useRef(null);
9658
+ const [showSchedulePicker, setShowSchedulePicker] = React56.useState(false);
9659
+ const [localSaveStatus, setLocalSaveStatus] = React56.useState("idle");
9660
+ const [now, setNow] = React56.useState(Date.now());
9798
9661
  const disabled = propDisabled;
9662
+ React56.useEffect(() => {
9663
+ const id = setInterval(() => setNow(Date.now()), 1e4);
9664
+ return () => clearInterval(id);
9665
+ }, []);
9799
9666
  const resolveAdminFlag = (value, currentData) => {
9800
9667
  if (typeof value === "function") {
9801
9668
  try {
@@ -9870,7 +9737,7 @@ function AutoForm({
9870
9737
  return [...prev, versionId];
9871
9738
  });
9872
9739
  };
9873
- React54.useEffect(() => {
9740
+ React56.useEffect(() => {
9874
9741
  const handleShortcuts = (e) => {
9875
9742
  if ((e.metaKey || e.ctrlKey) && e.key === "s") {
9876
9743
  e.preventDefault();
@@ -9893,12 +9760,12 @@ function AutoForm({
9893
9760
  window.addEventListener("keydown", handleShortcuts);
9894
9761
  return () => window.removeEventListener("keydown", handleShortcuts);
9895
9762
  }, []);
9896
- React54.useEffect(() => {
9763
+ React56.useEffect(() => {
9897
9764
  const handler = () => setView("version");
9898
9765
  window.addEventListener("kyro:show-version-history", handler);
9899
9766
  return () => window.removeEventListener("kyro:show-version-history", handler);
9900
9767
  }, []);
9901
- React54.useEffect(() => {
9768
+ React56.useEffect(() => {
9902
9769
  const handleClickOutside = (e) => {
9903
9770
  if (menuRef.current && !menuRef.current.contains(e.target)) {
9904
9771
  setIsMenuOpen(false);
@@ -9909,7 +9776,7 @@ function AutoForm({
9909
9776
  return () => document.removeEventListener("mousedown", handleClickOutside);
9910
9777
  }
9911
9778
  }, [isMenuOpen]);
9912
- React54.useEffect(() => {
9779
+ React56.useEffect(() => {
9913
9780
  const handleClickOutside = (e) => {
9914
9781
  if (scheduleRef.current && !scheduleRef.current.contains(e.target)) {
9915
9782
  setShowSchedulePicker(false);
@@ -9990,18 +9857,16 @@ function AutoForm({
9990
9857
  message: "Unpublish this document?",
9991
9858
  onConfirm: async () => {
9992
9859
  try {
9993
- const response = await fetchWithAuth(
9994
- resolveApi(`/api/${collectionSlug}/${formData.id}/unpublish`),
9995
- {
9996
- method: "POST"
9997
- }
9860
+ const response = await saveDocument(
9861
+ { ...formData, status: "draft" },
9862
+ false
9998
9863
  );
9999
- if (response.ok) {
9864
+ if (response?.ok) {
10000
9865
  onActionSuccess?.("Document unpublished successfully");
10001
9866
  location.reload();
10002
9867
  } else {
10003
- const error = await response.json();
10004
- toast.error(error.error || "Failed to unpublish");
9868
+ const error = await response?.json().catch(() => ({}));
9869
+ toast.error(error?.error || "Failed to unpublish");
10005
9870
  }
10006
9871
  } catch (err) {
10007
9872
  toast.error("Failed to unpublish");
@@ -10012,12 +9877,7 @@ function AutoForm({
10012
9877
  const handleSaveDraft = async () => {
10013
9878
  const isNewDoc = !formData.id;
10014
9879
  autoSaveSkipRef.current = true;
10015
- const btn = document.getElementById("btn-publish");
10016
- const originalText = btn?.textContent || "";
10017
- if (btn) {
10018
- btn.textContent = "Saving...";
10019
- btn.setAttribute("disabled", "true");
10020
- }
9880
+ setLocalSaveStatus("saving");
10021
9881
  try {
10022
9882
  const data = normalizeUploadFields({ ...formData });
10023
9883
  const isPost = isNewDoc && !globalSlug;
@@ -10033,9 +9893,12 @@ function AutoForm({
10033
9893
  setLastSavedData({ ...formData, ...savedData });
10034
9894
  lastAutoSaveTimeRef.current = Date.now();
10035
9895
  setAutoSaveStatus("success");
10036
- await clearDraftArtifacts();
9896
+ setLocalSaveStatus("saved");
10037
9897
  if (versionsEnabled) fetchVersions();
10038
- setTimeout(() => setAutoSaveStatus("idle"), 5e3);
9898
+ setTimeout(() => {
9899
+ setAutoSaveStatus("idle");
9900
+ setLocalSaveStatus("idle");
9901
+ }, 2e3);
10039
9902
  onActionSuccess?.(
10040
9903
  isPost ? "Document created successfully" : "Changes saved"
10041
9904
  );
@@ -10049,78 +9912,63 @@ function AutoForm({
10049
9912
  if (response.status === 409) {
10050
9913
  setAutoSaveStatus("conflict");
10051
9914
  }
9915
+ setLocalSaveStatus("error");
10052
9916
  toast.error(error.error || "Failed to save");
9917
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10053
9918
  }
10054
9919
  } catch (err) {
9920
+ setLocalSaveStatus("error");
10055
9921
  toast.error("Failed to save document");
9922
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10056
9923
  } finally {
10057
9924
  autoSaveSkipRef.current = false;
10058
- if (btn) {
10059
- btn.textContent = originalText;
10060
- btn.removeAttribute("disabled");
10061
- }
10062
9925
  }
10063
9926
  };
10064
9927
  const handlePublish = async () => {
10065
9928
  const isNewDoc = !formData.id;
10066
9929
  autoSaveSkipRef.current = true;
10067
- const btn = document.getElementById("btn-publish");
10068
- const originalText = btn?.textContent || "";
10069
- if (btn) {
10070
- btn.textContent = "Publishing...";
10071
- btn.setAttribute("disabled", "true");
10072
- }
9930
+ setLocalSaveStatus("saving");
10073
9931
  try {
10074
9932
  if (isNewDoc && !globalSlug) {
10075
- const data = normalizeUploadFields({ ...formData });
9933
+ const data2 = normalizeUploadFields({ ...formData });
10076
9934
  const response2 = await fetchWithAuth(`/api/${collectionSlug}`, {
10077
9935
  method: "POST",
10078
9936
  headers: { "Content-Type": "application/json" },
10079
- body: JSON.stringify(data)
9937
+ body: JSON.stringify(data2)
10080
9938
  });
10081
9939
  if (!response2.ok) {
10082
9940
  const error = await response2.json().catch(() => ({}));
10083
9941
  if (response2.status === 409) setAutoSaveStatus("conflict");
9942
+ setLocalSaveStatus("error");
10084
9943
  toast.error(error.error || "Failed to create document");
9944
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10085
9945
  return;
10086
9946
  }
10087
9947
  const result = await response2.json();
10088
- const savedData = result.data || data;
9948
+ const savedData = result.data || data2;
10089
9949
  setFormData({ ...formData, ...savedData });
10090
9950
  setLastSavedData({ ...formData, ...savedData });
10091
- } else if (hasUnsavedChanges) {
10092
- const response2 = await saveDocument(formData);
10093
- if (!response2.ok) {
10094
- const error = await response2.json().catch(() => ({}));
10095
- if (response2.status === 409) setAutoSaveStatus("conflict");
10096
- toast.error(error.error || "Failed to save before publishing");
10097
- return;
10098
- }
10099
- const result = await response2.json();
10100
- if (result.data) {
10101
- setFormData({ ...formData, ...result.data });
10102
- setLastSavedData({ ...formData, ...result.data });
10103
- }
10104
9951
  }
10105
- const response = await publishDocument();
10106
- if (response.ok) {
10107
- await clearDraftArtifacts();
9952
+ const data = normalizeUploadFields({ ...formData });
9953
+ const response = await saveDocument(data, false);
9954
+ if (response?.ok) {
9955
+ setLocalSaveStatus("saved");
10108
9956
  onActionSuccess?.("Published successfully");
10109
- await new Promise((r) => setTimeout(r, 1500));
9957
+ await new Promise((r) => setTimeout(r, 1e3));
10110
9958
  location.reload();
10111
9959
  } else {
10112
- const error = await response.json();
10113
- if (response.status === 409) setAutoSaveStatus("conflict");
10114
- toast.error(error.error || "Failed to publish");
9960
+ const error = await response?.json().catch(() => ({}));
9961
+ if (response?.status === 409) setAutoSaveStatus("conflict");
9962
+ setLocalSaveStatus("error");
9963
+ toast.error(error?.error || "Failed to publish");
9964
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10115
9965
  }
10116
9966
  } catch (err) {
9967
+ setLocalSaveStatus("error");
10117
9968
  toast.error("Failed to publish");
9969
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10118
9970
  } finally {
10119
9971
  autoSaveSkipRef.current = false;
10120
- if (btn) {
10121
- btn.textContent = originalText;
10122
- btn.removeAttribute("disabled");
10123
- }
10124
9972
  }
10125
9973
  };
10126
9974
  const handleSchedulePublish = async (scheduledFor) => {
@@ -10437,12 +10285,12 @@ function AutoForm({
10437
10285
  const docTitle = String(
10438
10286
  formData.mainTabs?.title || (typeof formData.title === "object" ? "" : formData.title) || (typeof formData.name === "object" ? "" : formData.name) || "Untitled"
10439
10287
  );
10440
- const docStatus = documentStatus ?? formData.publishStatus ?? formData.status ?? "draft";
10288
+ const docStatus = documentStatus ?? formData.status ?? "draft";
10441
10289
  const isNew = !formData.id;
10442
10290
  const lastModified = formData.updatedAt ? new Date(formData.updatedAt).toLocaleString() : "Just now";
10443
10291
  const createdAt = formData.createdAt ? new Date(formData.createdAt).toLocaleString() : "Just now";
10444
- const isDraftMode = !formData.id || documentStatus === "draft" || !!formData.hasDraft;
10445
- const statusLabel = hasUnpublishedChanges ? docStatus === "draft" && !formData._prevStatus ? "Draft" : "Published (unpublished changes)" : docStatus === "published" ? "Published" : "Draft";
10292
+ !formData.id || documentStatus === "draft";
10293
+ const statusLabel = hasUnpublishedChanges ? "Draft (unpublished changes)" : docStatus === "published" ? "Published" : "Draft";
10446
10294
  const statusColor = docStatus === "published" && !hasUnsavedChanges ? "bg-[var(--kyro-success)]" : hasUnpublishedChanges ? "bg-[var(--kyro-warning)]" : "bg-[var(--kyro-text-muted)]";
10447
10295
  const statusBadgeBg = docStatus === "published" && !hasUnpublishedChanges ? "bg-[var(--kyro-success)]/10 text-[var(--kyro-success)] border-[var(--kyro-success)]/20" : hasUnpublishedChanges ? "bg-[var(--kyro-warning)]/10 text-[var(--kyro-warning)] border-[var(--kyro-warning)]/20" : "bg-[var(--kyro-text-muted)]/10 text-[var(--kyro-text-muted)] border-[var(--kyro-text-muted)]/20";
10448
10296
  return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "surface-tile px-8 py-6 flex items-center justify-between sticky top-0 z-50 border-b border-[var(--kyro-border)] mb-8 bg-[var(--kyro-surface)] backdrop-blur-md", children: [
@@ -10453,24 +10301,7 @@ function AutoForm({
10453
10301
  {
10454
10302
  href: `/${collectionSlug}`,
10455
10303
  className: "p-2 border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-bg-secondary)] transition-colors",
10456
- children: /* @__PURE__ */ jsxRuntime.jsx(
10457
- "svg",
10458
- {
10459
- className: "w-4 h-4",
10460
- fill: "none",
10461
- stroke: "currentColor",
10462
- viewBox: "0 0 24 24",
10463
- children: /* @__PURE__ */ jsxRuntime.jsx(
10464
- "path",
10465
- {
10466
- strokeLinecap: "round",
10467
- strokeLinejoin: "round",
10468
- strokeWidth: "2.5",
10469
- d: "M15 19l-7-7 7-7"
10470
- }
10471
- )
10472
- }
10473
- )
10304
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
10474
10305
  }
10475
10306
  ),
10476
10307
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-bold tracking-tighter", children: docTitle }),
@@ -10513,18 +10344,7 @@ function AutoForm({
10513
10344
  "Saving draft..."
10514
10345
  ] }),
10515
10346
  autoSaveStatus === "success" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[var(--kyro-success)] flex items-center gap-1", children: [
10516
- /* @__PURE__ */ jsxRuntime.jsx(
10517
- "svg",
10518
- {
10519
- width: "12",
10520
- height: "12",
10521
- viewBox: "0 0 24 24",
10522
- fill: "none",
10523
- stroke: "currentColor",
10524
- strokeWidth: "3",
10525
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" })
10526
- }
10527
- ),
10347
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" }),
10528
10348
  lastSavedAt ? `Saved ${Math.floor((Date.now() - lastSavedAt) / 6e4)}m ago` : "Draft saved"
10529
10349
  ] }),
10530
10350
  autoSaveStatus === "retrying" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[var(--kyro-warning)] flex items-center gap-1.5", children: [
@@ -10584,13 +10404,23 @@ function AutoForm({
10584
10404
  onClick: async () => {
10585
10405
  setFormData(lastSavedData);
10586
10406
  markSaved();
10587
- await clearDraftArtifacts();
10588
10407
  },
10589
10408
  className: "text-[var(--kyro-primary)] hover:underline",
10590
10409
  children: "Revert changes"
10591
10410
  }
10592
10411
  )
10593
10412
  ] }),
10413
+ lastSavedAt && autoSaveStatus !== "saving" && autoSaveStatus !== "retrying" && autoSaveStatus !== "success" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "border-l border-[var(--kyro-border)] pl-4", children: [
10414
+ "Draft saved ",
10415
+ (() => {
10416
+ const diffMs = now - lastSavedAt;
10417
+ const diffMin = Math.floor(diffMs / 6e4);
10418
+ const diffSec = Math.floor(diffMs / 1e3);
10419
+ if (diffMin >= 1) return `${diffMin}m ago`;
10420
+ if (diffSec >= 5) return `${diffSec}s ago`;
10421
+ return "just now";
10422
+ })()
10423
+ ] }),
10594
10424
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "border-l border-[var(--kyro-border)] pl-4", children: [
10595
10425
  "Modified ",
10596
10426
  lastModified
@@ -10622,18 +10452,7 @@ function AutoForm({
10622
10452
  className: `kyro-btn p-2.5 rounded-xl transition-all flex items-center gap-2 ${showPreview ? "shadow-lg" : "text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-bg-secondary)]"}`,
10623
10453
  title: "Live Preview",
10624
10454
  children: [
10625
- /* @__PURE__ */ jsxRuntime.jsx(
10626
- "svg",
10627
- {
10628
- width: "20",
10629
- height: "20",
10630
- viewBox: "0 0 24 24",
10631
- fill: "none",
10632
- stroke: "currentColor",
10633
- strokeWidth: "2",
10634
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3" })
10635
- }
10636
- ),
10455
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "w-4 h-4" }),
10637
10456
  showPreview && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold tracking-widest pr-1", children: "Active" })
10638
10457
  ]
10639
10458
  }
@@ -10664,164 +10483,98 @@ function AutoForm({
10664
10483
  )
10665
10484
  }
10666
10485
  ),
10667
- documentStatus === "published" && !isNew && !hasUnsavedChanges ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-6 py-2.5 text-xs rounded-xl bg-green-100 text-green-700 border border-green-200 cursor-not-allowed", children: [
10668
- /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" }) }),
10669
- "Published"
10670
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: menuRef, className: "relative flex items-center gap-3", children: [
10671
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center rounded-xl overflow-hidden shadow-lg", children: [
10672
- /* @__PURE__ */ jsxRuntime.jsx(
10486
+ /* @__PURE__ */ jsxRuntime.jsx(
10487
+ SplitButton,
10488
+ {
10489
+ status: documentStatus,
10490
+ saveStatus: localSaveStatus,
10491
+ hasChanges: hasUnsavedChanges,
10492
+ onPublish: handlePublish,
10493
+ disabled: localSaveStatus === "saving"
10494
+ }
10495
+ ),
10496
+ !isNew && /* @__PURE__ */ jsxRuntime.jsxs(
10497
+ Dropdown,
10498
+ {
10499
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
10673
10500
  "button",
10674
10501
  {
10675
- id: "btn-publish",
10676
10502
  type: "button",
10677
- onClick: isDraftMode ? handleSaveDraft : handlePublish,
10678
- className: `px-6 py-2.5 text-xs font-bold rounded-l-xl rounded-r-none transition-all whitespace-nowrap ${isDraftMode ? "kyro-btn-primary" : "kyro-btn-success"}`,
10679
- children: isDraftMode ? "Save Draft" : "Publish Changes"
10503
+ className: "kyro-btn p-2.5 rounded-xl border border-[var(--kyro-border)] hover:bg-[var(--kyro-bg-secondary)] transition-all",
10504
+ title: "More actions",
10505
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: [
10506
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "1.5" }),
10507
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "1.5" }),
10508
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "19", r: "1.5" })
10509
+ ] })
10680
10510
  }
10681
10511
  ),
10682
- /* @__PURE__ */ jsxRuntime.jsx(
10683
- "button",
10684
- {
10685
- type: "button",
10686
- onClick: () => setIsMenuOpen(!isMenuOpen),
10687
- className: `px-2.5 py-2.5 text-xs rounded-r-xl rounded-l-none transition-all ${isDraftMode ? "kyro-btn-primary" : "kyro-btn-success"}`,
10688
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" }) })
10689
- }
10690
- )
10691
- ] }),
10692
- isMenuOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-full mt-2 w-56 rounded-lg border border-[var(--kyro-border)] bg-[var(--kyro-surface)] shadow-2xl z-50 overflow-hidden", children: [
10693
- /* @__PURE__ */ jsxRuntime.jsxs(
10694
- "button",
10695
- {
10696
- type: "button",
10697
- onClick: () => {
10698
- handleSaveDraft();
10699
- setIsMenuOpen(false);
10700
- },
10701
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] flex items-center gap-3 transition-colors",
10702
- children: [
10703
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10704
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
10705
- /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "17 21 17 13 7 13 7 21" }),
10706
- /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "7 3 7 8 15 8" })
10512
+ direction: "down",
10513
+ children: [
10514
+ !globalSlug && /* @__PURE__ */ jsxRuntime.jsx(
10515
+ DropdownItem,
10516
+ {
10517
+ onClick: handleCreateNew,
10518
+ icon: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10519
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
10520
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
10707
10521
  ] }),
10708
- "Save Draft"
10709
- ]
10710
- }
10711
- ),
10712
- /* @__PURE__ */ jsxRuntime.jsxs(
10713
- "button",
10714
- {
10715
- type: "button",
10716
- onClick: () => {
10717
- handlePublish();
10718
- setIsMenuOpen(false);
10719
- },
10720
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] flex items-center gap-3 transition-colors",
10721
- children: [
10722
- /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "5 3 19 12 5 21 5 3" }) }),
10723
- "Publish"
10724
- ]
10725
- }
10726
- ),
10727
- /* @__PURE__ */ jsxRuntime.jsxs(
10728
- "button",
10729
- {
10730
- type: "button",
10731
- onClick: () => {
10732
- setIsMenuOpen(false);
10733
- setShowSchedulePicker(true);
10734
- },
10735
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] flex items-center gap-3 transition-colors",
10736
- children: [
10737
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10522
+ children: "Create New"
10523
+ }
10524
+ ),
10525
+ !globalSlug && /* @__PURE__ */ jsxRuntime.jsx(
10526
+ DropdownItem,
10527
+ {
10528
+ onClick: handleDuplicate,
10529
+ icon: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10530
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
10531
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
10532
+ ] }),
10533
+ children: "Duplicate"
10534
+ }
10535
+ ),
10536
+ /* @__PURE__ */ jsxRuntime.jsx(
10537
+ DropdownItem,
10538
+ {
10539
+ onClick: () => setShowSchedulePicker(true),
10540
+ icon: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10738
10541
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" }),
10739
10542
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
10740
10543
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
10741
10544
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
10742
10545
  ] }),
10743
- "Schedule Publish"
10744
- ]
10745
- }
10746
- ),
10747
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px bg-[var(--kyro-border)]" }),
10748
- !globalSlug && /* @__PURE__ */ jsxRuntime.jsxs(
10749
- "button",
10750
- {
10751
- type: "button",
10752
- onClick: () => {
10753
- handleCreateNew();
10754
- setIsMenuOpen(false);
10755
- },
10756
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] flex items-center gap-3 transition-colors",
10757
- children: [
10758
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10759
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
10760
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
10761
- ] }),
10762
- "Create New"
10763
- ]
10764
- }
10765
- ),
10766
- !isNew && !globalSlug && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10767
- /* @__PURE__ */ jsxRuntime.jsxs(
10768
- "button",
10769
- {
10770
- type: "button",
10771
- onClick: () => {
10772
- handleDuplicate();
10773
- setIsMenuOpen(false);
10774
- },
10775
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] flex items-center gap-3 transition-colors",
10776
- children: [
10777
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10778
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
10779
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
10780
- ] }),
10781
- "Duplicate"
10782
- ]
10546
+ children: "Schedule Publish"
10783
10547
  }
10784
10548
  ),
10785
- documentStatus === "published" && /* @__PURE__ */ jsxRuntime.jsxs(
10786
- "button",
10549
+ documentStatus === "published" && /* @__PURE__ */ jsxRuntime.jsx(
10550
+ DropdownItem,
10787
10551
  {
10788
- type: "button",
10789
- onClick: () => {
10790
- handleUnpublish();
10791
- setIsMenuOpen(false);
10792
- },
10793
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)] flex items-center gap-3 transition-colors",
10794
- children: [
10795
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10796
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" }),
10797
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
10798
- ] }),
10799
- "Unpublish"
10800
- ]
10552
+ onClick: handleUnpublish,
10553
+ icon: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10554
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" }),
10555
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
10556
+ ] }),
10557
+ children: "Unpublish"
10801
10558
  }
10802
10559
  ),
10803
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px bg-[var(--kyro-border)]" }),
10804
- /* @__PURE__ */ jsxRuntime.jsxs(
10805
- "button",
10806
- {
10807
- type: "button",
10808
- onClick: () => {
10809
- handleDelete();
10810
- setIsMenuOpen(false);
10811
- },
10812
- className: "w-full px-4 py-2.5 text-left text-xs font-medium text-red-600 hover:bg-red-50 flex items-center gap-3 transition-colors",
10813
- children: [
10814
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10560
+ !globalSlug && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10561
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownSeparator, {}),
10562
+ /* @__PURE__ */ jsxRuntime.jsx(
10563
+ DropdownItem,
10564
+ {
10565
+ onClick: handleDelete,
10566
+ danger: true,
10567
+ icon: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10815
10568
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }),
10816
10569
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
10817
10570
  ] }),
10818
- "Delete"
10819
- ]
10820
- }
10821
- )
10822
- ] })
10823
- ] })
10824
- ] }),
10571
+ children: "Delete"
10572
+ }
10573
+ )
10574
+ ] })
10575
+ ]
10576
+ }
10577
+ ),
10825
10578
  showSchedulePicker && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scheduleRef, className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-2 p-4 rounded-lg border border-[var(--kyro-border)] bg-[var(--kyro-surface)] shadow-2xl z-50", children: [
10826
10579
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium mb-2", children: "Schedule Publish" }),
10827
10580
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -10941,17 +10694,7 @@ function AutoForm({
10941
10694
  type: "button",
10942
10695
  onClick: () => setCompareDiffs([]),
10943
10696
  className: "p-1 rounded hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-muted)]",
10944
- children: /* @__PURE__ */ jsxRuntime.jsx(
10945
- "svg",
10946
- {
10947
- className: "w-3.5 h-3.5",
10948
- viewBox: "0 0 24 24",
10949
- fill: "none",
10950
- stroke: "currentColor",
10951
- strokeWidth: "2.5",
10952
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
10953
- }
10954
- )
10697
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
10955
10698
  }
10956
10699
  )
10957
10700
  ] }),
@@ -10982,17 +10725,7 @@ function AutoForm({
10982
10725
  "div",
10983
10726
  {
10984
10727
  className: `w-4 h-4 rounded-full border ${isSelected ? "border-[var(--kyro-primary)] bg-[var(--kyro-primary)]" : "border-[var(--kyro-border)]"}`,
10985
- children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
10986
- "svg",
10987
- {
10988
- className: "w-full h-full text-white p-0.5",
10989
- viewBox: "0 0 24 24",
10990
- fill: "none",
10991
- stroke: "currentColor",
10992
- strokeWidth: "3",
10993
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" })
10994
- }
10995
- )
10728
+ children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" })
10996
10729
  }
10997
10730
  ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold text-[var(--kyro-text-muted)] w-5", children: versions.length - i }) }),
10998
10731
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-4 min-w-0", children: [
@@ -11085,18 +10818,7 @@ function AutoForm({
11085
10818
  "div",
11086
10819
  {
11087
10820
  className: `w-4 h-4 rounded border transition-all flex items-center justify-center ${item.checked ? "bg-[var(--kyro-primary)] border-[var(--kyro-primary)]" : "border-[var(--kyro-border)] group-hover:border-[var(--kyro-text-secondary)]"}`,
11088
- children: item.checked && /* @__PURE__ */ jsxRuntime.jsx(
11089
- "svg",
11090
- {
11091
- width: "10",
11092
- height: "10",
11093
- viewBox: "0 0 24 24",
11094
- fill: "none",
11095
- stroke: "white",
11096
- strokeWidth: "4",
11097
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" })
11098
- }
11099
- )
10821
+ children: item.checked && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" })
11100
10822
  }
11101
10823
  ),
11102
10824
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-[var(--kyro-text-secondary)] group-hover:text-[var(--kyro-text-primary)] transition-colors", children: item.label })
@@ -11131,7 +10853,7 @@ function AutoForm({
11131
10853
  "Saving..."
11132
10854
  ] }),
11133
10855
  autoSaveStatus === "success" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[var(--kyro-success)] flex items-center gap-1", children: [
11134
- /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" }) }),
10856
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" }),
11135
10857
  lastSavedAt ? `Saved ${Math.floor((Date.now() - lastSavedAt) / 6e4)}m ago` : "Saved"
11136
10858
  ] }),
11137
10859
  autoSaveStatus === "retrying" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[var(--kyro-warning)] flex items-center gap-1.5", children: [
@@ -11156,85 +10878,29 @@ function AutoForm({
11156
10878
  type: "button",
11157
10879
  style: { width: 0, height: 0, opacity: 0, padding: 0, margin: 0, border: "none", position: "absolute" },
11158
10880
  onClick: async () => {
11159
- console.log("[AutoForm] Hidden save button clicked");
11160
10881
  try {
11161
10882
  const response = await saveDocument();
11162
10883
  if (response.ok) {
11163
- const result = await response.json();
11164
- const savedData = result.data || formData;
11165
- setFormData({ ...formData, ...savedData });
11166
- setLastSavedData({ ...formData, ...savedData });
11167
- onActionSuccess?.("Changes saved");
11168
- }
11169
- } catch (e) {
11170
- console.error("Save error exception:", e);
11171
- onActionError?.("Save failed: " + e.message);
11172
- }
11173
- }
11174
- }
11175
- )
11176
- ] }),
11177
- /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "w-full", children: [
11178
- view === "edit" && renderEditView(),
11179
- view === "version" && renderVersionView(),
11180
- view === "api" && renderApiView()
11181
- ] })
11182
- ] });
11183
- }
11184
- function Dropdown({
11185
- trigger,
11186
- children,
11187
- align = "right"
11188
- }) {
11189
- const [open, setOpen] = React54.useState(false);
11190
- const ref = React54.useRef(null);
11191
- React54.useEffect(() => {
11192
- const handleClickOutside = (e) => {
11193
- if (ref.current && !ref.current.contains(e.target)) {
11194
- setOpen(false);
11195
- }
11196
- };
11197
- if (open) {
11198
- document.addEventListener("mousedown", handleClickOutside);
11199
- }
11200
- return () => document.removeEventListener("mousedown", handleClickOutside);
11201
- }, [open]);
11202
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref, children: [
11203
- /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: () => setOpen(!open), className: "cursor-pointer", children: trigger }),
11204
- open && /* @__PURE__ */ jsxRuntime.jsx(
11205
- "div",
11206
- {
11207
- className: `absolute z-[100] mt-2 min-w-[200px] py-2 bg-[var(--kyro-surface)] rounded-2xl shadow-2xl border border-[var(--kyro-border)] animate-in fade-in zoom-in-95 duration-100 ${align === "right" ? "right-0 bottom-full mb-2" : "left-0 bottom-full mb-2"}`,
11208
- onClick: () => setOpen(false),
11209
- children
11210
- }
11211
- )
11212
- ] });
11213
- }
11214
- function DropdownItem({
11215
- children,
11216
- onClick,
11217
- icon,
11218
- danger,
11219
- disabled,
11220
- className = ""
11221
- }) {
11222
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1.5", children: /* @__PURE__ */ jsxRuntime.jsxs(
11223
- "button",
11224
- {
11225
- type: "button",
11226
- onClick,
11227
- disabled,
11228
- className: `w-full flex items-center gap-3 px-3 py-2.5 text-[11px] font-medium tracking-wide text-left transition-all rounded-xl ${danger ? "text-red-500 hover:bg-red-500/10" : "text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"} ${disabled ? "opacity-50 cursor-not-allowed" : ""} ${className}`,
11229
- children: [
11230
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 opacity-70", children: icon }),
11231
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children })
11232
- ]
11233
- }
11234
- ) });
11235
- }
11236
- function DropdownSeparator() {
11237
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--kyro-border)] opacity-50" });
10884
+ const result = await response.json();
10885
+ const savedData = result.data || formData;
10886
+ setFormData({ ...formData, ...savedData });
10887
+ setLastSavedData({ ...formData, ...savedData });
10888
+ onActionSuccess?.("Changes saved");
10889
+ }
10890
+ } catch (e) {
10891
+ console.error("Save error exception:", e);
10892
+ onActionError?.("Save failed: " + e.message);
10893
+ }
10894
+ }
10895
+ }
10896
+ )
10897
+ ] }),
10898
+ /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "w-full", children: [
10899
+ view === "edit" && renderEditView(),
10900
+ view === "version" && renderVersionView(),
10901
+ view === "api" && renderApiView()
10902
+ ] })
10903
+ ] });
11238
10904
  }
11239
10905
  function ActionBar({
11240
10906
  status,
@@ -11257,82 +10923,27 @@ function ActionBar({
11257
10923
  if (hasChanges) return "Unsaved changes";
11258
10924
  return null;
11259
10925
  };
11260
- const getSaveButtonClass = () => {
11261
- const base = "kyro-btn kyro-btn-md text-[11px] font-bold tracking-widest transition-all duration-300";
11262
- if (saveStatus === "saving") return `${base} bg-[var(--kyro-gray-400)] text-white opacity-70 cursor-wait`;
11263
- if (saveStatus === "saved") return `${base} bg-[var(--kyro-success)] border-[var(--kyro-success)] text-white shadow-[0_0_15px_rgba(34,197,94,0.3)]`;
11264
- if (saveStatus === "error") return `${base} bg-[var(--kyro-error)] border-[var(--kyro-error)] text-white shadow-[0_0_15px_rgba(239,68,68,0.3)]`;
11265
- if (hasChanges) return `${base} bg-[var(--kyro-warning)] border-[var(--kyro-warning)] text-black shadow-[0_0_15px_rgba(255,174,0,0.3)] animate-pulse`;
11266
- return `${base} bg-[var(--kyro-gray-500)] border-[var(--kyro-gray-500)] text-white hover:bg-[var(--kyro-gray-600)]`;
11267
- };
11268
10926
  const getStatusBadge = () => {
11269
10927
  const statusConfig2 = {
11270
10928
  draft: {
11271
10929
  label: "Draft",
11272
10930
  class: "bg-gray-100 text-gray-600 border-gray-200",
11273
- icon: /* @__PURE__ */ jsxRuntime.jsx(
11274
- "svg",
11275
- {
11276
- width: "12",
11277
- height: "12",
11278
- viewBox: "0 0 24 24",
11279
- fill: "none",
11280
- stroke: "currentColor",
11281
- strokeWidth: "2.5",
11282
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2v20M2 12h20" })
11283
- }
11284
- )
10931
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3 h-3" })
11285
10932
  },
11286
10933
  published: {
11287
10934
  label: "Published",
11288
10935
  class: "bg-green-100 text-green-700 border-green-200",
11289
- icon: /* @__PURE__ */ jsxRuntime.jsx(
11290
- "svg",
11291
- {
11292
- width: "12",
11293
- height: "12",
11294
- viewBox: "0 0 24 24",
11295
- fill: "none",
11296
- stroke: "currentColor",
11297
- strokeWidth: "2.5",
11298
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" })
11299
- }
11300
- )
10936
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-3 h-3" })
11301
10937
  },
11302
10938
  scheduled: {
11303
10939
  label: "Scheduled",
11304
10940
  class: "bg-blue-100 text-blue-700 border-blue-200",
11305
- icon: /* @__PURE__ */ jsxRuntime.jsxs(
11306
- "svg",
11307
- {
11308
- width: "12",
11309
- height: "12",
11310
- viewBox: "0 0 24 24",
11311
- fill: "none",
11312
- stroke: "currentColor",
11313
- strokeWidth: "2.5",
11314
- children: [
11315
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
11316
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 6v6l4 2" })
11317
- ]
11318
- }
11319
- )
10941
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-3 h-3" })
11320
10942
  },
11321
10943
  archived: {
11322
10944
  label: "Archived",
11323
10945
  class: "bg-yellow-100 text-yellow-700 border-yellow-200",
11324
- icon: /* @__PURE__ */ jsxRuntime.jsx(
11325
- "svg",
11326
- {
11327
- width: "12",
11328
- height: "12",
11329
- viewBox: "0 0 24 24",
11330
- fill: "none",
11331
- stroke: "currentColor",
11332
- strokeWidth: "2.5",
11333
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 8v13H3V8M1 3h22v5H1zM10 12h4" })
11334
- }
11335
- )
10946
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Archive, { className: "w-3 h-3" })
11336
10947
  }
11337
10948
  };
11338
10949
  const config = statusConfig2[status];
@@ -11358,7 +10969,7 @@ function ActionBar({
11358
10969
  getSaveStatusText() && /* @__PURE__ */ jsxRuntime.jsxs(
11359
10970
  "span",
11360
10971
  {
11361
- className: `text-sm ${saveStatus === "error" ? "text-red-500" : "text-gray-500"}`,
10972
+ className: `text-sm ${saveStatus === "error" ? "text-[var(--kyro-error)]" : "text-[var(--kyro-text-muted)]"}`,
11362
10973
  children: [
11363
10974
  saveStatus === "saving" ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm", className: "inline mr-1" }) : null,
11364
10975
  getSaveStatusText()
@@ -11367,7 +10978,7 @@ function ActionBar({
11367
10978
  )
11368
10979
  ] }),
11369
10980
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs space-y-0.5", children: [
11370
- updatedAt && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-400", children: [
10981
+ updatedAt && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[var(--kyro-text-muted)]", children: [
11371
10982
  "Updated: ",
11372
10983
  formatDate2(updatedAt)
11373
10984
  ] }),
@@ -11384,20 +10995,9 @@ function ActionBar({
11384
10995
  type: "button",
11385
10996
  onClick: onPublish,
11386
10997
  disabled: saveStatus === "saving",
11387
- className: "kyro-btn-success hover:opacity-90 kyro-btn-md flex items-center gap-2",
10998
+ className: "kyro-btn kyro-btn-primary kyro-btn-md flex items-center gap-2",
11388
10999
  children: [
11389
- /* @__PURE__ */ jsxRuntime.jsx(
11390
- "svg",
11391
- {
11392
- width: "16",
11393
- height: "16",
11394
- viewBox: "0 0 24 24",
11395
- fill: "none",
11396
- stroke: "currentColor",
11397
- strokeWidth: "2.5",
11398
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" })
11399
- }
11400
- ),
11000
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-4 h-4" }),
11401
11001
  "Publish"
11402
11002
  ]
11403
11003
  }
@@ -11408,114 +11008,39 @@ function ActionBar({
11408
11008
  type: "button",
11409
11009
  onClick: onUnpublish,
11410
11010
  disabled: saveStatus === "saving",
11411
- className: "kyro-btn kyro-btn-warning kyro-btn-md flex items-center gap-2",
11011
+ className: "kyro-btn kyro-btn-secondary kyro-btn-md flex items-center gap-2",
11412
11012
  children: [
11413
- /* @__PURE__ */ jsxRuntime.jsx(
11414
- "svg",
11415
- {
11416
- width: "16",
11417
- height: "16",
11418
- viewBox: "0 0 24 24",
11419
- fill: "none",
11420
- stroke: "currentColor",
11421
- strokeWidth: "2.5",
11422
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8M3 3v5h5" })
11423
- }
11424
- ),
11013
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo, { className: "w-4 h-4" }),
11425
11014
  "Unpublish"
11426
11015
  ]
11427
11016
  }
11428
11017
  ),
11429
- status === "published" && !hasChanges && saveStatus !== "saving" && saveStatus !== "error" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-[11px] font-bold tracking-widest bg-green-100 text-green-700 border border-green-200 cursor-not-allowed shadow-sm", children: [
11430
- /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" }) }),
11431
- "Published"
11432
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
11433
- "button",
11434
- {
11435
- type: "button",
11436
- onClick: onSave,
11437
- disabled: saveStatus === "saving" || !hasChanges && saveStatus !== "error",
11438
- className: getSaveButtonClass(),
11439
- children: saveStatus === "saving" ? "Saving..." : saveStatus === "saved" ? "Saved" : hasChanges && status === "published" ? "Save Draft" : status === "draft" && hasChanges ? "Save Draft" : "Save"
11440
- }
11441
- ),
11442
11018
  /* @__PURE__ */ jsxRuntime.jsxs(
11443
- Dropdown,
11019
+ SplitButton,
11444
11020
  {
11445
- trigger: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "kyro-btn kyro-btn-ghost kyro-btn-md p-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
11446
- "svg",
11447
- {
11448
- width: "16",
11449
- height: "16",
11450
- viewBox: "0 0 24 24",
11451
- fill: "none",
11452
- stroke: "currentColor",
11453
- strokeWidth: "2",
11454
- children: [
11455
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "1" }),
11456
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "1" }),
11457
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "19", r: "1" })
11458
- ]
11459
- }
11460
- ) }),
11021
+ status,
11022
+ saveStatus,
11023
+ hasChanges,
11024
+ onPublish: onSave,
11461
11025
  children: [
11462
11026
  onDuplicate && /* @__PURE__ */ jsxRuntime.jsx(
11463
11027
  DropdownItem,
11464
11028
  {
11465
- onClick: onDuplicate,
11466
- icon: /* @__PURE__ */ jsxRuntime.jsxs(
11467
- "svg",
11468
- {
11469
- viewBox: "0 0 24 24",
11470
- fill: "none",
11471
- stroke: "currentColor",
11472
- strokeWidth: "2",
11473
- children: [
11474
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
11475
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
11476
- ]
11477
- }
11478
- ),
11029
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "w-4 h-4" }),
11479
11030
  children: "Duplicate"
11480
11031
  }
11481
11032
  ),
11482
11033
  onViewHistory && /* @__PURE__ */ jsxRuntime.jsx(
11483
11034
  DropdownItem,
11484
11035
  {
11485
- onClick: onViewHistory,
11486
- icon: /* @__PURE__ */ jsxRuntime.jsxs(
11487
- "svg",
11488
- {
11489
- viewBox: "0 0 24 24",
11490
- fill: "none",
11491
- stroke: "currentColor",
11492
- strokeWidth: "2",
11493
- children: [
11494
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
11495
- /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "12,6 12,12 16,14" })
11496
- ]
11497
- }
11498
- ),
11036
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4" }),
11499
11037
  children: "View History"
11500
11038
  }
11501
11039
  ),
11502
11040
  onPreview && /* @__PURE__ */ jsxRuntime.jsx(
11503
11041
  DropdownItem,
11504
11042
  {
11505
- onClick: onPreview,
11506
- icon: /* @__PURE__ */ jsxRuntime.jsxs(
11507
- "svg",
11508
- {
11509
- viewBox: "0 0 24 24",
11510
- fill: "none",
11511
- stroke: "currentColor",
11512
- strokeWidth: "2",
11513
- children: [
11514
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
11515
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" })
11516
- ]
11517
- }
11518
- ),
11043
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "w-4 h-4" }),
11519
11044
  children: "Preview"
11520
11045
  }
11521
11046
  ),
@@ -11525,16 +11050,7 @@ function ActionBar({
11525
11050
  {
11526
11051
  onClick: onDelete,
11527
11052
  danger: true,
11528
- icon: /* @__PURE__ */ jsxRuntime.jsx(
11529
- "svg",
11530
- {
11531
- viewBox: "0 0 24 24",
11532
- fill: "none",
11533
- stroke: "currentColor",
11534
- strokeWidth: "2",
11535
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
11536
- }
11537
- ),
11053
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-4 h-4" }),
11538
11054
  children: "Delete"
11539
11055
  }
11540
11056
  )
@@ -11544,62 +11060,6 @@ function ActionBar({
11544
11060
  ] })
11545
11061
  ] });
11546
11062
  }
11547
- function Toast({ type, message, onClose }) {
11548
- const [isPaused, setIsPaused] = React54__default.default.useState(false);
11549
- const timerRef = React54__default.default.useRef(null);
11550
- const startTimer = () => {
11551
- if (timerRef.current) clearTimeout(timerRef.current);
11552
- timerRef.current = setTimeout(onClose, 5e3);
11553
- };
11554
- const clearTimer = () => {
11555
- if (timerRef.current) clearTimeout(timerRef.current);
11556
- };
11557
- React54__default.default.useEffect(() => {
11558
- if (!isPaused) {
11559
- startTimer();
11560
- } else {
11561
- clearTimer();
11562
- }
11563
- return clearTimer;
11564
- }, [isPaused, onClose]);
11565
- const Icon = {
11566
- success: lucideReact.CircleCheck,
11567
- error: lucideReact.ShieldAlert,
11568
- warning: lucideReact.TriangleAlert,
11569
- info: lucideReact.Info
11570
- }[type];
11571
- return /* @__PURE__ */ jsxRuntime.jsxs(
11572
- "div",
11573
- {
11574
- className: `kyro-toast kyro-toast-${type} group animate-in fade-in slide-in-from-right-4 duration-300`,
11575
- onMouseEnter: () => setIsPaused(true),
11576
- onMouseLeave: () => setIsPaused(false),
11577
- children: [
11578
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "kyro-toast-accent" }),
11579
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "kyro-toast-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-4 h-4" }) }),
11580
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "kyro-toast-content", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "kyro-toast-message", children: message }) }),
11581
- /* @__PURE__ */ jsxRuntime.jsx(
11582
- "button",
11583
- {
11584
- type: "button",
11585
- className: "kyro-toast-close group-hover:opacity-100 opacity-40 transition-opacity",
11586
- onClick: onClose,
11587
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
11588
- }
11589
- )
11590
- ]
11591
- }
11592
- );
11593
- }
11594
- function ToastProvider({ children }) {
11595
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
11596
- }
11597
- function useToast() {
11598
- const addToast = useToastStore((state) => state.addToast);
11599
- const removeToast = useToastStore((state) => state.removeToast);
11600
- const toasts = useToastStore((state) => state.toasts);
11601
- return { toasts, addToast, removeToast };
11602
- }
11603
11063
  function DetailView({
11604
11064
  config,
11605
11065
  collection,
@@ -11611,34 +11071,33 @@ function DetailView({
11611
11071
  onError,
11612
11072
  mode = "collection"
11613
11073
  }) {
11614
- const { addToast } = useToast();
11615
11074
  const { confirm, alert } = useUIStore();
11616
- const [data, setData] = React54.useState({});
11617
- const [originalData, setOriginalData] = React54.useState({});
11618
- const [loading, setLoading] = React54.useState(true);
11619
- const [saving, setSaving] = React54.useState(false);
11620
- const [saveStatus, setSaveStatus] = React54.useState("idle");
11621
- const [deleting, setDeleting] = React54.useState(false);
11622
- const [status, setStatus] = React54.useState("draft");
11623
- const [createdAt, setCreatedAt] = React54.useState(null);
11624
- const [updatedAt, setUpdatedAt] = React54.useState(null);
11625
- const [publishedAt, setPublishedAt] = React54.useState(null);
11626
- const [justSaved, setJustSaved] = React54.useState(false);
11075
+ const [data, setData] = React56.useState({});
11076
+ const [originalData, setOriginalData] = React56.useState({});
11077
+ const [loading, setLoading] = React56.useState(true);
11078
+ const [saving, setSaving] = React56.useState(false);
11079
+ const [saveStatus, setSaveStatus] = React56.useState("idle");
11080
+ const [deleting, setDeleting] = React56.useState(false);
11081
+ const [status, setStatus] = React56.useState("draft");
11082
+ const [createdAt, setCreatedAt] = React56.useState(null);
11083
+ const [updatedAt, setUpdatedAt] = React56.useState(null);
11084
+ const [publishedAt, setPublishedAt] = React56.useState(null);
11085
+ const [justSaved, setJustSaved] = React56.useState(false);
11627
11086
  const fields2 = global?.fields || collection?.fields || [];
11628
11087
  const label = global?.label || collection?.label || "Document";
11629
11088
  const slug = global?.slug || collection?.slug || "";
11630
11089
  const hasChanges = JSON.stringify(data) !== JSON.stringify(originalData);
11631
- React54.useEffect(() => {
11090
+ React56.useEffect(() => {
11632
11091
  if (hasChanges && status === "published") {
11633
11092
  setStatus("draft");
11634
11093
  }
11635
11094
  }, [hasChanges, status]);
11636
- React54.useEffect(() => {
11095
+ React56.useEffect(() => {
11637
11096
  if (hasChanges && saveStatus === "saved") {
11638
11097
  setSaveStatus("idle");
11639
11098
  }
11640
11099
  }, [hasChanges, saveStatus]);
11641
- React54.useEffect(() => {
11100
+ React56.useEffect(() => {
11642
11101
  if (mode === "global") {
11643
11102
  loadGlobal();
11644
11103
  } else if (documentId) {
@@ -11652,7 +11111,7 @@ function DetailView({
11652
11111
  const docData = result.data || {};
11653
11112
  setData(docData);
11654
11113
  setOriginalData(docData);
11655
- setStatus(docData?.publishStatus || result.status || "draft");
11114
+ setStatus(docData?.status || result.status || "draft");
11656
11115
  setCreatedAt(result.createdAt || docData.createdAt || null);
11657
11116
  setUpdatedAt(result.updatedAt || docData.updatedAt || null);
11658
11117
  setPublishedAt(result.publishedAt || docData.publishedAt || null);
@@ -11677,26 +11136,28 @@ function DetailView({
11677
11136
  setLoading(false);
11678
11137
  }
11679
11138
  };
11680
- const handleSave = React54.useCallback(
11139
+ const handleSave = React56.useCallback(
11681
11140
  async (isAutosave = false) => {
11682
11141
  try {
11683
11142
  setSaveStatus("saving");
11684
11143
  const endpoint = mode === "global" ? `/api/globals/${slug}` : `/api/${slug}/${documentId}`;
11685
- const result = await apiPatch(endpoint, data, { autoToast: false });
11144
+ const isDraft = status === "draft" || data?.status === "draft";
11145
+ const result = await apiPatch(endpoint, data, { autoToast: false, headers: { "X-Draft": String(isDraft) } });
11686
11146
  const savedData = result && (result.data || result) || data;
11687
11147
  if (!isAutosave) {
11688
11148
  setOriginalData(savedData);
11689
11149
  onSave();
11690
11150
  }
11691
11151
  setData(savedData);
11692
- setStatus(savedData?.publishStatus || status);
11152
+ setStatus(savedData?.status || status);
11693
11153
  setSaveStatus("saved");
11694
11154
  setUpdatedAt((/* @__PURE__ */ new Date()).toISOString());
11695
11155
  setJustSaved(true);
11696
11156
  setTimeout(() => setJustSaved(false), 3e3);
11697
11157
  if (!isAutosave) {
11698
- const isDraft = status === "draft" || savedData?.publishStatus === "draft";
11699
- addToast?.(isDraft ? "warning" : "success", isDraft ? "Draft saved" : "Updated");
11158
+ const isDraft2 = status === "draft" || savedData?.status === "draft";
11159
+ if (isDraft2) toast.warning("Draft saved");
11160
+ else toast.success("Updated");
11700
11161
  }
11701
11162
  setTimeout(() => {
11702
11163
  setSaveStatus("idle");
@@ -11705,24 +11166,28 @@ function DetailView({
11705
11166
  setSaveStatus("error");
11706
11167
  if (!isAutosave) {
11707
11168
  onError("Failed to save changes");
11708
- addToast?.("error", "Failed to save changes");
11169
+ toast.error("Failed to save changes");
11709
11170
  }
11710
11171
  } finally {
11711
11172
  setSaving(false);
11712
11173
  }
11713
11174
  },
11714
- [data, mode, slug, documentId, onSave, onError]
11175
+ [data, mode, slug, documentId, status, onSave, onError]
11715
11176
  );
11716
11177
  const handlePublish = async () => {
11717
11178
  try {
11718
11179
  setSaving(true);
11719
- await apiPost(`/api/${slug}/${documentId}/publish`, void 0, { autoToast: false });
11180
+ await apiPatch(`/api/${slug}/${documentId}`, data, {
11181
+ autoToast: false,
11182
+ headers: { "X-Draft": "false" }
11183
+ });
11720
11184
  setStatus("published");
11721
11185
  setPublishedAt((/* @__PURE__ */ new Date()).toISOString());
11722
- addToast?.("success", "Published successfully");
11186
+ toast.success("Published successfully");
11187
+ onSave();
11723
11188
  } catch {
11724
11189
  onError("Failed to publish");
11725
- addToast?.("error", "Failed to publish");
11190
+ toast.error("Failed to publish");
11726
11191
  } finally {
11727
11192
  setSaving(false);
11728
11193
  }
@@ -11730,12 +11195,16 @@ function DetailView({
11730
11195
  const handleUnpublish = async () => {
11731
11196
  try {
11732
11197
  setSaving(true);
11733
- await apiPost(`/api/${slug}/${documentId}/unpublish`, void 0, { autoToast: false });
11198
+ await apiPatch(`/api/${slug}/${documentId}`, { status: "draft" }, {
11199
+ autoToast: false,
11200
+ headers: { "X-Draft": "false" }
11201
+ });
11734
11202
  setStatus("draft");
11735
- addToast?.("warning", "Document unpublished");
11203
+ toast.warning("Document unpublished");
11204
+ onSave();
11736
11205
  } catch {
11737
11206
  onError("Failed to unpublish");
11738
- addToast?.("error", "Failed to unpublish");
11207
+ toast.error("Failed to unpublish");
11739
11208
  } finally {
11740
11209
  setSaving(false);
11741
11210
  }
@@ -11744,11 +11213,11 @@ function DetailView({
11744
11213
  try {
11745
11214
  setSaving(true);
11746
11215
  await apiPost(`/api/${slug}/${documentId}/duplicate`, void 0, { autoToast: false });
11747
- addToast?.("success", "Document duplicated");
11216
+ toast.success("Document duplicated");
11748
11217
  } catch (err) {
11749
11218
  const message = err instanceof Error ? err.message : "Failed to duplicate document";
11750
11219
  onError(message);
11751
- addToast?.("error", message);
11220
+ toast.error(message);
11752
11221
  } finally {
11753
11222
  setSaving(false);
11754
11223
  }
@@ -11763,10 +11232,10 @@ function DetailView({
11763
11232
  setDeleting(true);
11764
11233
  await apiDelete(`/api/${slug}/${documentId}`, { autoToast: false });
11765
11234
  onDelete?.();
11766
- addToast?.("error", "Document deleted");
11235
+ toast.error("Document deleted");
11767
11236
  } catch (err) {
11768
11237
  const message = err instanceof Error ? err.message : "Failed to delete document";
11769
- addToast?.("error", message);
11238
+ toast.error(message);
11770
11239
  } finally {
11771
11240
  setDeleting(false);
11772
11241
  }
@@ -11849,8 +11318,8 @@ function DetailView({
11849
11318
  layout: isSingleLayout ? "single" : "split",
11850
11319
  globalSlug: mode === "global" ? slug : void 0,
11851
11320
  collectionSlug: mode === "collection" ? slug : void 0,
11852
- onActionSuccess: (message) => addToast?.("success", message),
11853
- onActionError: (message) => addToast?.("error", message),
11321
+ onActionSuccess: (message) => toast.success(message),
11322
+ onActionError: (message) => toast.error(message),
11854
11323
  documentStatus: status,
11855
11324
  justSaved
11856
11325
  }
@@ -11865,44 +11334,14 @@ function DetailView({
11865
11334
  children: "Delete"
11866
11335
  }
11867
11336
  ),
11868
- status === "published" && !hasChanges && !saving ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 px-6 py-2.5 rounded-lg text-xs font-bold bg-green-100 text-green-700 border border-green-200 cursor-not-allowed shadow-xl", children: [
11869
- /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" }) }),
11870
- "Published"
11871
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
11872
- "button",
11337
+ /* @__PURE__ */ jsxRuntime.jsx(
11338
+ SplitButton,
11873
11339
  {
11874
- type: "button",
11875
- onClick: () => handleSave(false),
11876
- disabled: saving,
11877
- className: "kyro-btn kyro-btn-lg kyro-btn-primary shadow-xl flex items-center gap-2",
11878
- children: [
11879
- saving ? /* @__PURE__ */ jsxRuntime.jsx(
11880
- "svg",
11881
- {
11882
- className: "w-4 h-4 animate-spin",
11883
- viewBox: "0 0 24 24",
11884
- fill: "none",
11885
- stroke: "currentColor",
11886
- strokeWidth: "2",
11887
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
11888
- }
11889
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
11890
- "svg",
11891
- {
11892
- className: "w-4 h-4",
11893
- viewBox: "0 0 24 24",
11894
- fill: "none",
11895
- stroke: "currentColor",
11896
- strokeWidth: "2",
11897
- children: [
11898
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
11899
- /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "17 21 17 13 7 13 7 21" }),
11900
- /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "7 3 7 8 15 8" })
11901
- ]
11902
- }
11903
- ),
11904
- saving ? "Saving..." : mode === "global" ? "Save Configuration" : "Save Document"
11905
- ]
11340
+ status,
11341
+ saveStatus: saving ? "saving" : "idle",
11342
+ hasChanges,
11343
+ onPublish: () => handleSave(false),
11344
+ disabled: saving
11906
11345
  }
11907
11346
  )
11908
11347
  ] })
@@ -11989,8 +11428,8 @@ function CreateView({
11989
11428
  onSuccess,
11990
11429
  onError
11991
11430
  }) {
11992
- const [data, setData] = React54.useState({});
11993
- const [saving, setSaving] = React54.useState(false);
11431
+ const [data, setData] = React56.useState({});
11432
+ const [saving, setSaving] = React56.useState(false);
11994
11433
  const fields2 = collection.fields || [];
11995
11434
  const label = collection.label || collection.slug;
11996
11435
  const handleSubmit = async (e) => {
@@ -12032,15 +11471,71 @@ function CreateView({
12032
11471
  ) }) }) }) })
12033
11472
  ] });
12034
11473
  }
11474
+ function Toast({ type, message, onClose }) {
11475
+ const [isPaused, setIsPaused] = React56__default.default.useState(false);
11476
+ const timerRef = React56__default.default.useRef(null);
11477
+ const startTimer = () => {
11478
+ if (timerRef.current) clearTimeout(timerRef.current);
11479
+ timerRef.current = setTimeout(onClose, 5e3);
11480
+ };
11481
+ const clearTimer = () => {
11482
+ if (timerRef.current) clearTimeout(timerRef.current);
11483
+ };
11484
+ React56__default.default.useEffect(() => {
11485
+ if (!isPaused) {
11486
+ startTimer();
11487
+ } else {
11488
+ clearTimer();
11489
+ }
11490
+ return clearTimer;
11491
+ }, [isPaused, onClose]);
11492
+ const Icon = {
11493
+ success: lucideReact.CircleCheck,
11494
+ error: lucideReact.ShieldAlert,
11495
+ warning: lucideReact.TriangleAlert,
11496
+ info: lucideReact.Info
11497
+ }[type];
11498
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11499
+ "div",
11500
+ {
11501
+ className: `kyro-toast kyro-toast-${type} group animate-in fade-in slide-in-from-right-4 duration-300`,
11502
+ onMouseEnter: () => setIsPaused(true),
11503
+ onMouseLeave: () => setIsPaused(false),
11504
+ children: [
11505
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "kyro-toast-accent" }),
11506
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "kyro-toast-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-4 h-4" }) }),
11507
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "kyro-toast-content", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "kyro-toast-message", children: message }) }),
11508
+ /* @__PURE__ */ jsxRuntime.jsx(
11509
+ "button",
11510
+ {
11511
+ type: "button",
11512
+ className: "kyro-toast-close group-hover:opacity-100 opacity-40 transition-opacity",
11513
+ onClick: onClose,
11514
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
11515
+ }
11516
+ )
11517
+ ]
11518
+ }
11519
+ );
11520
+ }
11521
+ function ToastProvider({ children }) {
11522
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
11523
+ }
11524
+ function useToast() {
11525
+ const addToast = useToastStore((state) => state.addToast);
11526
+ const removeToast = useToastStore((state) => state.removeToast);
11527
+ const toasts = useToastStore((state) => state.toasts);
11528
+ return { toasts, addToast, removeToast };
11529
+ }
12035
11530
  function LoginPage({ onAuth, theme = "light" }) {
12036
- const [mode, setMode] = React54.useState("login");
12037
- const [email, setEmail] = React54.useState("");
12038
- const [password, setPassword] = React54.useState("");
12039
- const [confirmPassword, setConfirmPassword] = React54.useState("");
12040
- const [loading, setLoading] = React54.useState(false);
12041
- const [toasts, setToasts] = React54.useState([]);
12042
- const [isFirstUser, setIsFirstUser] = React54.useState(false);
12043
- React54.useEffect(() => {
11531
+ const [mode, setMode] = React56.useState("login");
11532
+ const [email, setEmail] = React56.useState("");
11533
+ const [password, setPassword] = React56.useState("");
11534
+ const [confirmPassword, setConfirmPassword] = React56.useState("");
11535
+ const [loading, setLoading] = React56.useState(false);
11536
+ const [toasts, setToasts] = React56.useState([]);
11537
+ const [isFirstUser, setIsFirstUser] = React56.useState(false);
11538
+ React56.useEffect(() => {
12044
11539
  checkIfFirstUser();
12045
11540
  }, []);
12046
11541
  const checkIfFirstUser = async () => {
@@ -12186,14 +11681,14 @@ function LoginPage({ onAuth, theme = "light" }) {
12186
11681
  }
12187
11682
  function Dashboard({ collections: collections2, onNavigate, user }) {
12188
11683
  const { permissions } = useAuthStore();
12189
- const [stats, setStats] = React54.useState({
11684
+ const [stats, setStats] = React56.useState({
12190
11685
  totalDocs: 0,
12191
11686
  totalMedia: 0,
12192
11687
  totalUsers: 0,
12193
11688
  recentActivity: []
12194
11689
  });
12195
- const [loading, setLoading] = React54.useState(true);
12196
- React54.useEffect(() => {
11690
+ const [loading, setLoading] = React56.useState(true);
11691
+ React56.useEffect(() => {
12197
11692
  const timer = setTimeout(() => {
12198
11693
  setStats({
12199
11694
  totalDocs: 124,
@@ -12514,15 +12009,15 @@ function Dashboard({ collections: collections2, onNavigate, user }) {
12514
12009
  ] });
12515
12010
  }
12516
12011
  function UserManagement() {
12517
- const [users, setUsers] = React54.useState([]);
12518
- const [loading, setLoading] = React54.useState(true);
12519
- const [searchQuery, setSearchQuery] = React54.useState("");
12520
- const [showCreateModal, setShowCreateModal] = React54.useState(false);
12521
- const [createForm, setCreateForm] = React54.useState({ name: "", email: "", password: "", role: "customer" });
12522
- const [createError, setCreateError] = React54.useState("");
12523
- const [creating, setCreating] = React54.useState(false);
12012
+ const [users, setUsers] = React56.useState([]);
12013
+ const [loading, setLoading] = React56.useState(true);
12014
+ const [searchQuery, setSearchQuery] = React56.useState("");
12015
+ const [showCreateModal, setShowCreateModal] = React56.useState(false);
12016
+ const [createForm, setCreateForm] = React56.useState({ name: "", email: "", password: "", role: "customer" });
12017
+ const [createError, setCreateError] = React56.useState("");
12018
+ const [creating, setCreating] = React56.useState(false);
12524
12019
  const { confirm, alert } = useUIStore();
12525
- React54.useEffect(() => {
12020
+ React56.useEffect(() => {
12526
12021
  loadUsers();
12527
12022
  }, []);
12528
12023
  const loadUsers = async () => {
@@ -12787,8 +12282,8 @@ function UserManagement() {
12787
12282
  ] });
12788
12283
  }
12789
12284
  function AvatarCell({ user }) {
12790
- const [url, setUrl] = React54.useState(null);
12791
- React54.useEffect(() => {
12285
+ const [url, setUrl] = React56.useState(null);
12286
+ React56.useEffect(() => {
12792
12287
  const avatar = user.avatar;
12793
12288
  if (typeof avatar === "string" && /^[0-9a-f-]+$/i.test(avatar)) {
12794
12289
  apiGet(`/api/media/${avatar}`).then((media) => setUrl(media?.thumbnailUrl || media?.url || null)).catch(() => setUrl(null));
@@ -12800,15 +12295,15 @@ function AvatarCell({ user }) {
12800
12295
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 rounded-lg bg-[var(--kyro-surface-accent)] border border-[var(--kyro-border)] flex items-center justify-center text-xs font-bold text-[var(--kyro-primary)] flex-shrink-0", children: user.name ? user.name[0] : user.email[0].toUpperCase() });
12801
12296
  }
12802
12297
  function BrandingHub() {
12803
- const [siteName, setSiteName] = React54.useState("Kyro CMS");
12804
- const [adminTitle, setAdminTitle] = React54.useState("Command Center");
12805
- const [primaryColor, setPrimaryColor] = React54.useState("#6366f1");
12806
- const [dashboardGreeting, setDashboardGreeting] = React54.useState(
12298
+ const [siteName, setSiteName] = React56.useState("Kyro CMS");
12299
+ const [adminTitle, setAdminTitle] = React56.useState("Command Center");
12300
+ const [primaryColor, setPrimaryColor] = React56.useState("#6366f1");
12301
+ const [dashboardGreeting, setDashboardGreeting] = React56.useState(
12807
12302
  "Welcome back to your Command Center."
12808
12303
  );
12809
- const [saving, setSaving] = React54.useState(false);
12810
- const [saved, setSaved] = React54.useState(false);
12811
- React54.useEffect(() => {
12304
+ const [saving, setSaving] = React56.useState(false);
12305
+ const [saved, setSaved] = React56.useState(false);
12306
+ React56.useEffect(() => {
12812
12307
  const fetchBranding = async () => {
12813
12308
  try {
12814
12309
  const result = await apiGet("/api/globals/site-settings");
@@ -13004,15 +12499,15 @@ function BrandingHub() {
13004
12499
  }
13005
12500
  var API_BASE2 = typeof __KYRO_API_PATH__ !== "undefined" ? __KYRO_API_PATH__ : "/api";
13006
12501
  function DeveloperCenter({ collections: collections2 }) {
13007
- const [keys, setKeys] = React54.useState([]);
13008
- const [loading, setLoading] = React54.useState(false);
13009
- const [showKey, setShowKey] = React54.useState(null);
13010
- const [testEndpoint, setTestEndpoint] = React54.useState("");
13011
- const [playgroundResult, setPlaygroundResult] = React54.useState(null);
13012
- const [exploring, setExploring] = React54.useState(false);
12502
+ const [keys, setKeys] = React56.useState([]);
12503
+ const [loading, setLoading] = React56.useState(false);
12504
+ const [showKey, setShowKey] = React56.useState(null);
12505
+ const [testEndpoint, setTestEndpoint] = React56.useState("");
12506
+ const [playgroundResult, setPlaygroundResult] = React56.useState(null);
12507
+ const [exploring, setExploring] = React56.useState(false);
13013
12508
  const { confirm, alert } = useUIStore();
13014
- const [showCreateModal, setShowCreateModal] = React54.useState(false);
13015
- const [newKeyName, setNewKeyName] = React54.useState("");
12509
+ const [showCreateModal, setShowCreateModal] = React56.useState(false);
12510
+ const [newKeyName, setNewKeyName] = React56.useState("");
13016
12511
  const loadKeys = async () => {
13017
12512
  try {
13018
12513
  const data = await apiGet("/api/keys");
@@ -13021,7 +12516,7 @@ function DeveloperCenter({ collections: collections2 }) {
13021
12516
  console.error(e);
13022
12517
  }
13023
12518
  };
13024
- React54.useEffect(() => {
12519
+ React56.useEffect(() => {
13025
12520
  loadKeys();
13026
12521
  }, []);
13027
12522
  const handleGenerateKey = async () => {
@@ -13339,12 +12834,12 @@ function DeveloperCenter({ collections: collections2 }) {
13339
12834
  ] });
13340
12835
  }
13341
12836
  function useResourceManager(options) {
13342
- const [items, setItems] = React54.useState([]);
13343
- const [loading, setLoading] = React54.useState(false);
13344
- const [error, setError] = React54.useState(null);
13345
- const [isCreateModalOpen, setIsCreateModalOpen] = React54.useState(false);
12837
+ const [items, setItems] = React56.useState([]);
12838
+ const [loading, setLoading] = React56.useState(false);
12839
+ const [error, setError] = React56.useState(null);
12840
+ const [isCreateModalOpen, setIsCreateModalOpen] = React56.useState(false);
13346
12841
  const { confirm } = useUIStore();
13347
- const load = React54.useCallback(async () => {
12842
+ const load = React56.useCallback(async () => {
13348
12843
  setLoading(true);
13349
12844
  setError(null);
13350
12845
  try {
@@ -13361,10 +12856,10 @@ function useResourceManager(options) {
13361
12856
  setLoading(false);
13362
12857
  }
13363
12858
  }, [options.endpoint, options.transformLoad]);
13364
- React54.useEffect(() => {
12859
+ React56.useEffect(() => {
13365
12860
  load();
13366
12861
  }, [load]);
13367
- const remove = React54.useCallback((id, resourceName = "item") => {
12862
+ const remove = React56.useCallback((id, resourceName = "item") => {
13368
12863
  confirm({
13369
12864
  title: `Delete ${resourceName}`,
13370
12865
  message: `Are you sure you want to delete this ${resourceName.toLowerCase()}? This action cannot be undone.`,
@@ -13382,7 +12877,7 @@ function useResourceManager(options) {
13382
12877
  }
13383
12878
  });
13384
12879
  }, [options.endpoint, confirm]);
13385
- const create3 = React54.useCallback(async (data) => {
12880
+ const create3 = React56.useCallback(async (data) => {
13386
12881
  setError(null);
13387
12882
  try {
13388
12883
  const created = await apiPost(options.endpoint, data);
@@ -13397,7 +12892,7 @@ function useResourceManager(options) {
13397
12892
  throw e;
13398
12893
  }
13399
12894
  }, [options.endpoint]);
13400
- const update = React54.useCallback(async (id, data) => {
12895
+ const update = React56.useCallback(async (id, data) => {
13401
12896
  setError(null);
13402
12897
  try {
13403
12898
  const updated = await apiPatch(`${options.endpoint}/${id}`, data);
@@ -13437,17 +12932,17 @@ function WebhookManager() {
13437
12932
  endpoint: "/api/webhooks"
13438
12933
  });
13439
12934
  const { confirm } = useUIStore();
13440
- const [showTestModal, setShowTestModal] = React54.useState(false);
13441
- const [showHelpModal, setShowHelpModal] = React54.useState(false);
13442
- const [testResult, setTestResult] = React54.useState(null);
13443
- const [testId, setTestId] = React54.useState(null);
13444
- const [formData, setFormData] = React54.useState({
12935
+ const [showTestModal, setShowTestModal] = React56.useState(false);
12936
+ const [showHelpModal, setShowHelpModal] = React56.useState(false);
12937
+ const [testResult, setTestResult] = React56.useState(null);
12938
+ const [testId, setTestId] = React56.useState(null);
12939
+ const [formData, setFormData] = React56.useState({
13445
12940
  name: "",
13446
12941
  url: "",
13447
12942
  events: [],
13448
12943
  secret: ""
13449
12944
  });
13450
- const [createError, setCreateError] = React54.useState("");
12945
+ const [createError, setCreateError] = React56.useState("");
13451
12946
  const handleCreate = async () => {
13452
12947
  if (!formData.name.trim() || !formData.url.trim()) {
13453
12948
  setCreateError("Name and URL are required");
@@ -13899,13 +13394,13 @@ function CommandPalette({
13899
13394
  onNavigate
13900
13395
  }) {
13901
13396
  const { user, permissions } = useAuthStore();
13902
- const [query, setQuery] = React54.useState("");
13903
- const [selectedIndex, setSelectedIndex] = React54.useState(0);
13904
- const [loading, setLoading] = React54.useState(false);
13905
- const [searchResults, setSearchResults] = React54.useState([]);
13906
- const inputRef = React54.useRef(null);
13907
- const debounceRef = React54.useRef(null);
13908
- React54.useEffect(() => {
13397
+ const [query, setQuery] = React56.useState("");
13398
+ const [selectedIndex, setSelectedIndex] = React56.useState(0);
13399
+ const [loading, setLoading] = React56.useState(false);
13400
+ const [searchResults, setSearchResults] = React56.useState([]);
13401
+ const inputRef = React56.useRef(null);
13402
+ const debounceRef = React56.useRef(null);
13403
+ React56.useEffect(() => {
13909
13404
  if (isOpen) {
13910
13405
  setQuery("");
13911
13406
  setSelectedIndex(0);
@@ -13914,7 +13409,7 @@ function CommandPalette({
13914
13409
  setTimeout(() => inputRef.current?.focus(), 100);
13915
13410
  }
13916
13411
  }, [isOpen]);
13917
- const performSearch = React54.useCallback(async (searchQuery) => {
13412
+ const performSearch = React56.useCallback(async (searchQuery) => {
13918
13413
  if (!searchQuery || searchQuery.length < 2) {
13919
13414
  setSearchResults([]);
13920
13415
  return;
@@ -13935,7 +13430,7 @@ function CommandPalette({
13935
13430
  setLoading(false);
13936
13431
  }
13937
13432
  }, []);
13938
- React54.useEffect(() => {
13433
+ React56.useEffect(() => {
13939
13434
  if (debounceRef.current) {
13940
13435
  clearTimeout(debounceRef.current);
13941
13436
  }
@@ -14182,7 +13677,7 @@ function CommandPalette({
14182
13677
  }
14183
13678
  function GlobalModal() {
14184
13679
  const { modal, closeModal } = useUIStore();
14185
- const [loading, setLoading] = React54.useState(false);
13680
+ const [loading, setLoading] = React56.useState(false);
14186
13681
  if (!modal.open || !modal.config) return null;
14187
13682
  const { config } = modal;
14188
13683
  const handleConfirm = async () => {
@@ -14222,24 +13717,24 @@ function GlobalModal() {
14222
13717
  );
14223
13718
  }
14224
13719
  function Admin({ config, theme = "light", onThemeChange }) {
14225
- const [authenticated, setAuthenticated] = React54.useState(false);
14226
- const [currentUser, setCurrentUser] = React54.useState(null);
14227
- const collections2 = React54.useMemo(
13720
+ const [authenticated, setAuthenticated] = React56.useState(false);
13721
+ const [currentUser, setCurrentUser] = React56.useState(null);
13722
+ const collections2 = React56.useMemo(
14228
13723
  () => toCollectionMap(toArray(config.collections)),
14229
13724
  [config.collections]
14230
13725
  );
14231
- const globals2 = React54.useMemo(
13726
+ const globals2 = React56.useMemo(
14232
13727
  () => toGlobalMap(toArray(config.globals)),
14233
13728
  [config.globals]
14234
13729
  );
14235
- const [activeCollection, setActiveCollection] = React54.useState(null);
14236
- const [activeGlobal, setActiveGlobal] = React54.useState(null);
14237
- const [currentView, setCurrentView] = React54.useState("list");
14238
- const [activeDocumentId, setActiveDocumentId] = React54.useState(null);
14239
- const [isCommandPaletteOpen, setIsCommandPaletteOpen] = React54.useState(false);
13730
+ const [activeCollection, setActiveCollection] = React56.useState(null);
13731
+ const [activeGlobal, setActiveGlobal] = React56.useState(null);
13732
+ const [currentView, setCurrentView] = React56.useState("list");
13733
+ const [activeDocumentId, setActiveDocumentId] = React56.useState(null);
13734
+ const [isCommandPaletteOpen, setIsCommandPaletteOpen] = React56.useState(false);
14240
13735
  const toasts = useToastStore((state) => state.toasts);
14241
13736
  const removeToast = useToastStore((state) => state.removeToast);
14242
- React54.useEffect(() => {
13737
+ React56.useEffect(() => {
14243
13738
  const checkAuth = async () => {
14244
13739
  try {
14245
13740
  const response = await fetch("/api/users/me");
@@ -14254,7 +13749,7 @@ function Admin({ config, theme = "light", onThemeChange }) {
14254
13749
  };
14255
13750
  checkAuth();
14256
13751
  }, []);
14257
- React54.useEffect(() => {
13752
+ React56.useEffect(() => {
14258
13753
  if (authenticated && !activeCollection) {
14259
13754
  const firstCol = Object.keys(collections2)[0];
14260
13755
  if (firstCol) setActiveCollection(firstCol);
@@ -14266,7 +13761,7 @@ function Admin({ config, theme = "light", onThemeChange }) {
14266
13761
  if (id) setActiveDocumentId(id);
14267
13762
  setIsCommandPaletteOpen(false);
14268
13763
  };
14269
- React54.useEffect(() => {
13764
+ React56.useEffect(() => {
14270
13765
  const handleKeyDown = (e) => {
14271
13766
  if ((e.metaKey || e.ctrlKey) && e.key === "k") {
14272
13767
  e.preventDefault();
@@ -14345,7 +13840,7 @@ function Admin({ config, theme = "light", onThemeChange }) {
14345
13840
  );
14346
13841
  }
14347
13842
  };
14348
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { ...{ mode: theme, onChange: onThemeChange }, children: /* @__PURE__ */ jsxRuntime.jsx(ToastProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "kyro-admin min-h-screen bg-[var(--kyro-bg)] text-[var(--kyro-text-primary)]", children: [
13843
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { ...{ mode: theme, onChange: onThemeChange }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "kyro-admin min-h-screen bg-[var(--kyro-bg)] text-[var(--kyro-text-primary)]", children: [
14349
13844
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 flex flex-col min-w-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
14350
13845
  /* @__PURE__ */ jsxRuntime.jsx(
14351
13846
  CommandPalette,
@@ -14369,7 +13864,7 @@ function Admin({ config, theme = "light", onThemeChange }) {
14369
13864
  },
14370
13865
  t.id
14371
13866
  )) })
14372
- ] }) }) });
13867
+ ] }) });
14373
13868
  }
14374
13869
  function BulkActionsBar({
14375
13870
  selectedCount,
@@ -14600,32 +14095,6 @@ function Header({ title, onMenuClick, actions }) {
14600
14095
  ] })
14601
14096
  ] });
14602
14097
  }
14603
- function Button({
14604
- variant = "secondary",
14605
- size = "md",
14606
- loading = false,
14607
- children,
14608
- className = "",
14609
- disabled,
14610
- ...props
14611
- }) {
14612
- const baseClass = "kyro-btn";
14613
- const variantClass = `kyro-btn-${variant}`;
14614
- const sizeClass = `kyro-btn-${size}`;
14615
- return /* @__PURE__ */ jsxRuntime.jsxs(
14616
- "button",
14617
- {
14618
- type: "button",
14619
- className: `${baseClass} ${variantClass} ${sizeClass} ${className}`,
14620
- disabled: disabled || loading,
14621
- ...props,
14622
- children: [
14623
- loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "kyro-btn-loading", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "kyro-btn-spinner", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", strokeDasharray: "60 30" }) }) }) : null,
14624
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: loading ? "kyro-btn-text-hidden" : "", children })
14625
- ]
14626
- }
14627
- );
14628
- }
14629
14098
 
14630
14099
  // src/hooks/lifecycle.ts
14631
14100
  var registry = {
@@ -14692,10 +14161,10 @@ function getApiUrl(path2) {
14692
14161
  return `${base}${path2}`;
14693
14162
  }
14694
14163
  function useKyroQuery(slug, options = {}) {
14695
- const [data, setData] = React54.useState(null);
14696
- const [loading, setLoading] = React54.useState(true);
14697
- const [error, setError] = React54.useState(null);
14698
- const fetchData = React54.useCallback(async () => {
14164
+ const [data, setData] = React56.useState(null);
14165
+ const [loading, setLoading] = React56.useState(true);
14166
+ const [error, setError] = React56.useState(null);
14167
+ const fetchData = React56.useCallback(async () => {
14699
14168
  setLoading(true);
14700
14169
  setError(null);
14701
14170
  try {
@@ -14717,15 +14186,15 @@ function useKyroQuery(slug, options = {}) {
14717
14186
  setLoading(false);
14718
14187
  }
14719
14188
  }, [slug, options.page, options.limit, options.sort, options.order]);
14720
- React54.useEffect(() => {
14189
+ React56.useEffect(() => {
14721
14190
  fetchData();
14722
14191
  }, [fetchData]);
14723
14192
  return { data, loading, error, refetch: fetchData };
14724
14193
  }
14725
14194
  function useKyroMutation(slug, method = "POST") {
14726
- const [loading, setLoading] = React54.useState(false);
14727
- const [error, setError] = React54.useState(null);
14728
- const mutate = React54.useCallback(
14195
+ const [loading, setLoading] = React56.useState(false);
14196
+ const [error, setError] = React56.useState(null);
14197
+ const mutate = React56.useCallback(
14729
14198
  async (data) => {
14730
14199
  setLoading(true);
14731
14200
  setError(null);