@kyro-cms/admin 0.9.1 → 0.9.2

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 -2
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import React54, { createContext, lazy, useState, useContext, useRef, useEffect, useCallback, useMemo, Suspense } from 'react';
1
+ import React56, { createContext, lazy, useState, useContext, useRef, useEffect, useCallback, useMemo, Suspense } from 'react';
2
2
  import { create, useStore } from 'zustand';
3
3
  import { persist, createJSONStorage } from 'zustand/middleware';
4
+ import { GripVertical, Copy, X, Video, Mail, Clock, Database, Users, Tag, Image, Activity, CircleHelp, Sparkles, Blocks, Columns3, Link2, ListOrdered, Box, Heading1, Star, AlignLeft, ChevronDown, File, Code, List, Plus, ChevronRight, ChevronUp, Search, LoaderCircle, Check, RefreshCw, Undo, Redo, Bold, Italic, Underline, Strikethrough, Highlighter, Palette, CheckSquare, Quote, Terminal, Minus, AlignCenter, AlignRight, Link, Minimize2, Maximize2, Grid3X3, FolderPlus, FolderInput, Folder, Trash2, Film, Music, FileText, Archive, Download, Crop, Server, CodeXml, Filter, Pencil, Send, Eye, Info, TriangleAlert, ShieldAlert, CircleCheck, History, CheckCircle2, User, GitCompare, Undo2, ExternalLink, Settings, Shield, Hexagon, Network, Sun, Moon, LogOut, ArrowRight, LayoutDashboard, ArrowUpRight, Webhook, Zap, Pause, Play, Key, Lock, EyeOff, CirclePlay, RefreshCcw, Save, UserPlus, LockOpen } from 'lucide-react';
4
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
- import { GripVertical, Copy, X, Video, Mail, Clock, Database, Users, Tag, Image, Activity, CircleHelp, Sparkles, Blocks, Columns3, Link2, ListOrdered, Box, Heading1, Star, AlignLeft, ChevronDown, File, Code, List, Plus, ChevronRight, ChevronUp, Search, LoaderCircle, Check, RefreshCw, Undo, Redo, Bold, Italic, Underline, Strikethrough, Highlighter, Palette, CheckSquare, Quote, Terminal, Minus, AlignCenter, AlignRight, Link, Minimize2, Maximize2, Grid3X3, FolderPlus, FolderInput, Folder, Trash2, Film, Music, FileText, Archive, Download, Crop, CodeXml, Info, TriangleAlert, ShieldAlert, CircleCheck, History, CheckCircle2, User, Eye, GitCompare, Undo2, Settings, Shield, Hexagon, Network, Sun, Moon, LogOut, ArrowRight, LayoutDashboard, ArrowUpRight, Webhook, Zap, Send, Pause, Play, ExternalLink, Key, Lock, EyeOff, CirclePlay, RefreshCcw, Save, UserPlus, LockOpen } from 'lucide-react';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { useEditor, EditorContent } from '@tiptap/react';
8
8
  import StarterKit from '@tiptap/starter-kit';
@@ -465,7 +465,7 @@ function PageHeader({
465
465
  children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsx("path", { d: "M19 12H5M12 19l-7-7 7-7" }) })
466
466
  }
467
467
  ),
468
- breadcrumbs?.map((crumb, i) => /* @__PURE__ */ jsxs(React54.Fragment, { children: [
468
+ breadcrumbs?.map((crumb, i) => /* @__PURE__ */ jsxs(React56.Fragment, { children: [
469
469
  i > 0 && /* @__PURE__ */ jsx("span", { className: "opacity-20 text-[10px]", children: "/" }),
470
470
  crumb.href || crumb.onClick ? /* @__PURE__ */ jsx(
471
471
  "a",
@@ -489,7 +489,7 @@ function PageHeader({
489
489
  ] }),
490
490
  description && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
491
491
  /* @__PURE__ */ jsx("p", { className: "text-[var(--kyro-text-secondary)] font-medium opacity-60 line-clamp-1", children: description }),
492
- metadata && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: metadata.map((item, i) => /* @__PURE__ */ jsxs(React54.Fragment, { children: [
492
+ metadata && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: metadata.map((item, i) => /* @__PURE__ */ jsxs(React56.Fragment, { children: [
493
493
  i === 0 && /* @__PURE__ */ jsx("span", { className: "opacity-20 ml-1", children: "\xB7" }),
494
494
  item
495
495
  ] }, i)) }),
@@ -555,6 +555,89 @@ function CountBadge({ count, max = 99 }) {
555
555
  if (count === 0) return null;
556
556
  return /* @__PURE__ */ 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 });
557
557
  }
558
+ function Button({
559
+ variant = "secondary",
560
+ size = "md",
561
+ loading = false,
562
+ children,
563
+ className = "",
564
+ disabled,
565
+ ...props
566
+ }) {
567
+ const baseClass = "kyro-btn";
568
+ const variantClass = `kyro-btn-${variant}`;
569
+ const sizeClass = `kyro-btn-${size}`;
570
+ return /* @__PURE__ */ jsxs(
571
+ "button",
572
+ {
573
+ type: "button",
574
+ className: `${baseClass} ${variantClass} ${sizeClass} ${className}`,
575
+ disabled: disabled || loading,
576
+ ...props,
577
+ children: [
578
+ loading ? /* @__PURE__ */ jsx("span", { className: "kyro-btn-loading", children: /* @__PURE__ */ jsx("svg", { className: "kyro-btn-spinner", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", strokeDasharray: "60 30" }) }) }) : null,
579
+ /* @__PURE__ */ jsx("span", { className: loading ? "kyro-btn-text-hidden" : "", children })
580
+ ]
581
+ }
582
+ );
583
+ }
584
+ function Pagination({ page, totalPages, totalDocs, limit, onPageChange, onLimitChange }) {
585
+ if (totalPages <= 1) return null;
586
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-t border-[var(--kyro-border)]", children: [
587
+ totalDocs !== void 0 && limit ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--kyro-text-secondary)] font-medium", children: [
588
+ "Showing ",
589
+ (page - 1) * limit + 1,
590
+ " to ",
591
+ Math.min(page * limit, totalDocs),
592
+ " of ",
593
+ totalDocs
594
+ ] }) : /* @__PURE__ */ jsx("span", {}),
595
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
596
+ onLimitChange && /* @__PURE__ */ jsxs(
597
+ "select",
598
+ {
599
+ value: limit,
600
+ onChange: (e) => onLimitChange(Number(e.target.value)),
601
+ className: "text-xs border border-[var(--kyro-border)] rounded-lg px-2 py-1 bg-[var(--kyro-bg)] text-[var(--kyro-text-secondary)]",
602
+ children: [
603
+ /* @__PURE__ */ jsx("option", { value: 10, children: "10/page" }),
604
+ /* @__PURE__ */ jsx("option", { value: 25, children: "25/page" }),
605
+ /* @__PURE__ */ jsx("option", { value: 50, children: "50/page" }),
606
+ /* @__PURE__ */ jsx("option", { value: 100, children: "100/page" })
607
+ ]
608
+ }
609
+ ),
610
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--kyro-text-secondary)] font-medium", children: [
611
+ "Page ",
612
+ page,
613
+ " of ",
614
+ totalPages
615
+ ] }),
616
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
617
+ /* @__PURE__ */ jsx(
618
+ Button,
619
+ {
620
+ variant: "ghost",
621
+ size: "sm",
622
+ disabled: page <= 1,
623
+ onClick: () => onPageChange(page - 1),
624
+ children: "\u2190 Previous"
625
+ }
626
+ ),
627
+ /* @__PURE__ */ jsx(
628
+ Button,
629
+ {
630
+ variant: "ghost",
631
+ size: "sm",
632
+ disabled: page >= totalPages,
633
+ onClick: () => onPageChange(page + 1),
634
+ children: "Next \u2192"
635
+ }
636
+ )
637
+ ] })
638
+ ] })
639
+ ] });
640
+ }
558
641
 
559
642
  // src/lib/resolve-field-value.ts
560
643
  function resolveFieldValue(fields2, formData, fieldName) {
@@ -724,9 +807,9 @@ function ListView({
724
807
  const displayFields = useMemo(
725
808
  () => {
726
809
  const fields2 = allFields.filter((f) => !!f.name && visibleColumns.has(f.name));
727
- if (visibleColumns.has("publishStatus")) {
810
+ if (visibleColumns.has("status")) {
728
811
  fields2.push({
729
- name: "publishStatus",
812
+ name: "status",
730
813
  type: "select",
731
814
  label: "Status",
732
815
  options: [
@@ -865,24 +948,7 @@ function ListView({
865
948
  ),
866
949
  /* @__PURE__ */ jsxs("div", { className: "surface-tile p-4 flex flex-col lg:flex-row gap-4 items-start lg:items-center", children: [
867
950
  /* @__PURE__ */ jsxs("div", { className: "relative flex-1 max-w-md", children: [
868
- /* @__PURE__ */ jsx(
869
- "svg",
870
- {
871
- className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--kyro-text-secondary)]",
872
- fill: "none",
873
- stroke: "currentColor",
874
- viewBox: "0 0 24 24",
875
- children: /* @__PURE__ */ jsx(
876
- "path",
877
- {
878
- strokeLinecap: "round",
879
- strokeLinejoin: "round",
880
- strokeWidth: "2",
881
- d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
882
- }
883
- )
884
- }
885
- ),
951
+ /* @__PURE__ */ jsx(Search, { className: "w-4 h-4" }),
886
952
  /* @__PURE__ */ jsx(
887
953
  "input",
888
954
  {
@@ -902,24 +968,7 @@ function ListView({
902
968
  onClick: () => setShowFilters(!showFilters),
903
969
  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)]"}`,
904
970
  children: [
905
- /* @__PURE__ */ jsx(
906
- "svg",
907
- {
908
- className: "w-4 h-4",
909
- fill: "none",
910
- stroke: "currentColor",
911
- viewBox: "0 0 24 24",
912
- children: /* @__PURE__ */ jsx(
913
- "path",
914
- {
915
- strokeLinecap: "round",
916
- strokeLinejoin: "round",
917
- strokeWidth: "2",
918
- 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"
919
- }
920
- )
921
- }
922
- ),
971
+ /* @__PURE__ */ jsx(Filter, { className: "w-4 h-4" }),
923
972
  "Filters",
924
973
  filters.length > 0 && /* @__PURE__ */ 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 })
925
974
  ]
@@ -933,24 +982,7 @@ function ListView({
933
982
  onClick: () => setShowColumns(!showColumns),
934
983
  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",
935
984
  children: [
936
- /* @__PURE__ */ jsx(
937
- "svg",
938
- {
939
- className: "w-4 h-4",
940
- fill: "none",
941
- stroke: "currentColor",
942
- viewBox: "0 0 24 24",
943
- children: /* @__PURE__ */ jsx(
944
- "path",
945
- {
946
- strokeLinecap: "round",
947
- strokeLinejoin: "round",
948
- strokeWidth: "2",
949
- 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"
950
- }
951
- )
952
- }
953
- ),
985
+ /* @__PURE__ */ jsx(Columns3, { className: "w-4 h-4" }),
954
986
  "Columns"
955
987
  ]
956
988
  }
@@ -1002,24 +1034,7 @@ function ListView({
1002
1034
  onClick: addFilter,
1003
1035
  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",
1004
1036
  children: [
1005
- /* @__PURE__ */ jsx(
1006
- "svg",
1007
- {
1008
- className: "w-4 h-4",
1009
- fill: "none",
1010
- stroke: "currentColor",
1011
- viewBox: "0 0 24 24",
1012
- children: /* @__PURE__ */ jsx(
1013
- "path",
1014
- {
1015
- strokeLinecap: "round",
1016
- strokeLinejoin: "round",
1017
- strokeWidth: "2",
1018
- d: "M12 5v14M5 12h14"
1019
- }
1020
- )
1021
- }
1022
- ),
1037
+ /* @__PURE__ */ jsx(Plus, { className: "w-4 h-4" }),
1023
1038
  "Add Filter"
1024
1039
  ]
1025
1040
  }
@@ -1070,24 +1085,7 @@ function ListView({
1070
1085
  type: "button",
1071
1086
  onClick: () => removeFilter(index),
1072
1087
  className: "p-2 text-[var(--kyro-text-muted)] hover:text-red-500 transition-colors",
1073
- children: /* @__PURE__ */ jsx(
1074
- "svg",
1075
- {
1076
- className: "w-4 h-4",
1077
- fill: "none",
1078
- stroke: "currentColor",
1079
- viewBox: "0 0 24 24",
1080
- children: /* @__PURE__ */ jsx(
1081
- "path",
1082
- {
1083
- strokeLinecap: "round",
1084
- strokeLinejoin: "round",
1085
- strokeWidth: "2",
1086
- d: "M6 18L18 6M6 6l12 12"
1087
- }
1088
- )
1089
- }
1090
- )
1088
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
1091
1089
  }
1092
1090
  )
1093
1091
  ] }, index)),
@@ -1107,24 +1105,7 @@ function ListView({
1107
1105
  onClick: handleBulkDelete,
1108
1106
  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",
1109
1107
  children: [
1110
- /* @__PURE__ */ jsx(
1111
- "svg",
1112
- {
1113
- className: "w-4 h-4",
1114
- fill: "none",
1115
- stroke: "currentColor",
1116
- viewBox: "0 0 24 24",
1117
- children: /* @__PURE__ */ jsx(
1118
- "path",
1119
- {
1120
- strokeLinecap: "round",
1121
- strokeLinejoin: "round",
1122
- strokeWidth: "2",
1123
- 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"
1124
- }
1125
- )
1126
- }
1127
- ),
1108
+ /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
1128
1109
  "Delete Selected"
1129
1110
  ]
1130
1111
  }
@@ -1141,24 +1122,7 @@ function ListView({
1141
1122
  ] })
1142
1123
  ] }),
1143
1124
  /* @__PURE__ */ jsx("div", { className: "surface-tile overflow-hidden", children: loading ? /* @__PURE__ */ jsx("div", { className: "space-y-2 p-4", children: /* @__PURE__ */ jsx(Shimmer, { variant: "table-row", count: 8 }) }) : docs.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-8", children: [
1144
- /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(
1145
- "svg",
1146
- {
1147
- className: "w-8 h-8 text-[var(--kyro-text-muted)]",
1148
- fill: "none",
1149
- stroke: "currentColor",
1150
- viewBox: "0 0 24 24",
1151
- children: /* @__PURE__ */ jsx(
1152
- "path",
1153
- {
1154
- strokeLinecap: "round",
1155
- strokeLinejoin: "round",
1156
- strokeWidth: "1.5",
1157
- 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"
1158
- }
1159
- )
1160
- }
1161
- ) }),
1125
+ /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(Archive, { className: "w-4 h-4" }) }),
1162
1126
  /* @__PURE__ */ jsx("p", { className: "font-medium text-[var(--kyro-text-primary)] text-base", children: "No documents found" }),
1163
1127
  /* @__PURE__ */ 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()}.` }),
1164
1128
  !hasActiveFilters && canCreate && /* @__PURE__ */ jsxs(
@@ -1168,24 +1132,7 @@ function ListView({
1168
1132
  onClick: handleCreate,
1169
1133
  className: "mt-4 kyro-btn kyro-btn-md kyro-btn-primary shadow-md flex items-center gap-2",
1170
1134
  children: [
1171
- /* @__PURE__ */ jsx(
1172
- "svg",
1173
- {
1174
- className: "w-3.5 h-3.5",
1175
- fill: "none",
1176
- stroke: "currentColor",
1177
- viewBox: "0 0 24 24",
1178
- children: /* @__PURE__ */ jsx(
1179
- "path",
1180
- {
1181
- strokeLinecap: "round",
1182
- strokeLinejoin: "round",
1183
- strokeWidth: "2.5",
1184
- d: "M12 5v14M5 12h14"
1185
- }
1186
- )
1187
- }
1188
- ),
1135
+ /* @__PURE__ */ jsx(Plus, { className: "w-4 h-4" }),
1189
1136
  "Create",
1190
1137
  " ",
1191
1138
  String(collection.singularLabel || collection.label || collectionSlug)
@@ -1210,24 +1157,7 @@ function ListView({
1210
1157
  onClick: () => handleSort(field3.name),
1211
1158
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1212
1159
  checkTabbedValue(displayFields, field3.type) ?? (field3.label || field3.name),
1213
- sort && sort.field === field3.name && /* @__PURE__ */ jsx(
1214
- "svg",
1215
- {
1216
- className: `w-3 h-3 ${sort.direction === "desc" ? "rotate-180" : ""}`,
1217
- fill: "none",
1218
- stroke: "currentColor",
1219
- viewBox: "0 0 24 24",
1220
- children: /* @__PURE__ */ jsx(
1221
- "path",
1222
- {
1223
- strokeLinecap: "round",
1224
- strokeLinejoin: "round",
1225
- strokeWidth: "2",
1226
- d: "M5 15l7-7 7 7"
1227
- }
1228
- )
1229
- }
1230
- )
1160
+ sort && sort.field === field3.name && /* @__PURE__ */ jsx(ChevronUp, { className: "w-4 h-4" })
1231
1161
  ] })
1232
1162
  },
1233
1163
  field3.name
@@ -1290,24 +1220,7 @@ function ListView({
1290
1220
  onClick: () => handleEdit(doc.id),
1291
1221
  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",
1292
1222
  title: canUpdate ? "Edit" : "View",
1293
- children: /* @__PURE__ */ jsx(
1294
- "svg",
1295
- {
1296
- className: "w-4 h-4",
1297
- fill: "none",
1298
- stroke: "currentColor",
1299
- viewBox: "0 0 24 24",
1300
- children: /* @__PURE__ */ jsx(
1301
- "path",
1302
- {
1303
- strokeLinecap: "round",
1304
- strokeLinejoin: "round",
1305
- strokeWidth: "2",
1306
- 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"
1307
- }
1308
- )
1309
- }
1310
- )
1223
+ children: /* @__PURE__ */ jsx(Pencil, { className: "w-4 h-4" })
1311
1224
  }
1312
1225
  ),
1313
1226
  canDelete && /* @__PURE__ */ jsx(
@@ -1317,24 +1230,7 @@ function ListView({
1317
1230
  onClick: () => handleDeleteSingle(doc.id),
1318
1231
  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",
1319
1232
  title: "Delete",
1320
- children: /* @__PURE__ */ jsx(
1321
- "svg",
1322
- {
1323
- className: "w-4 h-4",
1324
- fill: "none",
1325
- stroke: "currentColor",
1326
- viewBox: "0 0 24 24",
1327
- children: /* @__PURE__ */ jsx(
1328
- "path",
1329
- {
1330
- strokeLinecap: "round",
1331
- strokeLinejoin: "round",
1332
- strokeWidth: "2",
1333
- 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"
1334
- }
1335
- )
1336
- }
1337
- )
1233
+ children: /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" })
1338
1234
  }
1339
1235
  )
1340
1236
  ] })
@@ -1345,60 +1241,20 @@ function ListView({
1345
1241
  doc.id
1346
1242
  )) })
1347
1243
  ] }) }) }),
1348
- totalDocs > limit && /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row items-center justify-between gap-4 px-2", children: [
1349
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
1350
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-[var(--kyro-text-secondary)] font-medium", children: [
1351
- "Showing",
1352
- " ",
1353
- /* @__PURE__ */ jsx("span", { className: "text-[var(--kyro-text-primary)] font-bold", children: (page - 1) * limit + 1 }),
1354
- " ",
1355
- "to",
1356
- " ",
1357
- /* @__PURE__ */ jsx("span", { className: "text-[var(--kyro-text-primary)] font-bold", children: Math.min(page * limit, totalDocs) }),
1358
- " ",
1359
- "of",
1360
- " ",
1361
- /* @__PURE__ */ jsx("span", { className: "text-[var(--kyro-text-primary)] font-bold", children: totalDocs })
1362
- ] }),
1363
- /* @__PURE__ */ jsxs(
1364
- "select",
1365
- {
1366
- value: limit,
1367
- onChange: (e) => {
1368
- setLimit(Number(e.target.value));
1369
- setPage(1);
1370
- },
1371
- 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)]",
1372
- children: [
1373
- /* @__PURE__ */ jsx("option", { value: 10, children: "10 / page" }),
1374
- /* @__PURE__ */ jsx("option", { value: 25, children: "25 / page" }),
1375
- /* @__PURE__ */ jsx("option", { value: 50, children: "50 / page" }),
1376
- /* @__PURE__ */ jsx("option", { value: 100, children: "100 / page" })
1377
- ]
1378
- }
1379
- )
1380
- ] }),
1381
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1382
- page > 1 && /* @__PURE__ */ jsx(
1383
- "button",
1384
- {
1385
- type: "button",
1386
- onClick: () => setPage(page - 1),
1387
- 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",
1388
- children: "\u2190 Previous"
1389
- }
1390
- ),
1391
- page < totalPages && /* @__PURE__ */ jsx(
1392
- "button",
1393
- {
1394
- type: "button",
1395
- onClick: () => setPage(page + 1),
1396
- 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",
1397
- children: "Next \u2192"
1398
- }
1399
- )
1400
- ] })
1401
- ] })
1244
+ /* @__PURE__ */ jsx(
1245
+ Pagination,
1246
+ {
1247
+ page,
1248
+ totalPages,
1249
+ totalDocs,
1250
+ limit,
1251
+ onPageChange: setPage,
1252
+ onLimitChange: (newLimit) => {
1253
+ setLimit(newLimit);
1254
+ setPage(1);
1255
+ }
1256
+ }
1257
+ )
1402
1258
  ] });
1403
1259
  }
1404
1260
  function formatCellValue(value, type) {
@@ -2108,18 +1964,7 @@ function SlidePanel({
2108
1964
  type: "button",
2109
1965
  onClick: onClose,
2110
1966
  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",
2111
- children: /* @__PURE__ */ jsx(
2112
- "svg",
2113
- {
2114
- width: "16",
2115
- height: "16",
2116
- viewBox: "0 0 24 24",
2117
- fill: "none",
2118
- stroke: "currentColor",
2119
- strokeWidth: "2",
2120
- children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
2121
- }
2122
- )
1967
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
2123
1968
  }
2124
1969
  )
2125
1970
  ] }),
@@ -2140,54 +1985,158 @@ function SlidePanel({
2140
1985
  document.body
2141
1986
  );
2142
1987
  }
2143
- function PromptModal({
1988
+ function ModalContent({ children }) {
1989
+ return /* @__PURE__ */ jsx("div", { className: "text-[var(--kyro-text-secondary)]", children });
1990
+ }
1991
+ function ModalActions({ children }) {
1992
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-3 mt-6", children });
1993
+ }
1994
+ function Modal({
2144
1995
  open,
2145
1996
  onClose,
2146
- onSubmit,
2147
1997
  title,
2148
- placeholder = "",
2149
- defaultValue = ""
1998
+ children,
1999
+ footer,
2000
+ size = "md",
2001
+ variant = "default"
2150
2002
  }) {
2151
- const [value, setValue] = useState(defaultValue);
2152
- const handleSubmit = (e) => {
2153
- e.preventDefault();
2154
- if (value.trim()) {
2155
- onSubmit(value.trim());
2156
- setValue("");
2157
- onClose();
2003
+ useEffect(() => {
2004
+ const handleEscape = (e) => {
2005
+ if (e.key === "Escape") onClose();
2006
+ };
2007
+ if (open) {
2008
+ document.addEventListener("keydown", handleEscape);
2009
+ document.body.style.overflow = "hidden";
2158
2010
  }
2159
- };
2011
+ return () => {
2012
+ document.removeEventListener("keydown", handleEscape);
2013
+ document.body.style.overflow = "";
2014
+ };
2015
+ }, [open, onClose]);
2160
2016
  if (!open) return null;
2017
+ const sizeClasses = {
2018
+ sm: "max-w-sm",
2019
+ md: "max-w-md",
2020
+ lg: "max-w-lg",
2021
+ full: "w-full h-full max-w-none rounded-none border-0"
2022
+ };
2023
+ const isLightbox = variant === "lightbox";
2161
2024
  return createPortal(
2162
- /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center", children: [
2025
+ /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center p-4", children: [
2163
2026
  /* @__PURE__ */ jsx(
2164
2027
  "div",
2165
2028
  {
2166
- className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
2029
+ className: `absolute inset-0 transition-all duration-500 ${isLightbox ? "bg-black/95 backdrop-blur-none" : "bg-[var(--kyro-black)]/40 backdrop-blur-md"}`,
2167
2030
  onClick: onClose
2168
2031
  }
2169
2032
  ),
2170
- /* @__PURE__ */ 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: [
2171
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--kyro-border)]", children: [
2172
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--kyro-text-primary)]", children: title }),
2173
- /* @__PURE__ */ jsx(
2174
- "button",
2175
- {
2176
- type: "button",
2177
- onClick: onClose,
2178
- className: "p-1 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-colors",
2179
- children: /* @__PURE__ */ jsx(
2180
- "svg",
2181
- {
2182
- width: "20",
2183
- height: "20",
2184
- viewBox: "0 0 24 24",
2185
- fill: "none",
2186
- stroke: "currentColor",
2187
- strokeWidth: "2",
2188
- children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
2033
+ /* @__PURE__ */ jsxs(
2034
+ "div",
2035
+ {
2036
+ 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`,
2037
+ children: [
2038
+ !isLightbox && /* @__PURE__ */ 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: [
2039
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold text-[var(--kyro-text-primary)]", children: title }),
2040
+ /* @__PURE__ */ jsx(
2041
+ "button",
2042
+ {
2043
+ type: "button",
2044
+ onClick: onClose,
2045
+ 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",
2046
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
2189
2047
  }
2190
2048
  )
2049
+ ] }),
2050
+ /* @__PURE__ */ jsx("div", { className: `flex-1 overflow-auto ${isLightbox ? "" : "px-8 py-8"}`, children }),
2051
+ footer && !isLightbox && /* @__PURE__ */ 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 })
2052
+ ]
2053
+ }
2054
+ )
2055
+ ] }),
2056
+ document.body
2057
+ );
2058
+ }
2059
+ function ConfirmModal({
2060
+ open,
2061
+ onClose,
2062
+ onConfirm,
2063
+ title,
2064
+ message,
2065
+ confirmLabel = "Confirm",
2066
+ cancelLabel = "Cancel",
2067
+ variant = "default",
2068
+ loading = false
2069
+ }) {
2070
+ return /* @__PURE__ */ jsx(
2071
+ Modal,
2072
+ {
2073
+ open,
2074
+ onClose,
2075
+ title,
2076
+ size: "sm",
2077
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
2078
+ /* @__PURE__ */ jsx(
2079
+ "button",
2080
+ {
2081
+ type: "button",
2082
+ onClick: onClose,
2083
+ disabled: loading,
2084
+ className: "kyro-btn kyro-btn-md kyro-btn-secondary",
2085
+ children: cancelLabel
2086
+ }
2087
+ ),
2088
+ /* @__PURE__ */ jsx(
2089
+ "button",
2090
+ {
2091
+ type: "button",
2092
+ onClick: onConfirm,
2093
+ disabled: loading,
2094
+ className: `kyro-btn kyro-btn-md ${variant === "danger" ? "kyro-btn-danger" : "kyro-btn-primary"}`,
2095
+ children: loading ? "Loading..." : confirmLabel
2096
+ }
2097
+ )
2098
+ ] }),
2099
+ children: /* @__PURE__ */ jsx("p", { className: "text-[var(--kyro-text-secondary)]", children: message })
2100
+ }
2101
+ );
2102
+ }
2103
+ function PromptModal({
2104
+ open,
2105
+ onClose,
2106
+ onSubmit,
2107
+ title,
2108
+ placeholder = "",
2109
+ defaultValue = ""
2110
+ }) {
2111
+ const [value, setValue] = useState(defaultValue);
2112
+ const handleSubmit = (e) => {
2113
+ e.preventDefault();
2114
+ if (value.trim()) {
2115
+ onSubmit(value.trim());
2116
+ setValue("");
2117
+ onClose();
2118
+ }
2119
+ };
2120
+ if (!open) return null;
2121
+ return createPortal(
2122
+ /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center", children: [
2123
+ /* @__PURE__ */ jsx(
2124
+ "div",
2125
+ {
2126
+ className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
2127
+ onClick: onClose
2128
+ }
2129
+ ),
2130
+ /* @__PURE__ */ 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: [
2131
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--kyro-border)]", children: [
2132
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--kyro-text-primary)]", children: title }),
2133
+ /* @__PURE__ */ jsx(
2134
+ "button",
2135
+ {
2136
+ type: "button",
2137
+ onClick: onClose,
2138
+ className: "p-1 text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-colors",
2139
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
2191
2140
  }
2192
2141
  )
2193
2142
  ] }),
@@ -2567,24 +2516,7 @@ function MediaGallery({
2567
2516
  ] }) }),
2568
2517
  /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 flex-wrap lg:flex-nowrap ${pickerMode ? "w-full" : ""}`, children: [
2569
2518
  /* @__PURE__ */ jsxs("div", { className: "relative group flex-1 min-w-[200px]", children: [
2570
- /* @__PURE__ */ jsx(
2571
- "svg",
2572
- {
2573
- 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",
2574
- fill: "none",
2575
- stroke: "currentColor",
2576
- viewBox: "0 0 24 24",
2577
- children: /* @__PURE__ */ jsx(
2578
- "path",
2579
- {
2580
- strokeLinecap: "round",
2581
- strokeLinejoin: "round",
2582
- strokeWidth: "2.5",
2583
- d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
2584
- }
2585
- )
2586
- }
2587
- ),
2519
+ /* @__PURE__ */ jsx(Search, { className: "w-4 h-4" }),
2588
2520
  /* @__PURE__ */ jsx(
2589
2521
  "input",
2590
2522
  {
@@ -2751,45 +2683,11 @@ function MediaGallery({
2751
2683
  {
2752
2684
  onClick: (e) => handleSelectOne(item.id, e),
2753
2685
  className: `kyro-btn-primary p-1.5 rounded-lg transition-all ${selectedIds.has(item.id) ? "" : "bg-white/10 text-white hover:bg-white/20"}`,
2754
- children: /* @__PURE__ */ jsx(
2755
- "svg",
2756
- {
2757
- className: "w-3 h-3",
2758
- fill: "none",
2759
- stroke: "currentColor",
2760
- viewBox: "0 0 24 24",
2761
- children: /* @__PURE__ */ jsx(
2762
- "path",
2763
- {
2764
- strokeLinecap: "round",
2765
- strokeLinejoin: "round",
2766
- strokeWidth: "3",
2767
- d: "M5 13l4 4L19 7"
2768
- }
2769
- )
2770
- }
2771
- )
2686
+ children: /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" })
2772
2687
  }
2773
2688
  ) })
2774
2689
  ] }) }),
2775
- selectedIds.has(item.id) && /* @__PURE__ */ 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__ */ 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__ */ jsx(
2783
- "path",
2784
- {
2785
- strokeLinecap: "round",
2786
- strokeLinejoin: "round",
2787
- strokeWidth: "3",
2788
- d: "M5 13l4 4L19 7"
2789
- }
2790
- )
2791
- }
2792
- ) })
2690
+ selectedIds.has(item.id) && /* @__PURE__ */ 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__ */ jsx(Check, { className: "w-4 h-4" }) })
2793
2691
  ]
2794
2692
  },
2795
2693
  item.id
@@ -2869,34 +2767,14 @@ function MediaGallery({
2869
2767
  item.id
2870
2768
  )) })
2871
2769
  ] }) }) }),
2872
- totalPages > 1 && /* @__PURE__ */ 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: [
2873
- /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-bold tracking-widest text-[var(--kyro-text-secondary)] opacity-50", children: [
2874
- "Page ",
2770
+ /* @__PURE__ */ jsx(
2771
+ Pagination,
2772
+ {
2875
2773
  page,
2876
- " of ",
2877
- totalPages
2878
- ] }),
2879
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2880
- /* @__PURE__ */ jsx(
2881
- "button",
2882
- {
2883
- disabled: page === 1,
2884
- onClick: () => setPage(page - 1),
2885
- 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",
2886
- children: "Previous"
2887
- }
2888
- ),
2889
- /* @__PURE__ */ jsx(
2890
- "button",
2891
- {
2892
- disabled: page === totalPages,
2893
- onClick: () => setPage(page + 1),
2894
- 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",
2895
- children: "Next"
2896
- }
2897
- )
2898
- ] })
2899
- ] })
2774
+ totalPages,
2775
+ onPageChange: setPage
2776
+ }
2777
+ )
2900
2778
  ] })
2901
2779
  ] }),
2902
2780
  !pickerMode && uploading && /* @__PURE__ */ jsx("div", { className: "fixed bottom-12 left-1/2 -translate-x-1/2 z-[60] w-full max-w-lg", children: /* @__PURE__ */ 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: [
@@ -3122,86 +3000,100 @@ function MediaGallery({
3122
3000
  ] })
3123
3001
  }
3124
3002
  ),
3125
- showPreview && panelItem && createPortal(
3126
- /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999] bg-black/95 flex flex-col animate-in fade-in duration-500", children: [
3127
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6", children: [
3128
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3129
- /* @__PURE__ */ jsx("span", { className: "text-white font-bold text-lg tracking-tight", children: panelItem.filename }),
3130
- /* @__PURE__ */ jsxs("span", { className: "text-white/40 text-[10px] font-bold tracking-widest mt-1", children: [
3131
- formatFileSize(panelItem.fileSize),
3132
- " \xB7 ",
3133
- panelItem.mimeType
3134
- ] })
3135
- ] }),
3136
- /* @__PURE__ */ jsx(
3137
- "button",
3138
- {
3139
- onClick: () => setShowPreview(false),
3140
- className: "p-3 bg-white/10 hover:bg-white/20 text-white rounded-2xl transition-all active:scale-90",
3141
- children: /* @__PURE__ */ jsx(X, { className: "w-6 h-6" })
3142
- }
3143
- )
3144
- ] }),
3145
- /* @__PURE__ */ jsx("div", { className: "flex-1 w-full flex items-center justify-center p-12", children: panelItem.type === "image" ? /* @__PURE__ */ jsx(
3146
- "img",
3147
- {
3148
- src: getAbsoluteUrl(panelItem.url),
3149
- alt: "",
3150
- className: "max-h-full max-w-full object-contain shadow-2xl rounded-lg animate-in zoom-in-95 duration-500"
3151
- }
3152
- ) : panelItem.type === "video" ? /* @__PURE__ */ jsx(
3153
- "video",
3154
- {
3155
- src: getAbsoluteUrl(panelItem.url),
3156
- controls: true,
3157
- autoPlay: true,
3158
- className: "max-h-full max-w-full rounded-lg shadow-2xl"
3159
- }
3160
- ) : /* @__PURE__ */ jsxs("div", { className: "text-white text-center", children: [
3161
- /* @__PURE__ */ jsx(File, { className: "w-24 h-24 mx-auto mb-6 opacity-20" }),
3162
- /* @__PURE__ */ jsx("p", { className: "text-xl font-bold opacity-50", children: "Preview not available for this file type" })
3163
- ] }) })
3164
- ] }),
3165
- document.body
3166
- ),
3167
- !pickerMode && showCrop && panelItem && createPortal(
3168
- /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999] bg-black/95 flex flex-col p-8", children: [
3169
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-8", children: [
3170
- /* @__PURE__ */ jsx("h3", { className: "text-white font-bold text-2xl tracking-tighter", children: "Crop Image" }),
3171
- /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
3172
- /* @__PURE__ */ jsx(
3173
- "button",
3174
- {
3175
- type: "button",
3176
- onClick: () => setShowCrop(false),
3177
- className: "px-4 py-2 border border-white/20 text-white/80 hover:bg-white/10 rounded-lg font-bold text-sm transition-colors",
3178
- children: "Cancel"
3179
- }
3180
- ),
3003
+ showPreview && panelItem && /* @__PURE__ */ jsxs(
3004
+ Modal,
3005
+ {
3006
+ open: showPreview,
3007
+ onClose: () => setShowPreview(false),
3008
+ title: "",
3009
+ size: "full",
3010
+ variant: "lightbox",
3011
+ children: [
3012
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6", children: [
3013
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3014
+ /* @__PURE__ */ jsx("span", { className: "text-white font-bold text-lg tracking-tight", children: panelItem.filename }),
3015
+ /* @__PURE__ */ jsxs("span", { className: "text-white/40 text-[10px] font-bold tracking-widest mt-1", children: [
3016
+ formatFileSize(panelItem.fileSize),
3017
+ " \xB7 ",
3018
+ panelItem.mimeType
3019
+ ] })
3020
+ ] }),
3181
3021
  /* @__PURE__ */ jsx(
3182
3022
  "button",
3183
3023
  {
3184
- type: "button",
3185
- disabled: uploading,
3186
- onClick: onCropComplete,
3187
- 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",
3188
- children: uploading ? "Saving..." : "Save Crop"
3024
+ onClick: () => setShowPreview(false),
3025
+ className: "p-3 bg-white/10 hover:bg-white/20 text-white rounded-2xl transition-all active:scale-90",
3026
+ children: /* @__PURE__ */ jsx(X, { className: "w-6 h-6" })
3189
3027
  }
3190
3028
  )
3191
- ] })
3192
- ] }),
3193
- /* @__PURE__ */ jsx("div", { className: "flex-1 w-full flex items-center justify-center overflow-auto", children: /* @__PURE__ */ jsx(ReactCrop, { crop, onChange: (c) => setCrop(c), children: /* @__PURE__ */ jsx(
3194
- "img",
3195
- {
3196
- ref: imgRef,
3197
- src: panelItem.url,
3198
- alt: "Crop preview",
3199
- className: "max-h-[70vh] object-contain",
3200
- onLoad: onImageLoad
3201
- }
3202
- ) }) })
3203
- ] }),
3204
- document.body
3029
+ ] }),
3030
+ /* @__PURE__ */ jsx("div", { className: "flex-1 w-full flex items-center justify-center p-12", children: panelItem.type === "image" ? /* @__PURE__ */ jsx(
3031
+ "img",
3032
+ {
3033
+ src: getAbsoluteUrl(panelItem.url),
3034
+ alt: "",
3035
+ className: "max-h-full max-w-full object-contain shadow-2xl rounded-lg animate-in zoom-in-95 duration-500"
3036
+ }
3037
+ ) : panelItem.type === "video" ? /* @__PURE__ */ jsx(
3038
+ "video",
3039
+ {
3040
+ src: getAbsoluteUrl(panelItem.url),
3041
+ controls: true,
3042
+ autoPlay: true,
3043
+ className: "max-h-full max-w-full rounded-lg shadow-2xl"
3044
+ }
3045
+ ) : /* @__PURE__ */ jsxs("div", { className: "text-white text-center", children: [
3046
+ /* @__PURE__ */ jsx(File, { className: "w-24 h-24 mx-auto mb-6 opacity-20" }),
3047
+ /* @__PURE__ */ jsx("p", { className: "text-xl font-bold opacity-50", children: "Preview not available for this file type" })
3048
+ ] }) })
3049
+ ]
3050
+ }
3051
+ ),
3052
+ !pickerMode && showCrop && panelItem && /* @__PURE__ */ jsx(
3053
+ Modal,
3054
+ {
3055
+ open: showCrop,
3056
+ onClose: () => setShowCrop(false),
3057
+ title: "",
3058
+ size: "full",
3059
+ variant: "lightbox",
3060
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full p-8", children: [
3061
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-8", children: [
3062
+ /* @__PURE__ */ jsx("h3", { className: "text-white font-bold text-2xl tracking-tighter", children: "Crop Image" }),
3063
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
3064
+ /* @__PURE__ */ jsx(
3065
+ "button",
3066
+ {
3067
+ type: "button",
3068
+ onClick: () => setShowCrop(false),
3069
+ className: "px-4 py-2 border border-white/20 text-white/80 hover:bg-white/10 rounded-lg font-bold text-sm transition-colors",
3070
+ children: "Cancel"
3071
+ }
3072
+ ),
3073
+ /* @__PURE__ */ jsx(
3074
+ "button",
3075
+ {
3076
+ type: "button",
3077
+ disabled: uploading,
3078
+ onClick: onCropComplete,
3079
+ 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",
3080
+ children: uploading ? "Saving..." : "Save Crop"
3081
+ }
3082
+ )
3083
+ ] })
3084
+ ] }),
3085
+ /* @__PURE__ */ jsx("div", { className: "flex-1 w-full flex items-center justify-center overflow-auto", children: /* @__PURE__ */ jsx(ReactCrop, { crop, onChange: (c) => setCrop(c), children: /* @__PURE__ */ jsx(
3086
+ "img",
3087
+ {
3088
+ ref: imgRef,
3089
+ src: panelItem.url,
3090
+ alt: "Crop preview",
3091
+ className: "max-h-[70vh] object-contain",
3092
+ onLoad: onImageLoad
3093
+ }
3094
+ ) }) })
3095
+ ] })
3096
+ }
3205
3097
  ),
3206
3098
  !pickerMode && /* @__PURE__ */ jsx(
3207
3099
  PromptModal,
@@ -3213,61 +3105,49 @@ function MediaGallery({
3213
3105
  placeholder: "Folder name"
3214
3106
  }
3215
3107
  ),
3216
- !pickerMode && showStorageConfigModal && createPortal(
3217
- /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[9999] bg-black/80 flex items-center justify-center p-4", children: /* @__PURE__ */ 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__ */ jsxs("div", { className: "text-center", children: [
3218
- /* @__PURE__ */ 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__ */ jsx(
3219
- "svg",
3220
- {
3221
- className: "w-8 h-8 text-[var(--kyro-sidebar-text-active)]",
3222
- fill: "none",
3223
- stroke: "currentColor",
3224
- viewBox: "0 0 24 24",
3225
- children: /* @__PURE__ */ jsx(
3226
- "path",
3108
+ !pickerMode && /* @__PURE__ */ jsx(
3109
+ Modal,
3110
+ {
3111
+ open: showStorageConfigModal,
3112
+ onClose: () => setShowStorageConfigModal(false),
3113
+ title: "Storage Not Configured",
3114
+ size: "md",
3115
+ children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
3116
+ /* @__PURE__ */ 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__ */ jsx(Server, { className: "w-8 h-8 text-[var(--kyro-sidebar-text-active)]" }) }),
3117
+ /* @__PURE__ */ 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." }),
3118
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
3119
+ /* @__PURE__ */ jsx(
3120
+ "a",
3121
+ {
3122
+ href: "/settings/storage-settings",
3123
+ 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",
3124
+ children: "Configure Storage"
3125
+ }
3126
+ ),
3127
+ /* @__PURE__ */ jsx(
3128
+ "button",
3227
3129
  {
3228
- strokeLinecap: "round",
3229
- strokeLinejoin: "round",
3230
- strokeWidth: 2,
3231
- 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"
3130
+ type: "button",
3131
+ onClick: () => {
3132
+ apiPost("/api/globals/storage-settings", {
3133
+ provider: "local",
3134
+ local: {
3135
+ uploadDir: "./public/uploads",
3136
+ baseUrl: "/uploads"
3137
+ }
3138
+ }).then(() => {
3139
+ setShowStorageConfigModal(false);
3140
+ setStorageConfigured(true);
3141
+ window.location.reload();
3142
+ });
3143
+ },
3144
+ 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",
3145
+ children: "Use Defaults"
3232
3146
  }
3233
3147
  )
3234
- }
3235
- ) }),
3236
- /* @__PURE__ */ jsx("h3", { className: "text-xl font-bold text-[var(--kyro-text-primary)] mb-2", children: "Storage Not Configured" }),
3237
- /* @__PURE__ */ 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." }),
3238
- /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
3239
- /* @__PURE__ */ jsx(
3240
- "a",
3241
- {
3242
- href: "/settings/storage-settings",
3243
- 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",
3244
- children: "Configure Storage"
3245
- }
3246
- ),
3247
- /* @__PURE__ */ jsx(
3248
- "button",
3249
- {
3250
- type: "button",
3251
- onClick: () => {
3252
- apiPost("/api/globals/storage-settings", {
3253
- provider: "local",
3254
- local: {
3255
- uploadDir: "./public/uploads",
3256
- baseUrl: "/uploads"
3257
- }
3258
- }).then(() => {
3259
- setShowStorageConfigModal(false);
3260
- setStorageConfigured(true);
3261
- window.location.reload();
3262
- });
3263
- },
3264
- 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",
3265
- children: "Use Defaults"
3266
- }
3267
- )
3148
+ ] })
3268
3149
  ] })
3269
- ] }) }) }),
3270
- document.body
3150
+ }
3271
3151
  ),
3272
3152
  !pickerMode && /* @__PURE__ */ jsx(
3273
3153
  "input",
@@ -4861,6 +4741,7 @@ var useAutoFormStore = create()(
4861
4741
  loadingDiffs: false,
4862
4742
  isAutoSaving: false,
4863
4743
  autoSaveStatus: "idle",
4744
+ backgroundProcessing: false,
4864
4745
  // Auto-save state
4865
4746
  lastAutoSaveTime: 0,
4866
4747
  lastSavedAt: null,
@@ -4936,6 +4817,7 @@ var useAutoFormStore = create()(
4936
4817
  setLoadingDiffs: (loading) => set({ loadingDiffs: loading }),
4937
4818
  setIsAutoSaving: (saving) => set({ isAutoSaving: saving }),
4938
4819
  setAutoSaveStatus: (status) => set({ autoSaveStatus: status }),
4820
+ setBackgroundProcessing: (processing) => set({ backgroundProcessing: processing }),
4939
4821
  setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
4940
4822
  setLastSavedAt: (time) => set({ lastSavedAt: time }),
4941
4823
  setRetryCount: (count) => set({ retryCount: count }),
@@ -5432,6 +5314,14 @@ function SelectField({
5432
5314
  }
5433
5315
  );
5434
5316
  }
5317
+ function EmptyState({ icon, title, description, action }) {
5318
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 justify-center py-16 px-8", children: [
5319
+ icon && /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center mb-4", children: icon }),
5320
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-[var(--kyro-text-primary)] text-base", children: title }),
5321
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--kyro-text-secondary)] mt-1", children: description }),
5322
+ action
5323
+ ] });
5324
+ }
5435
5325
  function getLabel(opt) {
5436
5326
  const mainTabs = opt?.mainTabs;
5437
5327
  return opt?.title || mainTabs?.title || opt?.name || opt?.label || opt?.email || opt?.filename || opt?.slug || "Untitled";
@@ -5647,7 +5537,7 @@ function RelationshipField({
5647
5537
  }
5648
5538
  ) })
5649
5539
  ] }),
5650
- isOpen && /* @__PURE__ */ 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__ */ jsx("div", { className: "p-4 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-sm text-[var(--kyro-text-muted)]", children: "No results found" }) : /* @__PURE__ */ jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxs(
5540
+ isOpen && /* @__PURE__ */ 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__ */ jsx("div", { className: "p-4 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { title: "No results found" }) : /* @__PURE__ */ jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxs(
5651
5541
  "button",
5652
5542
  {
5653
5543
  type: "button",
@@ -6234,7 +6124,7 @@ var ListField = ({
6234
6124
  onChange,
6235
6125
  compact = false
6236
6126
  }) => {
6237
- const [inputValue, setInputValue] = React54.useState("");
6127
+ const [inputValue, setInputValue] = React56.useState("");
6238
6128
  const handleAdd = () => {
6239
6129
  if (inputValue.trim()) {
6240
6130
  onChange([...items, inputValue.trim()]);
@@ -6490,7 +6380,7 @@ var AccordionField = ({
6490
6380
  onChange,
6491
6381
  compact = false
6492
6382
  }) => {
6493
- const [openIndex, setOpenIndex] = React54.useState(0);
6383
+ const [openIndex, setOpenIndex] = React56.useState(0);
6494
6384
  const handleTitleChange = (index, value) => {
6495
6385
  const newItems = [...items];
6496
6386
  newItems[index] = { ...newItems[index], title: value };
@@ -6916,7 +6806,7 @@ function ArrayLayout({
6916
6806
  const firstField = fields2[0];
6917
6807
  const labelField = firstField?.name || "user";
6918
6808
  const isRelationship = firstField?.type === "relationship";
6919
- const [openIndex, setOpenIndex] = React54.useState(0);
6809
+ const [openIndex, setOpenIndex] = React56.useState(0);
6920
6810
  function getItemLabel(item) {
6921
6811
  for (const key of ["label", "title", "name"]) {
6922
6812
  const val = item[key];
@@ -7271,7 +7161,7 @@ var FieldRenderer = ({
7271
7161
  {
7272
7162
  field: field3,
7273
7163
  value,
7274
- onChange,
7164
+ onChange: onChangeKeystroke,
7275
7165
  disabled,
7276
7166
  error
7277
7167
  }
@@ -8281,7 +8171,7 @@ var RelationshipBlockField = ({
8281
8171
  ),
8282
8172
  /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-1/2 -translate-y-1/2", children: loading && /* @__PURE__ */ jsx(LoaderCircle, { className: "w-4 h-4 text-[var(--kyro-text-muted)] animate-spin" }) })
8283
8173
  ] }),
8284
- isOpen && /* @__PURE__ */ 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__ */ jsx("div", { className: "p-3 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-3 text-center text-sm text-[var(--kyro-text-muted)]", children: "No results found" }) : /* @__PURE__ */ jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxs(
8174
+ isOpen && /* @__PURE__ */ 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__ */ jsx("div", { className: "p-3 text-center text-sm text-[var(--kyro-text-muted)]", children: "Loading..." }) : options.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { title: "No results found" }) : /* @__PURE__ */ jsx("div", { className: "py-1", children: options.map((opt) => /* @__PURE__ */ jsxs(
8285
8175
  "button",
8286
8176
  {
8287
8177
  type: "button",
@@ -8731,7 +8621,7 @@ var SortableBlockComponent = ({
8731
8621
  )
8732
8622
  ] });
8733
8623
  };
8734
- var SortableBlock = React54.memo(SortableBlockComponent);
8624
+ var SortableBlock = React56.memo(SortableBlockComponent);
8735
8625
  var BlocksField = ({
8736
8626
  field: field3,
8737
8627
  value,
@@ -8810,10 +8700,7 @@ var BlocksField = ({
8810
8700
  useEffect(() => {
8811
8701
  const valueArray = Array.isArray(value) ? value : [];
8812
8702
  const lastValueArray = lastValueRef.current || [];
8813
- const valueIds = valueArray.map((b) => b.id).join(",");
8814
- const lastValueIds = lastValueArray.map((b) => b.id).join(",");
8815
- if (valueIds !== lastValueIds) {
8816
- console.log("BlocksField sync: value=", value, "valueIds=", valueIds, "lastValueIds=", lastValueIds);
8703
+ if (JSON.stringify(valueArray) !== JSON.stringify(lastValueArray)) {
8817
8704
  const valueArrayCopy = [...valueArray];
8818
8705
  prevBlocksLengthRef.current = valueArrayCopy.length;
8819
8706
  prevBlockIdsRef.current = new Set(valueArrayCopy.map((b) => b.id));
@@ -8822,6 +8709,7 @@ var BlocksField = ({
8822
8709
  isInitializedRef.current = true;
8823
8710
  } else if (valueArray.length === 0 && !isInitializedRef.current) {
8824
8711
  isInitializedRef.current = true;
8712
+ lastValueRef.current = [];
8825
8713
  }
8826
8714
  }, [value, field3.name, store]);
8827
8715
  const onChangeRef = useRef(onChange);
@@ -8830,9 +8718,8 @@ var BlocksField = ({
8830
8718
  if (!onChangeRef.current) return;
8831
8719
  const lastValue = lastValueRef.current;
8832
8720
  if (!lastValue) return;
8833
- const currentIds = blocks2.map((b) => b.id).join(",");
8834
- const lastIds = lastValue.map((b) => b.id).join(",");
8835
- if (currentIds !== lastIds) {
8721
+ if (JSON.stringify(blocks2) !== JSON.stringify(lastValue)) {
8722
+ lastValueRef.current = [...blocks2];
8836
8723
  onChangeRef.current(blocks2);
8837
8724
  }
8838
8725
  }, [blocks2]);
@@ -9067,11 +8954,44 @@ function normalizeUploadFields(value) {
9067
8954
  }
9068
8955
  return value;
9069
8956
  }
9070
- function useAutoFormState({
9071
- config,
9072
- initialData,
9073
- collectionSlug,
9074
- globalSlug,
8957
+ function useQueue() {
8958
+ const queue = useRef([]);
8959
+ const isProcessing = useRef(false);
8960
+ const queueTask = useCallback((fn, options) => {
8961
+ queue.current.push(fn);
8962
+ async function processQueue() {
8963
+ if (isProcessing.current) return;
8964
+ if (typeof options?.beforeProcess === "function") {
8965
+ const shouldContinue = options.beforeProcess();
8966
+ if (shouldContinue === false) return;
8967
+ }
8968
+ while (queue.current.length > 0) {
8969
+ const latestTask = queue.current.pop();
8970
+ queue.current = [];
8971
+ isProcessing.current = true;
8972
+ try {
8973
+ await latestTask();
8974
+ } catch (err) {
8975
+ console.error("Error in queued function:", err);
8976
+ } finally {
8977
+ isProcessing.current = false;
8978
+ if (typeof options?.afterProcess === "function") {
8979
+ options.afterProcess();
8980
+ }
8981
+ }
8982
+ }
8983
+ }
8984
+ processQueue();
8985
+ }, []);
8986
+ return { queueTask };
8987
+ }
8988
+
8989
+ // src/hooks/useAutoFormState.ts
8990
+ function useAutoFormState({
8991
+ config,
8992
+ initialData,
8993
+ collectionSlug,
8994
+ globalSlug,
9075
8995
  onChange,
9076
8996
  onActionSuccess,
9077
8997
  onActionError
@@ -9117,6 +9037,9 @@ function useAutoFormState({
9117
9037
  const lastAutoSaveTimeRef = useRef(0);
9118
9038
  const autoSaveSkipRef = useRef(false);
9119
9039
  const restorePromptedRef = useRef(null);
9040
+ const previousFormDataRef = useRef("");
9041
+ const astroSyncDataRef = useRef("");
9042
+ const { queueTask } = useQueue();
9120
9043
  const getDocumentKey = useCallback(
9121
9044
  (id) => {
9122
9045
  if (globalSlug) return `global:${globalSlug}`;
@@ -9136,23 +9059,6 @@ function useAutoFormState({
9136
9059
  },
9137
9060
  [lastSavedData.updatedAt, setDraftCache]
9138
9061
  );
9139
- const clearDraftArtifacts = useCallback(async () => {
9140
- const state = useAutoFormStore.getState();
9141
- const documentKey = getDocumentKey(state.formData.id);
9142
- if (documentKey) {
9143
- clearDraftCache(documentKey);
9144
- }
9145
- const draftUrl = globalSlug ? resolveApi(`/api/globals/${globalSlug}/draft`) : collectionSlug && state.formData.id ? resolveApi(`/api/${collectionSlug}/${state.formData.id}/draft`) : null;
9146
- if (draftUrl && versionsEnabled) {
9147
- try {
9148
- await fetchWithAuth(draftUrl, {
9149
- method: "DELETE"
9150
- });
9151
- } catch (err) {
9152
- console.error("Failed to clear draft snapshot:", err);
9153
- }
9154
- }
9155
- }, [clearDraftCache, collectionSlug, globalSlug, getDocumentKey]);
9156
9062
  const fetchVersions = useCallback(async () => {
9157
9063
  const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}/versions`) : collectionSlug && formData.id ? resolveApi(`/api/${collectionSlug}/${formData.id}/versions`) : null;
9158
9064
  if (!url) return;
@@ -9177,7 +9083,7 @@ function useAutoFormState({
9177
9083
  persistBrowserDraft(documentKey, latestFormData);
9178
9084
  }
9179
9085
  }, [collectionSlug, getDocumentKey, persistBrowserDraft]);
9180
- const performServerAutoSave = useCallback(async (options) => {
9086
+ const doAutosaveFetch = useCallback(async (options) => {
9181
9087
  const state = useAutoFormStore.getState();
9182
9088
  const latestFormData = state.formData;
9183
9089
  const currentLastSaved = state.lastSavedData;
@@ -9194,29 +9100,22 @@ function useAutoFormState({
9194
9100
  }
9195
9101
  setIsAutoSaving(true);
9196
9102
  setAutoSaveStatus("saving");
9103
+ state.setBackgroundProcessing(true);
9197
9104
  try {
9198
- const draftUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
9199
- const draftUrl = globalSlug ? resolveApi(`/api/globals/${globalSlug}/draft`) : resolveApi(`/api/${collectionSlug}/${latestFormData.id}/draft`);
9200
- const delta = state.getDirtyData();
9201
- const idempotencyKey = `${documentKey || "global"}:${Date.now()}`;
9202
- const response = await fetchWithAuth(
9203
- draftUrl,
9204
- {
9205
- method: "PUT",
9206
- headers: {
9207
- "Content-Type": "application/json",
9208
- "X-Idempotency-Key": idempotencyKey
9209
- },
9210
- keepalive: options?.keepalive,
9211
- body: JSON.stringify({
9212
- delta: normalizeUploadFields(delta),
9213
- baseUpdatedAt: currentLastSaved.updatedAt ?? null,
9214
- draftUpdatedAt
9215
- })
9216
- }
9217
- );
9105
+ const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}?autosave=true`) : resolveApi(`/api/${collectionSlug}/${latestFormData.id}?autosave=true`);
9106
+ const response = await fetchWithAuth(url, {
9107
+ method: "PATCH",
9108
+ headers: {
9109
+ "Content-Type": "application/json",
9110
+ "X-Draft": "true"
9111
+ },
9112
+ keepalive: options?.keepalive,
9113
+ body: JSON.stringify({
9114
+ ...normalizeUploadFields(latestFormData),
9115
+ baseUpdatedAt: currentLastSaved.updatedAt ?? null
9116
+ })
9117
+ });
9218
9118
  if (response.ok) {
9219
- const result = await response.json();
9220
9119
  lastAutoSaveTimeRef.current = Date.now();
9221
9120
  state.setRetryCount(0);
9222
9121
  state.setLastSavedAt(Date.now());
@@ -9225,8 +9124,8 @@ function useAutoFormState({
9225
9124
  setDraftCache(documentKey, {
9226
9125
  data: latestFormData,
9227
9126
  baseUpdatedAt: currentLastSaved.updatedAt ?? null,
9228
- draftUpdatedAt: result.data?.draftUpdatedAt || draftUpdatedAt,
9229
- lastSyncedAt: result.data?.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
9127
+ draftUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9128
+ lastSyncedAt: (await response.clone().json()).data?.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
9230
9129
  });
9231
9130
  }
9232
9131
  setAutoSaveStatus("success");
@@ -9235,24 +9134,27 @@ function useAutoFormState({
9235
9134
  setAutoSaveStatus("idle");
9236
9135
  }
9237
9136
  }, 2e3);
9137
+ } else if (response.status === 409) {
9138
+ setAutoSaveStatus("conflict");
9238
9139
  } else {
9239
9140
  throw new Error(`Draft auto-save failed with status ${response.status}`);
9240
9141
  }
9241
9142
  } catch (err) {
9242
9143
  console.error("Auto-save failed:", err);
9243
- const state2 = useAutoFormStore.getState();
9244
- const currentRetryCount = state2.retryCount;
9144
+ const currentState = useAutoFormStore.getState();
9145
+ const currentRetryCount = currentState.retryCount;
9245
9146
  if (currentRetryCount < 5) {
9246
- state2.setRetryCount(currentRetryCount + 1);
9147
+ currentState.setRetryCount(currentRetryCount + 1);
9247
9148
  setAutoSaveStatus("retrying");
9248
9149
  const delay = Math.min(1e3 * Math.pow(2, currentRetryCount), 6e4);
9249
9150
  if (retryTimerRef.current) clearTimeout(retryTimerRef.current);
9250
- retryTimerRef.current = setTimeout(() => performServerAutoSave(options), delay);
9151
+ retryTimerRef.current = setTimeout(() => performAutosave(options), delay);
9251
9152
  } else {
9252
9153
  setAutoSaveStatus("offline");
9253
9154
  }
9254
9155
  } finally {
9255
9156
  setIsAutoSaving(false);
9157
+ useAutoFormStore.getState().setBackgroundProcessing(false);
9256
9158
  }
9257
9159
  }, [
9258
9160
  collectionSlug,
@@ -9264,8 +9166,20 @@ function useAutoFormState({
9264
9166
  setIsAutoSaving,
9265
9167
  versionsEnabled
9266
9168
  ]);
9169
+ const performAutosave = useCallback((options) => {
9170
+ queueTask(
9171
+ () => doAutosaveFetch(options),
9172
+ {
9173
+ beforeProcess: () => {
9174
+ return true;
9175
+ },
9176
+ afterProcess: () => {
9177
+ }
9178
+ }
9179
+ );
9180
+ }, [doAutosaveFetch, queueTask]);
9267
9181
  const saveDocument = useCallback(
9268
- async (dataOverride) => {
9182
+ async (dataOverride, isDraft = true) => {
9269
9183
  const state = useAutoFormStore.getState();
9270
9184
  const payload = dataOverride || state.formData;
9271
9185
  const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}`) : resolveApi(`/api/${collectionSlug}/${payload.id}`);
@@ -9273,7 +9187,10 @@ function useAutoFormState({
9273
9187
  url,
9274
9188
  {
9275
9189
  method: "PATCH",
9276
- headers: { "Content-Type": "application/json" },
9190
+ headers: {
9191
+ "Content-Type": "application/json",
9192
+ "X-Draft": String(isDraft)
9193
+ },
9277
9194
  body: JSON.stringify({
9278
9195
  ...normalizeUploadFields(payload),
9279
9196
  baseUpdatedAt: state.lastSavedData.updatedAt ?? null
@@ -9287,36 +9204,6 @@ function useAutoFormState({
9287
9204
  },
9288
9205
  [collectionSlug, globalSlug, setAutoSaveStatus]
9289
9206
  );
9290
- const publishDocument = useCallback(async () => {
9291
- const state = useAutoFormStore.getState();
9292
- const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}/publish`) : resolveApi(`/api/${collectionSlug}/${state.formData.id}/publish`);
9293
- const response = await fetchWithAuth(
9294
- url,
9295
- {
9296
- method: "POST",
9297
- headers: { "Content-Type": "application/json" },
9298
- body: JSON.stringify({
9299
- baseUpdatedAt: state.lastSavedData.updatedAt ?? null
9300
- })
9301
- }
9302
- );
9303
- if (response.status === 409) {
9304
- setAutoSaveStatus("conflict");
9305
- }
9306
- return response;
9307
- }, [collectionSlug, globalSlug, setAutoSaveStatus]);
9308
- const unpublishDocument = useCallback(async () => {
9309
- const state = useAutoFormStore.getState();
9310
- const url = globalSlug ? resolveApi(`/api/globals/${globalSlug}/unpublish`) : resolveApi(`/api/${collectionSlug}/${state.formData.id}/unpublish`);
9311
- const response = await fetchWithAuth(
9312
- url,
9313
- {
9314
- method: "POST",
9315
- headers: { "Content-Type": "application/json" }
9316
- }
9317
- );
9318
- return response;
9319
- }, [collectionSlug, globalSlug]);
9320
9207
  useEffect(() => {
9321
9208
  const handleToggle = () => {
9322
9209
  setSidebarCollapsed(!sidebarCollapsed);
@@ -9325,13 +9212,16 @@ function useAutoFormState({
9325
9212
  return () => window.removeEventListener("toggle-sidebar", handleToggle);
9326
9213
  }, [sidebarCollapsed, setSidebarCollapsed]);
9327
9214
  const lastLoadedSlugRef = useRef(null);
9215
+ const lastInitialDataRef = useRef("");
9328
9216
  const initialDataLoadedRef = useRef(false);
9329
9217
  useEffect(() => {
9330
9218
  const currentSlug = globalSlug || initialData?.id;
9331
- if (initialDataLoadedRef.current && lastLoadedSlugRef.current === currentSlug) return;
9219
+ const serialized = JSON.stringify(initialData);
9220
+ if (initialDataLoadedRef.current && lastLoadedSlugRef.current === currentSlug && lastInitialDataRef.current === serialized) return;
9332
9221
  loadDocument(initialData || {}, initialData || {});
9333
9222
  initialDataLoadedRef.current = true;
9334
9223
  lastLoadedSlugRef.current = currentSlug;
9224
+ lastInitialDataRef.current = serialized;
9335
9225
  }, [collectionSlug, formData.id, globalSlug, initialData, loadDocument]);
9336
9226
  useEffect(() => {
9337
9227
  if (!collectionSlug || !initialData?.id) return;
@@ -9342,24 +9232,9 @@ function useAutoFormState({
9342
9232
  const maybeRestoreDraft = async () => {
9343
9233
  if (!versionsEnabled) return;
9344
9234
  const browserDraft = getDraftCache(documentKey);
9345
- let serverDraft = null;
9346
- try {
9347
- const response = await fetchWithAuth(
9348
- resolveApi(`/api/${collectionSlug}/${initialData.id}/draft`)
9349
- );
9350
- if (response.ok) {
9351
- const result = await response.json();
9352
- serverDraft = result.data || null;
9353
- }
9354
- } catch (err) {
9355
- console.error("Failed to fetch server draft:", err);
9356
- }
9357
- const drafts = [browserDraft, serverDraft].filter(Boolean);
9358
- const candidate = drafts.sort(
9359
- (a, b) => new Date(b.draftUpdatedAt).getTime() - new Date(a.draftUpdatedAt).getTime()
9360
- )[0];
9361
- if (!candidate) return;
9362
- if (JSON.stringify(candidate.data) === JSON.stringify(initialData)) {
9235
+ if (!browserDraft) return;
9236
+ if (JSON.stringify(browserDraft.data) === JSON.stringify(initialData)) {
9237
+ clearDraftCache(documentKey);
9363
9238
  return;
9364
9239
  }
9365
9240
  restorePromptedRef.current = documentKey;
@@ -9371,20 +9246,12 @@ function useAutoFormState({
9371
9246
  onConfirm: async () => {
9372
9247
  if (cancelled) return;
9373
9248
  const currentFormData = useAutoFormStore.getState().formData;
9374
- const mergedData = { ...currentFormData, ...candidate.data };
9249
+ const mergedData = { ...currentFormData, ...browserDraft.data };
9375
9250
  setFormData(mergedData);
9376
9251
  onActionSuccess?.("Recovered autosaved draft");
9377
9252
  },
9378
9253
  onCancel: async () => {
9379
9254
  clearDraftCache(documentKey);
9380
- try {
9381
- await fetchWithAuth(
9382
- resolveApi(`/api/${collectionSlug}/${initialData.id}/draft`),
9383
- { method: "DELETE" }
9384
- );
9385
- } catch (err) {
9386
- console.error("Failed to discard server draft:", err);
9387
- }
9388
9255
  }
9389
9256
  });
9390
9257
  };
@@ -9400,7 +9267,8 @@ function useAutoFormState({
9400
9267
  getDraftCache,
9401
9268
  initialData,
9402
9269
  onActionSuccess,
9403
- setFormData
9270
+ setFormData,
9271
+ versionsEnabled
9404
9272
  ]);
9405
9273
  function findFieldDeep(fields2, name) {
9406
9274
  for (const f of fields2) {
@@ -9450,18 +9318,23 @@ function useAutoFormState({
9450
9318
  if (!state.hasDirtyFields()) return;
9451
9319
  if (getLastChangeSource() !== "keystroke") return;
9452
9320
  setChangeSource("other");
9321
+ const serialized = JSON.stringify(formData);
9322
+ if (serialized === previousFormDataRef.current) return;
9453
9323
  if (localSaveTimerRef.current) clearTimeout(localSaveTimerRef.current);
9454
- if (serverSaveTimerRef.current) clearTimeout(serverSaveTimerRef.current);
9455
9324
  localSaveTimerRef.current = setTimeout(performLocalAutoSave, 1500);
9456
- serverSaveTimerRef.current = setTimeout(performServerAutoSave, 8e3);
9457
- }, [formData, sidebarCollapsed, collectionSlug, globalSlug, performLocalAutoSave, performServerAutoSave]);
9325
+ if (serverSaveTimerRef.current) clearTimeout(serverSaveTimerRef.current);
9326
+ serverSaveTimerRef.current = setTimeout(() => {
9327
+ previousFormDataRef.current = serialized;
9328
+ performAutosave();
9329
+ }, 8e3);
9330
+ }, [formData, sidebarCollapsed, collectionSlug, globalSlug, performLocalAutoSave, performAutosave]);
9458
9331
  useEffect(() => {
9459
9332
  if (!globalSlug && (!collectionSlug || !formData.id)) return;
9460
9333
  const flushDraft = () => {
9461
9334
  if (autoSaveSkipRef.current) return;
9462
9335
  const state = useAutoFormStore.getState();
9463
9336
  if (!state.hasDirtyFields()) return;
9464
- void performServerAutoSave({ keepalive: true });
9337
+ void performAutosave({ keepalive: true });
9465
9338
  };
9466
9339
  const handleVisibilityChange = () => {
9467
9340
  if (document.hidden) {
@@ -9491,11 +9364,14 @@ function useAutoFormState({
9491
9364
  window.removeEventListener("offline", handleOffline);
9492
9365
  document.removeEventListener("visibilitychange", handleVisibilityChange);
9493
9366
  };
9494
- }, [collectionSlug, globalSlug, formData.id, performServerAutoSave]);
9367
+ }, [collectionSlug, globalSlug, formData.id, performAutosave]);
9495
9368
  useEffect(() => {
9369
+ const serialized = JSON.stringify(formData);
9370
+ if (serialized === astroSyncDataRef.current) return;
9371
+ astroSyncDataRef.current = serialized;
9496
9372
  const hiddenInput = document.getElementById("form-data");
9497
9373
  if (hiddenInput) {
9498
- hiddenInput.value = JSON.stringify(formData);
9374
+ hiddenInput.value = serialized;
9499
9375
  }
9500
9376
  onChange?.(formData);
9501
9377
  }, [formData, onChange]);
@@ -9505,18 +9381,14 @@ function useAutoFormState({
9505
9381
  const documentStatus = (() => {
9506
9382
  if (!formData.id && !globalSlug) return "draft";
9507
9383
  if (!versionsEnabled) return "published";
9508
- if (formData.hasDraft) return "draft";
9509
- return formData.publishStatus || "published";
9384
+ return formData.status || "published";
9510
9385
  })();
9511
- const hasUnpublishedChanges = (!!formData.id || !!globalSlug) && (documentStatus !== "published" || !!formData.hasDraft);
9386
+ const hasUnpublishedChanges = (!!formData.id || !!globalSlug) && documentStatus === "draft";
9512
9387
  return {
9513
9388
  ...store,
9514
9389
  fetchVersions,
9515
- performAutoSave: performServerAutoSave,
9390
+ performAutoSave: performAutosave,
9516
9391
  saveDocument,
9517
- publishDocument,
9518
- unpublishDocument,
9519
- clearDraftArtifacts,
9520
9392
  autoSaveSkipRef,
9521
9393
  lastAutoSaveTimeRef,
9522
9394
  documentStatus,
@@ -9524,129 +9396,120 @@ function useAutoFormState({
9524
9396
  versionsEnabled
9525
9397
  };
9526
9398
  }
9527
- function ModalContent({ children }) {
9528
- return /* @__PURE__ */ jsx("div", { className: "text-[var(--kyro-text-secondary)]", children });
9529
- }
9530
- function ModalActions({ children }) {
9531
- return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-3 mt-6", children });
9532
- }
9533
- function Modal({
9534
- open,
9535
- onClose,
9536
- title,
9399
+ function Dropdown({
9400
+ trigger,
9537
9401
  children,
9538
- footer,
9539
- size = "md",
9540
- variant = "default"
9402
+ align = "right",
9403
+ direction = "up"
9541
9404
  }) {
9405
+ const [open, setOpen] = useState(false);
9406
+ const ref = useRef(null);
9542
9407
  useEffect(() => {
9543
- const handleEscape = (e) => {
9544
- if (e.key === "Escape") onClose();
9408
+ const handleClickOutside = (e) => {
9409
+ if (ref.current && !ref.current.contains(e.target)) {
9410
+ setOpen(false);
9411
+ }
9545
9412
  };
9546
9413
  if (open) {
9547
- document.addEventListener("keydown", handleEscape);
9548
- document.body.style.overflow = "hidden";
9414
+ document.addEventListener("mousedown", handleClickOutside);
9549
9415
  }
9550
- return () => {
9551
- document.removeEventListener("keydown", handleEscape);
9552
- document.body.style.overflow = "";
9553
- };
9554
- }, [open, onClose]);
9555
- if (!open) return null;
9556
- const sizeClasses = {
9557
- sm: "max-w-sm",
9558
- md: "max-w-md",
9559
- lg: "max-w-lg"
9560
- };
9561
- return createPortal(
9562
- /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center p-4", children: [
9563
- /* @__PURE__ */ jsx(
9564
- "div",
9565
- {
9566
- className: "absolute inset-0 bg-[var(--kyro-black)]/40 backdrop-blur-md transition-all duration-500",
9567
- onClick: onClose
9568
- }
9569
- ),
9570
- /* @__PURE__ */ jsxs(
9571
- "div",
9572
- {
9573
- 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`,
9574
- children: [
9575
- /* @__PURE__ */ 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: [
9576
- /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold text-[var(--kyro-text-primary)]", children: title }),
9577
- /* @__PURE__ */ jsx(
9578
- "button",
9579
- {
9580
- type: "button",
9581
- onClick: onClose,
9582
- 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",
9583
- children: /* @__PURE__ */ jsx(
9584
- "svg",
9585
- {
9586
- width: "20",
9587
- height: "20",
9588
- viewBox: "0 0 24 24",
9589
- fill: "none",
9590
- stroke: "currentColor",
9591
- strokeWidth: "2.5",
9592
- children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
9593
- }
9594
- )
9595
- }
9596
- )
9597
- ] }),
9598
- /* @__PURE__ */ jsx("div", { className: "px-8 py-8", children }),
9599
- footer && /* @__PURE__ */ 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 })
9600
- ]
9601
- }
9602
- )
9603
- ] }),
9604
- document.body
9605
- );
9416
+ return () => document.removeEventListener("mousedown", handleClickOutside);
9417
+ }, [open]);
9418
+ return /* @__PURE__ */ jsxs("div", { className: "relative", ref, children: [
9419
+ /* @__PURE__ */ jsx("div", { onClick: () => setOpen(!open), className: "cursor-pointer", children: trigger }),
9420
+ open && /* @__PURE__ */ jsx(
9421
+ "div",
9422
+ {
9423
+ 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"}`,
9424
+ onClick: () => setOpen(false),
9425
+ children
9426
+ }
9427
+ )
9428
+ ] });
9606
9429
  }
9607
- function ConfirmModal({
9608
- open,
9609
- onClose,
9610
- onConfirm,
9611
- title,
9612
- message,
9613
- confirmLabel = "Confirm",
9614
- cancelLabel = "Cancel",
9615
- variant = "default",
9616
- loading = false
9430
+ function DropdownItem({
9431
+ children,
9432
+ onClick,
9433
+ icon,
9434
+ danger,
9435
+ disabled,
9436
+ className = ""
9617
9437
  }) {
9618
- return /* @__PURE__ */ jsx(
9619
- Modal,
9438
+ return /* @__PURE__ */ jsx("div", { className: "px-1.5", children: /* @__PURE__ */ jsxs(
9439
+ "button",
9620
9440
  {
9621
- open,
9622
- onClose,
9623
- title,
9624
- size: "sm",
9625
- footer: /* @__PURE__ */ jsxs(Fragment, { children: [
9626
- /* @__PURE__ */ jsx(
9627
- "button",
9628
- {
9629
- type: "button",
9630
- onClick: onClose,
9631
- disabled: loading,
9632
- className: "kyro-btn kyro-btn-md kyro-btn-secondary",
9633
- children: cancelLabel
9634
- }
9635
- ),
9636
- /* @__PURE__ */ jsx(
9637
- "button",
9638
- {
9639
- type: "button",
9640
- onClick: onConfirm,
9641
- disabled: loading,
9642
- className: `kyro-btn kyro-btn-md ${variant === "danger" ? "kyro-btn-danger" : "kyro-btn-primary"}`,
9643
- children: loading ? "Loading..." : confirmLabel
9644
- }
9645
- )
9646
- ] }),
9647
- children: /* @__PURE__ */ jsx("p", { className: "text-[var(--kyro-text-secondary)]", children: message })
9441
+ type: "button",
9442
+ onClick,
9443
+ disabled,
9444
+ 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}`,
9445
+ children: [
9446
+ icon && /* @__PURE__ */ jsx("span", { className: "w-4 h-4 opacity-70", children: icon }),
9447
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children })
9448
+ ]
9648
9449
  }
9649
- );
9450
+ ) });
9451
+ }
9452
+ function DropdownSeparator() {
9453
+ return /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--kyro-border)] opacity-50" });
9454
+ }
9455
+ function SplitButton({
9456
+ status,
9457
+ saveStatus,
9458
+ hasChanges,
9459
+ onPublish,
9460
+ children,
9461
+ disabled,
9462
+ direction = "down"
9463
+ }) {
9464
+ const isPublishedIdle = status === "published" && !hasChanges && saveStatus !== "saving" && saveStatus !== "error";
9465
+ const isDisabled = disabled || saveStatus === "saving" || isPublishedIdle;
9466
+ const btnBase = "kyro-btn kyro-btn-sm text-[11px] font-regular tracking-widest transition-all duration-300 rounded-lg";
9467
+ const getBtnClass = () => {
9468
+ if (saveStatus === "saving") return `${btnBase} bg-[var(--kyro-primary)]/70 border-[var(--kyro-primary)]/70 text-[var(--kyro-sidebar-text-active)] cursor-wait`;
9469
+ if (saveStatus === "saved") return `${btnBase} bg-[var(--kyro-success)] border-[var(--kyro-success)] text-[var(--kyro-sidebar-text-active)]`;
9470
+ if (saveStatus === "error") return `${btnBase} bg-[var(--kyro-error)] border-[var(--kyro-error)] text-[var(--kyro-sidebar-text-active)]`;
9471
+ if (isPublishedIdle) return `${btnBase} bg-[var(--kyro-gray-200)] border-[var(--kyro-gray-200)] text-[var(--kyro-text-muted)] cursor-not-allowed`;
9472
+ return `${btnBase} bg-[var(--kyro-primary)] border-[var(--kyro-primary)] hover:bg-[var(--kyro-primary-hover)]`;
9473
+ };
9474
+ const chevronBase = "kyro-btn kyro-btn-md px-2 rounded-l-none border-l-[1px] border-white/20 transition-all duration-300";
9475
+ const getChevronClass = () => {
9476
+ if (saveStatus === "saving") return `${chevronBase} bg-[var(--kyro-primary)]/70 text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-primary)]/70`;
9477
+ if (saveStatus === "saved") return `${chevronBase} bg-[var(--kyro-success)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-success)]`;
9478
+ if (saveStatus === "error") return `${chevronBase} bg-[var(--kyro-error)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-error)]`;
9479
+ if (isPublishedIdle) return `${chevronBase} bg-[var(--kyro-gray-200)] text-[var(--kyro-text-muted)] border-[var(--kyro-gray-200)]`;
9480
+ return `${chevronBase} bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-primary)] hover:bg-[var(--kyro-primary-hover)]`;
9481
+ };
9482
+ const getLabel2 = () => {
9483
+ if (saveStatus === "saving") return "Publishing...";
9484
+ if (saveStatus === "saved") return "Published \u2713";
9485
+ if (saveStatus === "error") return "Retry";
9486
+ if (isPublishedIdle) return "Published";
9487
+ return "Publish Changes";
9488
+ };
9489
+ return /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center", children: [
9490
+ /* @__PURE__ */ jsxs(
9491
+ "button",
9492
+ {
9493
+ type: "button",
9494
+ onClick: onPublish,
9495
+ disabled: isDisabled,
9496
+ className: `${getBtnClass()} ${!children ? "rounded-r-lg border-r border-[var(--kyro-border)]" : ""}`,
9497
+ children: [
9498
+ saveStatus === "saving" && /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "inline mr-1.5" }),
9499
+ isPublishedIdle && /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", className: "inline mr-1", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }),
9500
+ getLabel2()
9501
+ ]
9502
+ }
9503
+ ),
9504
+ children && /* @__PURE__ */ jsx(
9505
+ Dropdown,
9506
+ {
9507
+ trigger: /* @__PURE__ */ jsx("button", { type: "button", className: getChevronClass(), disabled: saveStatus === "saving", children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) }) }),
9508
+ direction,
9509
+ children
9510
+ }
9511
+ )
9512
+ ] });
9650
9513
  }
9651
9514
  var SeoPreview = ({ title, description, slug }) => /* @__PURE__ */ 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: [
9652
9515
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
@@ -9755,8 +9618,6 @@ function AutoForm({
9755
9618
  setAutoSaveStatus,
9756
9619
  fetchVersions,
9757
9620
  saveDocument,
9758
- publishDocument,
9759
- clearDraftArtifacts,
9760
9621
  autoSaveSkipRef,
9761
9622
  lastAutoSaveTimeRef,
9762
9623
  documentStatus,
@@ -9774,7 +9635,13 @@ function AutoForm({
9774
9635
  const menuRef = useRef(null);
9775
9636
  const scheduleRef = useRef(null);
9776
9637
  const [showSchedulePicker, setShowSchedulePicker] = useState(false);
9638
+ const [localSaveStatus, setLocalSaveStatus] = useState("idle");
9639
+ const [now, setNow] = useState(Date.now());
9777
9640
  const disabled = propDisabled;
9641
+ useEffect(() => {
9642
+ const id = setInterval(() => setNow(Date.now()), 1e4);
9643
+ return () => clearInterval(id);
9644
+ }, []);
9778
9645
  const resolveAdminFlag = (value, currentData) => {
9779
9646
  if (typeof value === "function") {
9780
9647
  try {
@@ -9969,18 +9836,16 @@ function AutoForm({
9969
9836
  message: "Unpublish this document?",
9970
9837
  onConfirm: async () => {
9971
9838
  try {
9972
- const response = await fetchWithAuth(
9973
- resolveApi(`/api/${collectionSlug}/${formData.id}/unpublish`),
9974
- {
9975
- method: "POST"
9976
- }
9839
+ const response = await saveDocument(
9840
+ { ...formData, status: "draft" },
9841
+ false
9977
9842
  );
9978
- if (response.ok) {
9843
+ if (response?.ok) {
9979
9844
  onActionSuccess?.("Document unpublished successfully");
9980
9845
  location.reload();
9981
9846
  } else {
9982
- const error = await response.json();
9983
- toast.error(error.error || "Failed to unpublish");
9847
+ const error = await response?.json().catch(() => ({}));
9848
+ toast.error(error?.error || "Failed to unpublish");
9984
9849
  }
9985
9850
  } catch (err) {
9986
9851
  toast.error("Failed to unpublish");
@@ -9991,12 +9856,7 @@ function AutoForm({
9991
9856
  const handleSaveDraft = async () => {
9992
9857
  const isNewDoc = !formData.id;
9993
9858
  autoSaveSkipRef.current = true;
9994
- const btn = document.getElementById("btn-publish");
9995
- const originalText = btn?.textContent || "";
9996
- if (btn) {
9997
- btn.textContent = "Saving...";
9998
- btn.setAttribute("disabled", "true");
9999
- }
9859
+ setLocalSaveStatus("saving");
10000
9860
  try {
10001
9861
  const data = normalizeUploadFields({ ...formData });
10002
9862
  const isPost = isNewDoc && !globalSlug;
@@ -10012,9 +9872,12 @@ function AutoForm({
10012
9872
  setLastSavedData({ ...formData, ...savedData });
10013
9873
  lastAutoSaveTimeRef.current = Date.now();
10014
9874
  setAutoSaveStatus("success");
10015
- await clearDraftArtifacts();
9875
+ setLocalSaveStatus("saved");
10016
9876
  if (versionsEnabled) fetchVersions();
10017
- setTimeout(() => setAutoSaveStatus("idle"), 5e3);
9877
+ setTimeout(() => {
9878
+ setAutoSaveStatus("idle");
9879
+ setLocalSaveStatus("idle");
9880
+ }, 2e3);
10018
9881
  onActionSuccess?.(
10019
9882
  isPost ? "Document created successfully" : "Changes saved"
10020
9883
  );
@@ -10028,78 +9891,63 @@ function AutoForm({
10028
9891
  if (response.status === 409) {
10029
9892
  setAutoSaveStatus("conflict");
10030
9893
  }
9894
+ setLocalSaveStatus("error");
10031
9895
  toast.error(error.error || "Failed to save");
9896
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10032
9897
  }
10033
9898
  } catch (err) {
9899
+ setLocalSaveStatus("error");
10034
9900
  toast.error("Failed to save document");
9901
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10035
9902
  } finally {
10036
9903
  autoSaveSkipRef.current = false;
10037
- if (btn) {
10038
- btn.textContent = originalText;
10039
- btn.removeAttribute("disabled");
10040
- }
10041
9904
  }
10042
9905
  };
10043
9906
  const handlePublish = async () => {
10044
9907
  const isNewDoc = !formData.id;
10045
9908
  autoSaveSkipRef.current = true;
10046
- const btn = document.getElementById("btn-publish");
10047
- const originalText = btn?.textContent || "";
10048
- if (btn) {
10049
- btn.textContent = "Publishing...";
10050
- btn.setAttribute("disabled", "true");
10051
- }
9909
+ setLocalSaveStatus("saving");
10052
9910
  try {
10053
9911
  if (isNewDoc && !globalSlug) {
10054
- const data = normalizeUploadFields({ ...formData });
9912
+ const data2 = normalizeUploadFields({ ...formData });
10055
9913
  const response2 = await fetchWithAuth(`/api/${collectionSlug}`, {
10056
9914
  method: "POST",
10057
9915
  headers: { "Content-Type": "application/json" },
10058
- body: JSON.stringify(data)
9916
+ body: JSON.stringify(data2)
10059
9917
  });
10060
9918
  if (!response2.ok) {
10061
9919
  const error = await response2.json().catch(() => ({}));
10062
9920
  if (response2.status === 409) setAutoSaveStatus("conflict");
9921
+ setLocalSaveStatus("error");
10063
9922
  toast.error(error.error || "Failed to create document");
9923
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10064
9924
  return;
10065
9925
  }
10066
9926
  const result = await response2.json();
10067
- const savedData = result.data || data;
9927
+ const savedData = result.data || data2;
10068
9928
  setFormData({ ...formData, ...savedData });
10069
9929
  setLastSavedData({ ...formData, ...savedData });
10070
- } else if (hasUnsavedChanges) {
10071
- const response2 = await saveDocument(formData);
10072
- if (!response2.ok) {
10073
- const error = await response2.json().catch(() => ({}));
10074
- if (response2.status === 409) setAutoSaveStatus("conflict");
10075
- toast.error(error.error || "Failed to save before publishing");
10076
- return;
10077
- }
10078
- const result = await response2.json();
10079
- if (result.data) {
10080
- setFormData({ ...formData, ...result.data });
10081
- setLastSavedData({ ...formData, ...result.data });
10082
- }
10083
9930
  }
10084
- const response = await publishDocument();
10085
- if (response.ok) {
10086
- await clearDraftArtifacts();
9931
+ const data = normalizeUploadFields({ ...formData });
9932
+ const response = await saveDocument(data, false);
9933
+ if (response?.ok) {
9934
+ setLocalSaveStatus("saved");
10087
9935
  onActionSuccess?.("Published successfully");
10088
- await new Promise((r) => setTimeout(r, 1500));
9936
+ await new Promise((r) => setTimeout(r, 1e3));
10089
9937
  location.reload();
10090
9938
  } else {
10091
- const error = await response.json();
10092
- if (response.status === 409) setAutoSaveStatus("conflict");
10093
- toast.error(error.error || "Failed to publish");
9939
+ const error = await response?.json().catch(() => ({}));
9940
+ if (response?.status === 409) setAutoSaveStatus("conflict");
9941
+ setLocalSaveStatus("error");
9942
+ toast.error(error?.error || "Failed to publish");
9943
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10094
9944
  }
10095
9945
  } catch (err) {
9946
+ setLocalSaveStatus("error");
10096
9947
  toast.error("Failed to publish");
9948
+ setTimeout(() => setLocalSaveStatus("idle"), 3e3);
10097
9949
  } finally {
10098
9950
  autoSaveSkipRef.current = false;
10099
- if (btn) {
10100
- btn.textContent = originalText;
10101
- btn.removeAttribute("disabled");
10102
- }
10103
9951
  }
10104
9952
  };
10105
9953
  const handleSchedulePublish = async (scheduledFor) => {
@@ -10416,12 +10264,12 @@ function AutoForm({
10416
10264
  const docTitle = String(
10417
10265
  formData.mainTabs?.title || (typeof formData.title === "object" ? "" : formData.title) || (typeof formData.name === "object" ? "" : formData.name) || "Untitled"
10418
10266
  );
10419
- const docStatus = documentStatus ?? formData.publishStatus ?? formData.status ?? "draft";
10267
+ const docStatus = documentStatus ?? formData.status ?? "draft";
10420
10268
  const isNew = !formData.id;
10421
10269
  const lastModified = formData.updatedAt ? new Date(formData.updatedAt).toLocaleString() : "Just now";
10422
10270
  const createdAt = formData.createdAt ? new Date(formData.createdAt).toLocaleString() : "Just now";
10423
- const isDraftMode = !formData.id || documentStatus === "draft" || !!formData.hasDraft;
10424
- const statusLabel = hasUnpublishedChanges ? docStatus === "draft" && !formData._prevStatus ? "Draft" : "Published (unpublished changes)" : docStatus === "published" ? "Published" : "Draft";
10271
+ !formData.id || documentStatus === "draft";
10272
+ const statusLabel = hasUnpublishedChanges ? "Draft (unpublished changes)" : docStatus === "published" ? "Published" : "Draft";
10425
10273
  const statusColor = docStatus === "published" && !hasUnsavedChanges ? "bg-[var(--kyro-success)]" : hasUnpublishedChanges ? "bg-[var(--kyro-warning)]" : "bg-[var(--kyro-text-muted)]";
10426
10274
  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";
10427
10275
  return /* @__PURE__ */ 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: [
@@ -10432,24 +10280,7 @@ function AutoForm({
10432
10280
  {
10433
10281
  href: `/${collectionSlug}`,
10434
10282
  className: "p-2 border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-bg-secondary)] transition-colors",
10435
- children: /* @__PURE__ */ jsx(
10436
- "svg",
10437
- {
10438
- className: "w-4 h-4",
10439
- fill: "none",
10440
- stroke: "currentColor",
10441
- viewBox: "0 0 24 24",
10442
- children: /* @__PURE__ */ jsx(
10443
- "path",
10444
- {
10445
- strokeLinecap: "round",
10446
- strokeLinejoin: "round",
10447
- strokeWidth: "2.5",
10448
- d: "M15 19l-7-7 7-7"
10449
- }
10450
- )
10451
- }
10452
- )
10283
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
10453
10284
  }
10454
10285
  ),
10455
10286
  /* @__PURE__ */ jsx("h1", { className: "text-xl font-bold tracking-tighter", children: docTitle }),
@@ -10492,18 +10323,7 @@ function AutoForm({
10492
10323
  "Saving draft..."
10493
10324
  ] }),
10494
10325
  autoSaveStatus === "success" && /* @__PURE__ */ jsxs("span", { className: "text-[var(--kyro-success)] flex items-center gap-1", children: [
10495
- /* @__PURE__ */ jsx(
10496
- "svg",
10497
- {
10498
- width: "12",
10499
- height: "12",
10500
- viewBox: "0 0 24 24",
10501
- fill: "none",
10502
- stroke: "currentColor",
10503
- strokeWidth: "3",
10504
- children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" })
10505
- }
10506
- ),
10326
+ /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" }),
10507
10327
  lastSavedAt ? `Saved ${Math.floor((Date.now() - lastSavedAt) / 6e4)}m ago` : "Draft saved"
10508
10328
  ] }),
10509
10329
  autoSaveStatus === "retrying" && /* @__PURE__ */ jsxs("span", { className: "text-[var(--kyro-warning)] flex items-center gap-1.5", children: [
@@ -10563,13 +10383,23 @@ function AutoForm({
10563
10383
  onClick: async () => {
10564
10384
  setFormData(lastSavedData);
10565
10385
  markSaved();
10566
- await clearDraftArtifacts();
10567
10386
  },
10568
10387
  className: "text-[var(--kyro-primary)] hover:underline",
10569
10388
  children: "Revert changes"
10570
10389
  }
10571
10390
  )
10572
10391
  ] }),
10392
+ lastSavedAt && autoSaveStatus !== "saving" && autoSaveStatus !== "retrying" && autoSaveStatus !== "success" && /* @__PURE__ */ jsxs("span", { className: "border-l border-[var(--kyro-border)] pl-4", children: [
10393
+ "Draft saved ",
10394
+ (() => {
10395
+ const diffMs = now - lastSavedAt;
10396
+ const diffMin = Math.floor(diffMs / 6e4);
10397
+ const diffSec = Math.floor(diffMs / 1e3);
10398
+ if (diffMin >= 1) return `${diffMin}m ago`;
10399
+ if (diffSec >= 5) return `${diffSec}s ago`;
10400
+ return "just now";
10401
+ })()
10402
+ ] }),
10573
10403
  /* @__PURE__ */ jsxs("span", { className: "border-l border-[var(--kyro-border)] pl-4", children: [
10574
10404
  "Modified ",
10575
10405
  lastModified
@@ -10601,18 +10431,7 @@ function AutoForm({
10601
10431
  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)]"}`,
10602
10432
  title: "Live Preview",
10603
10433
  children: [
10604
- /* @__PURE__ */ jsx(
10605
- "svg",
10606
- {
10607
- width: "20",
10608
- height: "20",
10609
- viewBox: "0 0 24 24",
10610
- fill: "none",
10611
- stroke: "currentColor",
10612
- strokeWidth: "2",
10613
- children: /* @__PURE__ */ 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" })
10614
- }
10615
- ),
10434
+ /* @__PURE__ */ jsx(ExternalLink, { className: "w-4 h-4" }),
10616
10435
  showPreview && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold tracking-widest pr-1", children: "Active" })
10617
10436
  ]
10618
10437
  }
@@ -10643,164 +10462,98 @@ function AutoForm({
10643
10462
  )
10644
10463
  }
10645
10464
  ),
10646
- documentStatus === "published" && !isNew && !hasUnsavedChanges ? /* @__PURE__ */ 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: [
10647
- /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" }) }),
10648
- "Published"
10649
- ] }) : /* @__PURE__ */ jsxs("div", { ref: menuRef, className: "relative flex items-center gap-3", children: [
10650
- /* @__PURE__ */ jsxs("div", { className: "flex items-center rounded-xl overflow-hidden shadow-lg", children: [
10651
- /* @__PURE__ */ jsx(
10465
+ /* @__PURE__ */ jsx(
10466
+ SplitButton,
10467
+ {
10468
+ status: documentStatus,
10469
+ saveStatus: localSaveStatus,
10470
+ hasChanges: hasUnsavedChanges,
10471
+ onPublish: handlePublish,
10472
+ disabled: localSaveStatus === "saving"
10473
+ }
10474
+ ),
10475
+ !isNew && /* @__PURE__ */ jsxs(
10476
+ Dropdown,
10477
+ {
10478
+ trigger: /* @__PURE__ */ jsx(
10652
10479
  "button",
10653
10480
  {
10654
- id: "btn-publish",
10655
10481
  type: "button",
10656
- onClick: isDraftMode ? handleSaveDraft : handlePublish,
10657
- 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"}`,
10658
- children: isDraftMode ? "Save Draft" : "Publish Changes"
10482
+ className: "kyro-btn p-2.5 rounded-xl border border-[var(--kyro-border)] hover:bg-[var(--kyro-bg-secondary)] transition-all",
10483
+ title: "More actions",
10484
+ children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: [
10485
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.5" }),
10486
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.5" }),
10487
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.5" })
10488
+ ] })
10659
10489
  }
10660
10490
  ),
10661
- /* @__PURE__ */ jsx(
10662
- "button",
10663
- {
10664
- type: "button",
10665
- onClick: () => setIsMenuOpen(!isMenuOpen),
10666
- className: `px-2.5 py-2.5 text-xs rounded-r-xl rounded-l-none transition-all ${isDraftMode ? "kyro-btn-primary" : "kyro-btn-success"}`,
10667
- children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" }) })
10668
- }
10669
- )
10670
- ] }),
10671
- isMenuOpen && /* @__PURE__ */ 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: [
10672
- /* @__PURE__ */ jsxs(
10673
- "button",
10674
- {
10675
- type: "button",
10676
- onClick: () => {
10677
- handleSaveDraft();
10678
- setIsMenuOpen(false);
10679
- },
10680
- 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",
10681
- children: [
10682
- /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10683
- /* @__PURE__ */ jsx("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
10684
- /* @__PURE__ */ jsx("polyline", { points: "17 21 17 13 7 13 7 21" }),
10685
- /* @__PURE__ */ jsx("polyline", { points: "7 3 7 8 15 8" })
10491
+ direction: "down",
10492
+ children: [
10493
+ !globalSlug && /* @__PURE__ */ jsx(
10494
+ DropdownItem,
10495
+ {
10496
+ onClick: handleCreateNew,
10497
+ icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10498
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
10499
+ /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
10686
10500
  ] }),
10687
- "Save Draft"
10688
- ]
10689
- }
10690
- ),
10691
- /* @__PURE__ */ jsxs(
10692
- "button",
10693
- {
10694
- type: "button",
10695
- onClick: () => {
10696
- handlePublish();
10697
- setIsMenuOpen(false);
10698
- },
10699
- 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",
10700
- children: [
10701
- /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("polygon", { points: "5 3 19 12 5 21 5 3" }) }),
10702
- "Publish"
10703
- ]
10704
- }
10705
- ),
10706
- /* @__PURE__ */ jsxs(
10707
- "button",
10708
- {
10709
- type: "button",
10710
- onClick: () => {
10711
- setIsMenuOpen(false);
10712
- setShowSchedulePicker(true);
10713
- },
10714
- 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",
10715
- children: [
10716
- /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10501
+ children: "Create New"
10502
+ }
10503
+ ),
10504
+ !globalSlug && /* @__PURE__ */ jsx(
10505
+ DropdownItem,
10506
+ {
10507
+ onClick: handleDuplicate,
10508
+ icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10509
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
10510
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
10511
+ ] }),
10512
+ children: "Duplicate"
10513
+ }
10514
+ ),
10515
+ /* @__PURE__ */ jsx(
10516
+ DropdownItem,
10517
+ {
10518
+ onClick: () => setShowSchedulePicker(true),
10519
+ icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10717
10520
  /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" }),
10718
10521
  /* @__PURE__ */ jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
10719
10522
  /* @__PURE__ */ jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
10720
10523
  /* @__PURE__ */ jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
10721
10524
  ] }),
10722
- "Schedule Publish"
10723
- ]
10724
- }
10725
- ),
10726
- /* @__PURE__ */ jsx("div", { className: "h-px bg-[var(--kyro-border)]" }),
10727
- !globalSlug && /* @__PURE__ */ jsxs(
10728
- "button",
10729
- {
10730
- type: "button",
10731
- onClick: () => {
10732
- handleCreateNew();
10733
- setIsMenuOpen(false);
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__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10738
- /* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
10739
- /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
10740
- ] }),
10741
- "Create New"
10742
- ]
10743
- }
10744
- ),
10745
- !isNew && !globalSlug && /* @__PURE__ */ jsxs(Fragment, { children: [
10746
- /* @__PURE__ */ jsxs(
10747
- "button",
10748
- {
10749
- type: "button",
10750
- onClick: () => {
10751
- handleDuplicate();
10752
- setIsMenuOpen(false);
10753
- },
10754
- 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",
10755
- children: [
10756
- /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10757
- /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
10758
- /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
10759
- ] }),
10760
- "Duplicate"
10761
- ]
10525
+ children: "Schedule Publish"
10762
10526
  }
10763
10527
  ),
10764
- documentStatus === "published" && /* @__PURE__ */ jsxs(
10765
- "button",
10528
+ documentStatus === "published" && /* @__PURE__ */ jsx(
10529
+ DropdownItem,
10766
10530
  {
10767
- type: "button",
10768
- onClick: () => {
10769
- handleUnpublish();
10770
- setIsMenuOpen(false);
10771
- },
10772
- 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",
10773
- children: [
10774
- /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10775
- /* @__PURE__ */ 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" }),
10776
- /* @__PURE__ */ jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
10777
- ] }),
10778
- "Unpublish"
10779
- ]
10531
+ onClick: handleUnpublish,
10532
+ icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10533
+ /* @__PURE__ */ 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" }),
10534
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
10535
+ ] }),
10536
+ children: "Unpublish"
10780
10537
  }
10781
10538
  ),
10782
- /* @__PURE__ */ jsx("div", { className: "h-px bg-[var(--kyro-border)]" }),
10783
- /* @__PURE__ */ jsxs(
10784
- "button",
10785
- {
10786
- type: "button",
10787
- onClick: () => {
10788
- handleDelete();
10789
- setIsMenuOpen(false);
10790
- },
10791
- 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",
10792
- children: [
10793
- /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10539
+ !globalSlug && /* @__PURE__ */ jsxs(Fragment, { children: [
10540
+ /* @__PURE__ */ jsx(DropdownSeparator, {}),
10541
+ /* @__PURE__ */ jsx(
10542
+ DropdownItem,
10543
+ {
10544
+ onClick: handleDelete,
10545
+ danger: true,
10546
+ icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
10794
10547
  /* @__PURE__ */ jsx("polyline", { points: "3 6 5 6 21 6" }),
10795
10548
  /* @__PURE__ */ 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" })
10796
10549
  ] }),
10797
- "Delete"
10798
- ]
10799
- }
10800
- )
10801
- ] })
10802
- ] })
10803
- ] }),
10550
+ children: "Delete"
10551
+ }
10552
+ )
10553
+ ] })
10554
+ ]
10555
+ }
10556
+ ),
10804
10557
  showSchedulePicker && /* @__PURE__ */ jsx("div", { ref: scheduleRef, className: "relative", children: /* @__PURE__ */ 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: [
10805
10558
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium mb-2", children: "Schedule Publish" }),
10806
10559
  /* @__PURE__ */ jsx(
@@ -10920,17 +10673,7 @@ function AutoForm({
10920
10673
  type: "button",
10921
10674
  onClick: () => setCompareDiffs([]),
10922
10675
  className: "p-1 rounded hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-muted)]",
10923
- children: /* @__PURE__ */ jsx(
10924
- "svg",
10925
- {
10926
- className: "w-3.5 h-3.5",
10927
- viewBox: "0 0 24 24",
10928
- fill: "none",
10929
- stroke: "currentColor",
10930
- strokeWidth: "2.5",
10931
- children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
10932
- }
10933
- )
10676
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
10934
10677
  }
10935
10678
  )
10936
10679
  ] }),
@@ -10961,17 +10704,7 @@ function AutoForm({
10961
10704
  "div",
10962
10705
  {
10963
10706
  className: `w-4 h-4 rounded-full border ${isSelected ? "border-[var(--kyro-primary)] bg-[var(--kyro-primary)]" : "border-[var(--kyro-border)]"}`,
10964
- children: isSelected && /* @__PURE__ */ jsx(
10965
- "svg",
10966
- {
10967
- className: "w-full h-full text-white p-0.5",
10968
- viewBox: "0 0 24 24",
10969
- fill: "none",
10970
- stroke: "currentColor",
10971
- strokeWidth: "3",
10972
- children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" })
10973
- }
10974
- )
10707
+ children: isSelected && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" })
10975
10708
  }
10976
10709
  ) : /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-[var(--kyro-text-muted)] w-5", children: versions.length - i }) }),
10977
10710
  /* @__PURE__ */ jsxs("div", { className: "col-span-4 min-w-0", children: [
@@ -11064,18 +10797,7 @@ function AutoForm({
11064
10797
  "div",
11065
10798
  {
11066
10799
  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)]"}`,
11067
- children: item.checked && /* @__PURE__ */ jsx(
11068
- "svg",
11069
- {
11070
- width: "10",
11071
- height: "10",
11072
- viewBox: "0 0 24 24",
11073
- fill: "none",
11074
- stroke: "white",
11075
- strokeWidth: "4",
11076
- children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" })
11077
- }
11078
- )
10800
+ children: item.checked && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" })
11079
10801
  }
11080
10802
  ),
11081
10803
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-[var(--kyro-text-secondary)] group-hover:text-[var(--kyro-text-primary)] transition-colors", children: item.label })
@@ -11110,7 +10832,7 @@ function AutoForm({
11110
10832
  "Saving..."
11111
10833
  ] }),
11112
10834
  autoSaveStatus === "success" && /* @__PURE__ */ jsxs("span", { className: "text-[var(--kyro-success)] flex items-center gap-1", children: [
11113
- /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" }) }),
10835
+ /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" }),
11114
10836
  lastSavedAt ? `Saved ${Math.floor((Date.now() - lastSavedAt) / 6e4)}m ago` : "Saved"
11115
10837
  ] }),
11116
10838
  autoSaveStatus === "retrying" && /* @__PURE__ */ jsxs("span", { className: "text-[var(--kyro-warning)] flex items-center gap-1.5", children: [
@@ -11135,85 +10857,29 @@ function AutoForm({
11135
10857
  type: "button",
11136
10858
  style: { width: 0, height: 0, opacity: 0, padding: 0, margin: 0, border: "none", position: "absolute" },
11137
10859
  onClick: async () => {
11138
- console.log("[AutoForm] Hidden save button clicked");
11139
10860
  try {
11140
10861
  const response = await saveDocument();
11141
10862
  if (response.ok) {
11142
- const result = await response.json();
11143
- const savedData = result.data || formData;
11144
- setFormData({ ...formData, ...savedData });
11145
- setLastSavedData({ ...formData, ...savedData });
11146
- onActionSuccess?.("Changes saved");
11147
- }
11148
- } catch (e) {
11149
- console.error("Save error exception:", e);
11150
- onActionError?.("Save failed: " + e.message);
11151
- }
11152
- }
11153
- }
11154
- )
11155
- ] }),
11156
- /* @__PURE__ */ jsxs("main", { className: "w-full", children: [
11157
- view === "edit" && renderEditView(),
11158
- view === "version" && renderVersionView(),
11159
- view === "api" && renderApiView()
11160
- ] })
11161
- ] });
11162
- }
11163
- function Dropdown({
11164
- trigger,
11165
- children,
11166
- align = "right"
11167
- }) {
11168
- const [open, setOpen] = useState(false);
11169
- const ref = useRef(null);
11170
- useEffect(() => {
11171
- const handleClickOutside = (e) => {
11172
- if (ref.current && !ref.current.contains(e.target)) {
11173
- setOpen(false);
11174
- }
11175
- };
11176
- if (open) {
11177
- document.addEventListener("mousedown", handleClickOutside);
11178
- }
11179
- return () => document.removeEventListener("mousedown", handleClickOutside);
11180
- }, [open]);
11181
- return /* @__PURE__ */ jsxs("div", { className: "relative", ref, children: [
11182
- /* @__PURE__ */ jsx("div", { onClick: () => setOpen(!open), className: "cursor-pointer", children: trigger }),
11183
- open && /* @__PURE__ */ jsx(
11184
- "div",
11185
- {
11186
- 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"}`,
11187
- onClick: () => setOpen(false),
11188
- children
11189
- }
11190
- )
11191
- ] });
11192
- }
11193
- function DropdownItem({
11194
- children,
11195
- onClick,
11196
- icon,
11197
- danger,
11198
- disabled,
11199
- className = ""
11200
- }) {
11201
- return /* @__PURE__ */ jsx("div", { className: "px-1.5", children: /* @__PURE__ */ jsxs(
11202
- "button",
11203
- {
11204
- type: "button",
11205
- onClick,
11206
- disabled,
11207
- 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}`,
11208
- children: [
11209
- icon && /* @__PURE__ */ jsx("span", { className: "w-4 h-4 opacity-70", children: icon }),
11210
- /* @__PURE__ */ jsx("span", { className: "flex-1", children })
11211
- ]
11212
- }
11213
- ) });
11214
- }
11215
- function DropdownSeparator() {
11216
- return /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--kyro-border)] opacity-50" });
10863
+ const result = await response.json();
10864
+ const savedData = result.data || formData;
10865
+ setFormData({ ...formData, ...savedData });
10866
+ setLastSavedData({ ...formData, ...savedData });
10867
+ onActionSuccess?.("Changes saved");
10868
+ }
10869
+ } catch (e) {
10870
+ console.error("Save error exception:", e);
10871
+ onActionError?.("Save failed: " + e.message);
10872
+ }
10873
+ }
10874
+ }
10875
+ )
10876
+ ] }),
10877
+ /* @__PURE__ */ jsxs("main", { className: "w-full", children: [
10878
+ view === "edit" && renderEditView(),
10879
+ view === "version" && renderVersionView(),
10880
+ view === "api" && renderApiView()
10881
+ ] })
10882
+ ] });
11217
10883
  }
11218
10884
  function ActionBar({
11219
10885
  status,
@@ -11236,82 +10902,27 @@ function ActionBar({
11236
10902
  if (hasChanges) return "Unsaved changes";
11237
10903
  return null;
11238
10904
  };
11239
- const getSaveButtonClass = () => {
11240
- const base = "kyro-btn kyro-btn-md text-[11px] font-bold tracking-widest transition-all duration-300";
11241
- if (saveStatus === "saving") return `${base} bg-[var(--kyro-gray-400)] text-white opacity-70 cursor-wait`;
11242
- 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)]`;
11243
- 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)]`;
11244
- 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`;
11245
- return `${base} bg-[var(--kyro-gray-500)] border-[var(--kyro-gray-500)] text-white hover:bg-[var(--kyro-gray-600)]`;
11246
- };
11247
10905
  const getStatusBadge = () => {
11248
10906
  const statusConfig2 = {
11249
10907
  draft: {
11250
10908
  label: "Draft",
11251
10909
  class: "bg-gray-100 text-gray-600 border-gray-200",
11252
- icon: /* @__PURE__ */ jsx(
11253
- "svg",
11254
- {
11255
- width: "12",
11256
- height: "12",
11257
- viewBox: "0 0 24 24",
11258
- fill: "none",
11259
- stroke: "currentColor",
11260
- strokeWidth: "2.5",
11261
- children: /* @__PURE__ */ jsx("path", { d: "M12 2v20M2 12h20" })
11262
- }
11263
- )
10910
+ icon: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3" })
11264
10911
  },
11265
10912
  published: {
11266
10913
  label: "Published",
11267
10914
  class: "bg-green-100 text-green-700 border-green-200",
11268
- icon: /* @__PURE__ */ jsx(
11269
- "svg",
11270
- {
11271
- width: "12",
11272
- height: "12",
11273
- viewBox: "0 0 24 24",
11274
- fill: "none",
11275
- stroke: "currentColor",
11276
- strokeWidth: "2.5",
11277
- children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" })
11278
- }
11279
- )
10915
+ icon: /* @__PURE__ */ jsx(Send, { className: "w-3 h-3" })
11280
10916
  },
11281
10917
  scheduled: {
11282
10918
  label: "Scheduled",
11283
10919
  class: "bg-blue-100 text-blue-700 border-blue-200",
11284
- icon: /* @__PURE__ */ jsxs(
11285
- "svg",
11286
- {
11287
- width: "12",
11288
- height: "12",
11289
- viewBox: "0 0 24 24",
11290
- fill: "none",
11291
- stroke: "currentColor",
11292
- strokeWidth: "2.5",
11293
- children: [
11294
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
11295
- /* @__PURE__ */ jsx("path", { d: "M12 6v6l4 2" })
11296
- ]
11297
- }
11298
- )
10920
+ icon: /* @__PURE__ */ jsx(Clock, { className: "w-3 h-3" })
11299
10921
  },
11300
10922
  archived: {
11301
10923
  label: "Archived",
11302
10924
  class: "bg-yellow-100 text-yellow-700 border-yellow-200",
11303
- icon: /* @__PURE__ */ jsx(
11304
- "svg",
11305
- {
11306
- width: "12",
11307
- height: "12",
11308
- viewBox: "0 0 24 24",
11309
- fill: "none",
11310
- stroke: "currentColor",
11311
- strokeWidth: "2.5",
11312
- children: /* @__PURE__ */ jsx("path", { d: "M21 8v13H3V8M1 3h22v5H1zM10 12h4" })
11313
- }
11314
- )
10925
+ icon: /* @__PURE__ */ jsx(Archive, { className: "w-3 h-3" })
11315
10926
  }
11316
10927
  };
11317
10928
  const config = statusConfig2[status];
@@ -11337,7 +10948,7 @@ function ActionBar({
11337
10948
  getSaveStatusText() && /* @__PURE__ */ jsxs(
11338
10949
  "span",
11339
10950
  {
11340
- className: `text-sm ${saveStatus === "error" ? "text-red-500" : "text-gray-500"}`,
10951
+ className: `text-sm ${saveStatus === "error" ? "text-[var(--kyro-error)]" : "text-[var(--kyro-text-muted)]"}`,
11341
10952
  children: [
11342
10953
  saveStatus === "saving" ? /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "inline mr-1" }) : null,
11343
10954
  getSaveStatusText()
@@ -11346,7 +10957,7 @@ function ActionBar({
11346
10957
  )
11347
10958
  ] }),
11348
10959
  /* @__PURE__ */ jsxs("div", { className: "text-xs space-y-0.5", children: [
11349
- updatedAt && /* @__PURE__ */ jsxs("div", { className: "text-gray-400", children: [
10960
+ updatedAt && /* @__PURE__ */ jsxs("div", { className: "text-[var(--kyro-text-muted)]", children: [
11350
10961
  "Updated: ",
11351
10962
  formatDate2(updatedAt)
11352
10963
  ] }),
@@ -11363,20 +10974,9 @@ function ActionBar({
11363
10974
  type: "button",
11364
10975
  onClick: onPublish,
11365
10976
  disabled: saveStatus === "saving",
11366
- className: "kyro-btn-success hover:opacity-90 kyro-btn-md flex items-center gap-2",
10977
+ className: "kyro-btn kyro-btn-primary kyro-btn-md flex items-center gap-2",
11367
10978
  children: [
11368
- /* @__PURE__ */ jsx(
11369
- "svg",
11370
- {
11371
- width: "16",
11372
- height: "16",
11373
- viewBox: "0 0 24 24",
11374
- fill: "none",
11375
- stroke: "currentColor",
11376
- strokeWidth: "2.5",
11377
- children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" })
11378
- }
11379
- ),
10979
+ /* @__PURE__ */ jsx(Send, { className: "w-4 h-4" }),
11380
10980
  "Publish"
11381
10981
  ]
11382
10982
  }
@@ -11387,114 +10987,39 @@ function ActionBar({
11387
10987
  type: "button",
11388
10988
  onClick: onUnpublish,
11389
10989
  disabled: saveStatus === "saving",
11390
- className: "kyro-btn kyro-btn-warning kyro-btn-md flex items-center gap-2",
10990
+ className: "kyro-btn kyro-btn-secondary kyro-btn-md flex items-center gap-2",
11391
10991
  children: [
11392
- /* @__PURE__ */ jsx(
11393
- "svg",
11394
- {
11395
- width: "16",
11396
- height: "16",
11397
- viewBox: "0 0 24 24",
11398
- fill: "none",
11399
- stroke: "currentColor",
11400
- strokeWidth: "2.5",
11401
- children: /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8M3 3v5h5" })
11402
- }
11403
- ),
10992
+ /* @__PURE__ */ jsx(Undo, { className: "w-4 h-4" }),
11404
10993
  "Unpublish"
11405
10994
  ]
11406
10995
  }
11407
10996
  ),
11408
- status === "published" && !hasChanges && saveStatus !== "saving" && saveStatus !== "error" ? /* @__PURE__ */ 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: [
11409
- /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" }) }),
11410
- "Published"
11411
- ] }) : /* @__PURE__ */ jsx(
11412
- "button",
11413
- {
11414
- type: "button",
11415
- onClick: onSave,
11416
- disabled: saveStatus === "saving" || !hasChanges && saveStatus !== "error",
11417
- className: getSaveButtonClass(),
11418
- children: saveStatus === "saving" ? "Saving..." : saveStatus === "saved" ? "Saved" : hasChanges && status === "published" ? "Save Draft" : status === "draft" && hasChanges ? "Save Draft" : "Save"
11419
- }
11420
- ),
11421
10997
  /* @__PURE__ */ jsxs(
11422
- Dropdown,
10998
+ SplitButton,
11423
10999
  {
11424
- trigger: /* @__PURE__ */ jsx("button", { type: "button", className: "kyro-btn kyro-btn-ghost kyro-btn-md p-2", children: /* @__PURE__ */ jsxs(
11425
- "svg",
11426
- {
11427
- width: "16",
11428
- height: "16",
11429
- viewBox: "0 0 24 24",
11430
- fill: "none",
11431
- stroke: "currentColor",
11432
- strokeWidth: "2",
11433
- children: [
11434
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1" }),
11435
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1" }),
11436
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1" })
11437
- ]
11438
- }
11439
- ) }),
11000
+ status,
11001
+ saveStatus,
11002
+ hasChanges,
11003
+ onPublish: onSave,
11440
11004
  children: [
11441
11005
  onDuplicate && /* @__PURE__ */ jsx(
11442
11006
  DropdownItem,
11443
11007
  {
11444
- onClick: onDuplicate,
11445
- icon: /* @__PURE__ */ jsxs(
11446
- "svg",
11447
- {
11448
- viewBox: "0 0 24 24",
11449
- fill: "none",
11450
- stroke: "currentColor",
11451
- strokeWidth: "2",
11452
- children: [
11453
- /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
11454
- /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
11455
- ]
11456
- }
11457
- ),
11008
+ icon: /* @__PURE__ */ jsx(Copy, { className: "w-4 h-4" }),
11458
11009
  children: "Duplicate"
11459
11010
  }
11460
11011
  ),
11461
11012
  onViewHistory && /* @__PURE__ */ jsx(
11462
11013
  DropdownItem,
11463
11014
  {
11464
- onClick: onViewHistory,
11465
- icon: /* @__PURE__ */ jsxs(
11466
- "svg",
11467
- {
11468
- viewBox: "0 0 24 24",
11469
- fill: "none",
11470
- stroke: "currentColor",
11471
- strokeWidth: "2",
11472
- children: [
11473
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
11474
- /* @__PURE__ */ jsx("polyline", { points: "12,6 12,12 16,14" })
11475
- ]
11476
- }
11477
- ),
11015
+ icon: /* @__PURE__ */ jsx(Clock, { className: "w-4 h-4" }),
11478
11016
  children: "View History"
11479
11017
  }
11480
11018
  ),
11481
11019
  onPreview && /* @__PURE__ */ jsx(
11482
11020
  DropdownItem,
11483
11021
  {
11484
- onClick: onPreview,
11485
- icon: /* @__PURE__ */ jsxs(
11486
- "svg",
11487
- {
11488
- viewBox: "0 0 24 24",
11489
- fill: "none",
11490
- stroke: "currentColor",
11491
- strokeWidth: "2",
11492
- children: [
11493
- /* @__PURE__ */ jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
11494
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
11495
- ]
11496
- }
11497
- ),
11022
+ icon: /* @__PURE__ */ jsx(Eye, { className: "w-4 h-4" }),
11498
11023
  children: "Preview"
11499
11024
  }
11500
11025
  ),
@@ -11504,16 +11029,7 @@ function ActionBar({
11504
11029
  {
11505
11030
  onClick: onDelete,
11506
11031
  danger: true,
11507
- icon: /* @__PURE__ */ jsx(
11508
- "svg",
11509
- {
11510
- viewBox: "0 0 24 24",
11511
- fill: "none",
11512
- stroke: "currentColor",
11513
- strokeWidth: "2",
11514
- children: /* @__PURE__ */ 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" })
11515
- }
11516
- ),
11032
+ icon: /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
11517
11033
  children: "Delete"
11518
11034
  }
11519
11035
  )
@@ -11523,62 +11039,6 @@ function ActionBar({
11523
11039
  ] })
11524
11040
  ] });
11525
11041
  }
11526
- function Toast({ type, message, onClose }) {
11527
- const [isPaused, setIsPaused] = React54.useState(false);
11528
- const timerRef = React54.useRef(null);
11529
- const startTimer = () => {
11530
- if (timerRef.current) clearTimeout(timerRef.current);
11531
- timerRef.current = setTimeout(onClose, 5e3);
11532
- };
11533
- const clearTimer = () => {
11534
- if (timerRef.current) clearTimeout(timerRef.current);
11535
- };
11536
- React54.useEffect(() => {
11537
- if (!isPaused) {
11538
- startTimer();
11539
- } else {
11540
- clearTimer();
11541
- }
11542
- return clearTimer;
11543
- }, [isPaused, onClose]);
11544
- const Icon = {
11545
- success: CircleCheck,
11546
- error: ShieldAlert,
11547
- warning: TriangleAlert,
11548
- info: Info
11549
- }[type];
11550
- return /* @__PURE__ */ jsxs(
11551
- "div",
11552
- {
11553
- className: `kyro-toast kyro-toast-${type} group animate-in fade-in slide-in-from-right-4 duration-300`,
11554
- onMouseEnter: () => setIsPaused(true),
11555
- onMouseLeave: () => setIsPaused(false),
11556
- children: [
11557
- /* @__PURE__ */ jsx("div", { className: "kyro-toast-accent" }),
11558
- /* @__PURE__ */ jsx("div", { className: "kyro-toast-icon-container", children: /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }) }),
11559
- /* @__PURE__ */ jsx("div", { className: "kyro-toast-content", children: /* @__PURE__ */ jsx("p", { className: "kyro-toast-message", children: message }) }),
11560
- /* @__PURE__ */ jsx(
11561
- "button",
11562
- {
11563
- type: "button",
11564
- className: "kyro-toast-close group-hover:opacity-100 opacity-40 transition-opacity",
11565
- onClick: onClose,
11566
- children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
11567
- }
11568
- )
11569
- ]
11570
- }
11571
- );
11572
- }
11573
- function ToastProvider({ children }) {
11574
- return /* @__PURE__ */ jsx(Fragment, { children });
11575
- }
11576
- function useToast() {
11577
- const addToast = useToastStore((state) => state.addToast);
11578
- const removeToast = useToastStore((state) => state.removeToast);
11579
- const toasts = useToastStore((state) => state.toasts);
11580
- return { toasts, addToast, removeToast };
11581
- }
11582
11042
  function DetailView({
11583
11043
  config,
11584
11044
  collection,
@@ -11590,7 +11050,6 @@ function DetailView({
11590
11050
  onError,
11591
11051
  mode = "collection"
11592
11052
  }) {
11593
- const { addToast } = useToast();
11594
11053
  const { confirm, alert } = useUIStore();
11595
11054
  const [data, setData] = useState({});
11596
11055
  const [originalData, setOriginalData] = useState({});
@@ -11631,7 +11090,7 @@ function DetailView({
11631
11090
  const docData = result.data || {};
11632
11091
  setData(docData);
11633
11092
  setOriginalData(docData);
11634
- setStatus(docData?.publishStatus || result.status || "draft");
11093
+ setStatus(docData?.status || result.status || "draft");
11635
11094
  setCreatedAt(result.createdAt || docData.createdAt || null);
11636
11095
  setUpdatedAt(result.updatedAt || docData.updatedAt || null);
11637
11096
  setPublishedAt(result.publishedAt || docData.publishedAt || null);
@@ -11661,21 +11120,23 @@ function DetailView({
11661
11120
  try {
11662
11121
  setSaveStatus("saving");
11663
11122
  const endpoint = mode === "global" ? `/api/globals/${slug}` : `/api/${slug}/${documentId}`;
11664
- const result = await apiPatch(endpoint, data, { autoToast: false });
11123
+ const isDraft = status === "draft" || data?.status === "draft";
11124
+ const result = await apiPatch(endpoint, data, { autoToast: false, headers: { "X-Draft": String(isDraft) } });
11665
11125
  const savedData = result && (result.data || result) || data;
11666
11126
  if (!isAutosave) {
11667
11127
  setOriginalData(savedData);
11668
11128
  onSave();
11669
11129
  }
11670
11130
  setData(savedData);
11671
- setStatus(savedData?.publishStatus || status);
11131
+ setStatus(savedData?.status || status);
11672
11132
  setSaveStatus("saved");
11673
11133
  setUpdatedAt((/* @__PURE__ */ new Date()).toISOString());
11674
11134
  setJustSaved(true);
11675
11135
  setTimeout(() => setJustSaved(false), 3e3);
11676
11136
  if (!isAutosave) {
11677
- const isDraft = status === "draft" || savedData?.publishStatus === "draft";
11678
- addToast?.(isDraft ? "warning" : "success", isDraft ? "Draft saved" : "Updated");
11137
+ const isDraft2 = status === "draft" || savedData?.status === "draft";
11138
+ if (isDraft2) toast.warning("Draft saved");
11139
+ else toast.success("Updated");
11679
11140
  }
11680
11141
  setTimeout(() => {
11681
11142
  setSaveStatus("idle");
@@ -11684,24 +11145,28 @@ function DetailView({
11684
11145
  setSaveStatus("error");
11685
11146
  if (!isAutosave) {
11686
11147
  onError("Failed to save changes");
11687
- addToast?.("error", "Failed to save changes");
11148
+ toast.error("Failed to save changes");
11688
11149
  }
11689
11150
  } finally {
11690
11151
  setSaving(false);
11691
11152
  }
11692
11153
  },
11693
- [data, mode, slug, documentId, onSave, onError]
11154
+ [data, mode, slug, documentId, status, onSave, onError]
11694
11155
  );
11695
11156
  const handlePublish = async () => {
11696
11157
  try {
11697
11158
  setSaving(true);
11698
- await apiPost(`/api/${slug}/${documentId}/publish`, void 0, { autoToast: false });
11159
+ await apiPatch(`/api/${slug}/${documentId}`, data, {
11160
+ autoToast: false,
11161
+ headers: { "X-Draft": "false" }
11162
+ });
11699
11163
  setStatus("published");
11700
11164
  setPublishedAt((/* @__PURE__ */ new Date()).toISOString());
11701
- addToast?.("success", "Published successfully");
11165
+ toast.success("Published successfully");
11166
+ onSave();
11702
11167
  } catch {
11703
11168
  onError("Failed to publish");
11704
- addToast?.("error", "Failed to publish");
11169
+ toast.error("Failed to publish");
11705
11170
  } finally {
11706
11171
  setSaving(false);
11707
11172
  }
@@ -11709,12 +11174,16 @@ function DetailView({
11709
11174
  const handleUnpublish = async () => {
11710
11175
  try {
11711
11176
  setSaving(true);
11712
- await apiPost(`/api/${slug}/${documentId}/unpublish`, void 0, { autoToast: false });
11177
+ await apiPatch(`/api/${slug}/${documentId}`, { status: "draft" }, {
11178
+ autoToast: false,
11179
+ headers: { "X-Draft": "false" }
11180
+ });
11713
11181
  setStatus("draft");
11714
- addToast?.("warning", "Document unpublished");
11182
+ toast.warning("Document unpublished");
11183
+ onSave();
11715
11184
  } catch {
11716
11185
  onError("Failed to unpublish");
11717
- addToast?.("error", "Failed to unpublish");
11186
+ toast.error("Failed to unpublish");
11718
11187
  } finally {
11719
11188
  setSaving(false);
11720
11189
  }
@@ -11723,11 +11192,11 @@ function DetailView({
11723
11192
  try {
11724
11193
  setSaving(true);
11725
11194
  await apiPost(`/api/${slug}/${documentId}/duplicate`, void 0, { autoToast: false });
11726
- addToast?.("success", "Document duplicated");
11195
+ toast.success("Document duplicated");
11727
11196
  } catch (err) {
11728
11197
  const message = err instanceof Error ? err.message : "Failed to duplicate document";
11729
11198
  onError(message);
11730
- addToast?.("error", message);
11199
+ toast.error(message);
11731
11200
  } finally {
11732
11201
  setSaving(false);
11733
11202
  }
@@ -11742,10 +11211,10 @@ function DetailView({
11742
11211
  setDeleting(true);
11743
11212
  await apiDelete(`/api/${slug}/${documentId}`, { autoToast: false });
11744
11213
  onDelete?.();
11745
- addToast?.("error", "Document deleted");
11214
+ toast.error("Document deleted");
11746
11215
  } catch (err) {
11747
11216
  const message = err instanceof Error ? err.message : "Failed to delete document";
11748
- addToast?.("error", message);
11217
+ toast.error(message);
11749
11218
  } finally {
11750
11219
  setDeleting(false);
11751
11220
  }
@@ -11828,8 +11297,8 @@ function DetailView({
11828
11297
  layout: isSingleLayout ? "single" : "split",
11829
11298
  globalSlug: mode === "global" ? slug : void 0,
11830
11299
  collectionSlug: mode === "collection" ? slug : void 0,
11831
- onActionSuccess: (message) => addToast?.("success", message),
11832
- onActionError: (message) => addToast?.("error", message),
11300
+ onActionSuccess: (message) => toast.success(message),
11301
+ onActionError: (message) => toast.error(message),
11833
11302
  documentStatus: status,
11834
11303
  justSaved
11835
11304
  }
@@ -11844,44 +11313,14 @@ function DetailView({
11844
11313
  children: "Delete"
11845
11314
  }
11846
11315
  ),
11847
- status === "published" && !hasChanges && !saving ? /* @__PURE__ */ 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: [
11848
- /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" }) }),
11849
- "Published"
11850
- ] }) : /* @__PURE__ */ jsxs(
11851
- "button",
11316
+ /* @__PURE__ */ jsx(
11317
+ SplitButton,
11852
11318
  {
11853
- type: "button",
11854
- onClick: () => handleSave(false),
11855
- disabled: saving,
11856
- className: "kyro-btn kyro-btn-lg kyro-btn-primary shadow-xl flex items-center gap-2",
11857
- children: [
11858
- saving ? /* @__PURE__ */ jsx(
11859
- "svg",
11860
- {
11861
- className: "w-4 h-4 animate-spin",
11862
- viewBox: "0 0 24 24",
11863
- fill: "none",
11864
- stroke: "currentColor",
11865
- strokeWidth: "2",
11866
- children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
11867
- }
11868
- ) : /* @__PURE__ */ jsxs(
11869
- "svg",
11870
- {
11871
- className: "w-4 h-4",
11872
- viewBox: "0 0 24 24",
11873
- fill: "none",
11874
- stroke: "currentColor",
11875
- strokeWidth: "2",
11876
- children: [
11877
- /* @__PURE__ */ jsx("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
11878
- /* @__PURE__ */ jsx("polyline", { points: "17 21 17 13 7 13 7 21" }),
11879
- /* @__PURE__ */ jsx("polyline", { points: "7 3 7 8 15 8" })
11880
- ]
11881
- }
11882
- ),
11883
- saving ? "Saving..." : mode === "global" ? "Save Configuration" : "Save Document"
11884
- ]
11319
+ status,
11320
+ saveStatus: saving ? "saving" : "idle",
11321
+ hasChanges,
11322
+ onPublish: () => handleSave(false),
11323
+ disabled: saving
11885
11324
  }
11886
11325
  )
11887
11326
  ] })
@@ -12011,6 +11450,62 @@ function CreateView({
12011
11450
  ) }) }) }) })
12012
11451
  ] });
12013
11452
  }
11453
+ function Toast({ type, message, onClose }) {
11454
+ const [isPaused, setIsPaused] = React56.useState(false);
11455
+ const timerRef = React56.useRef(null);
11456
+ const startTimer = () => {
11457
+ if (timerRef.current) clearTimeout(timerRef.current);
11458
+ timerRef.current = setTimeout(onClose, 5e3);
11459
+ };
11460
+ const clearTimer = () => {
11461
+ if (timerRef.current) clearTimeout(timerRef.current);
11462
+ };
11463
+ React56.useEffect(() => {
11464
+ if (!isPaused) {
11465
+ startTimer();
11466
+ } else {
11467
+ clearTimer();
11468
+ }
11469
+ return clearTimer;
11470
+ }, [isPaused, onClose]);
11471
+ const Icon = {
11472
+ success: CircleCheck,
11473
+ error: ShieldAlert,
11474
+ warning: TriangleAlert,
11475
+ info: Info
11476
+ }[type];
11477
+ return /* @__PURE__ */ jsxs(
11478
+ "div",
11479
+ {
11480
+ className: `kyro-toast kyro-toast-${type} group animate-in fade-in slide-in-from-right-4 duration-300`,
11481
+ onMouseEnter: () => setIsPaused(true),
11482
+ onMouseLeave: () => setIsPaused(false),
11483
+ children: [
11484
+ /* @__PURE__ */ jsx("div", { className: "kyro-toast-accent" }),
11485
+ /* @__PURE__ */ jsx("div", { className: "kyro-toast-icon-container", children: /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }) }),
11486
+ /* @__PURE__ */ jsx("div", { className: "kyro-toast-content", children: /* @__PURE__ */ jsx("p", { className: "kyro-toast-message", children: message }) }),
11487
+ /* @__PURE__ */ jsx(
11488
+ "button",
11489
+ {
11490
+ type: "button",
11491
+ className: "kyro-toast-close group-hover:opacity-100 opacity-40 transition-opacity",
11492
+ onClick: onClose,
11493
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
11494
+ }
11495
+ )
11496
+ ]
11497
+ }
11498
+ );
11499
+ }
11500
+ function ToastProvider({ children }) {
11501
+ return /* @__PURE__ */ jsx(Fragment, { children });
11502
+ }
11503
+ function useToast() {
11504
+ const addToast = useToastStore((state) => state.addToast);
11505
+ const removeToast = useToastStore((state) => state.removeToast);
11506
+ const toasts = useToastStore((state) => state.toasts);
11507
+ return { toasts, addToast, removeToast };
11508
+ }
12014
11509
  function LoginPage({ onAuth, theme = "light" }) {
12015
11510
  const [mode, setMode] = useState("login");
12016
11511
  const [email, setEmail] = useState("");
@@ -14324,7 +13819,7 @@ function Admin({ config, theme = "light", onThemeChange }) {
14324
13819
  );
14325
13820
  }
14326
13821
  };
14327
- return /* @__PURE__ */ jsx(ThemeProvider, { ...{ mode: theme, onChange: onThemeChange }, children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsxs("div", { className: "kyro-admin min-h-screen bg-[var(--kyro-bg)] text-[var(--kyro-text-primary)]", children: [
13822
+ return /* @__PURE__ */ jsx(ThemeProvider, { ...{ mode: theme, onChange: onThemeChange }, children: /* @__PURE__ */ jsxs("div", { className: "kyro-admin min-h-screen bg-[var(--kyro-bg)] text-[var(--kyro-text-primary)]", children: [
14328
13823
  /* @__PURE__ */ jsx("div", { className: "flex h-screen overflow-hidden", children: /* @__PURE__ */ jsx("main", { className: "flex-1 flex flex-col min-w-0 overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
14329
13824
  /* @__PURE__ */ jsx(
14330
13825
  CommandPalette,
@@ -14348,7 +13843,7 @@ function Admin({ config, theme = "light", onThemeChange }) {
14348
13843
  },
14349
13844
  t.id
14350
13845
  )) })
14351
- ] }) }) });
13846
+ ] }) });
14352
13847
  }
14353
13848
  function BulkActionsBar({
14354
13849
  selectedCount,
@@ -14579,32 +14074,6 @@ function Header({ title, onMenuClick, actions }) {
14579
14074
  ] })
14580
14075
  ] });
14581
14076
  }
14582
- function Button({
14583
- variant = "secondary",
14584
- size = "md",
14585
- loading = false,
14586
- children,
14587
- className = "",
14588
- disabled,
14589
- ...props
14590
- }) {
14591
- const baseClass = "kyro-btn";
14592
- const variantClass = `kyro-btn-${variant}`;
14593
- const sizeClass = `kyro-btn-${size}`;
14594
- return /* @__PURE__ */ jsxs(
14595
- "button",
14596
- {
14597
- type: "button",
14598
- className: `${baseClass} ${variantClass} ${sizeClass} ${className}`,
14599
- disabled: disabled || loading,
14600
- ...props,
14601
- children: [
14602
- loading ? /* @__PURE__ */ jsx("span", { className: "kyro-btn-loading", children: /* @__PURE__ */ jsx("svg", { className: "kyro-btn-spinner", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", strokeDasharray: "60 30" }) }) }) : null,
14603
- /* @__PURE__ */ jsx("span", { className: loading ? "kyro-btn-text-hidden" : "", children })
14604
- ]
14605
- }
14606
- );
14607
- }
14608
14077
 
14609
14078
  // src/hooks/lifecycle.ts
14610
14079
  var registry = {