@pixerate/editor-react 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +4 -0
  2. package/dist/canvas/index.cjs +219 -118
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +59 -3
  5. package/dist/canvas/index.d.ts +59 -3
  6. package/dist/canvas/index.js +103 -0
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-7SPRQZ6H.js +216 -0
  9. package/dist/chunk-7SPRQZ6H.js.map +1 -0
  10. package/dist/{chunk-QVQYYHJ5.js → chunk-FYIPXHAE.js} +21 -13
  11. package/dist/chunk-FYIPXHAE.js.map +1 -0
  12. package/dist/{chunk-LPFZMZ3L.js → chunk-XTFR3IMK.js} +676 -322
  13. package/dist/chunk-XTFR3IMK.js.map +1 -0
  14. package/dist/chunk-XWFU2CSG.js +167 -0
  15. package/dist/chunk-XWFU2CSG.js.map +1 -0
  16. package/dist/generation/index.cjs +235 -0
  17. package/dist/generation/index.cjs.map +1 -0
  18. package/dist/generation/index.d.cts +60 -0
  19. package/dist/generation/index.d.ts +60 -0
  20. package/dist/generation/index.js +13 -0
  21. package/dist/generation/index.js.map +1 -0
  22. package/dist/image-editor/index.cjs +684 -336
  23. package/dist/image-editor/index.cjs.map +1 -1
  24. package/dist/image-editor/index.d.cts +60 -2
  25. package/dist/image-editor/index.d.ts +60 -2
  26. package/dist/image-editor/index.js +13 -3
  27. package/dist/index.cjs +1881 -648
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +143 -4
  30. package/dist/index.d.ts +143 -4
  31. package/dist/index.js +626 -73
  32. package/dist/index.js.map +1 -1
  33. package/dist/persistence/index.cjs +191 -0
  34. package/dist/persistence/index.cjs.map +1 -0
  35. package/dist/persistence/index.d.cts +77 -0
  36. package/dist/persistence/index.d.ts +77 -0
  37. package/dist/persistence/index.js +9 -0
  38. package/dist/persistence/index.js.map +1 -0
  39. package/dist/spreadsheet/index.cjs +17 -10
  40. package/dist/spreadsheet/index.cjs.map +1 -1
  41. package/dist/spreadsheet/index.d.cts +2 -0
  42. package/dist/spreadsheet/index.d.ts +2 -0
  43. package/dist/spreadsheet/index.js +1 -1
  44. package/package.json +18 -2
  45. package/dist/chunk-LPFZMZ3L.js.map +0 -1
  46. package/dist/chunk-QVQYYHJ5.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -30,40 +30,56 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ AdjustmentLayersPanel: () => AdjustmentLayersPanel,
33
34
  BubbleMenu: () => BubbleMenu,
34
35
  CommandPalette: () => CommandPalette,
35
36
  CommentButton: () => CommentButton,
36
37
  CommentsPanel: () => CommentsPanel,
37
- DirtyTracker: () => import_editor12.DirtyTracker,
38
+ ControlSlider: () => ControlSlider,
39
+ CurvesEditor: () => CurvesEditor,
40
+ DirtyTracker: () => import_editor16.DirtyTracker,
38
41
  EditorContent: () => EditorContent,
39
42
  EditorSlotsProvider: () => EditorSlotsProvider,
43
+ FindReplaceBar: () => FindReplaceBar,
40
44
  FormulaHints: () => FormulaHints,
41
- HistoryManager: () => import_editor12.HistoryManager,
45
+ GenerationSettings: () => GenerationSettings,
46
+ Histogram: () => Histogram,
47
+ HistoryManager: () => import_editor16.HistoryManager,
42
48
  ImageEditor: () => ImageEditor,
49
+ JobProgress: () => JobProgress,
50
+ MergePreview: () => MergePreview,
43
51
  OutlinePanel: () => OutlinePanel,
52
+ RecoveryPrompt: () => RecoveryPrompt,
44
53
  ReviewToolbar: () => ReviewToolbar,
45
54
  SpreadsheetContextMenu: () => SpreadsheetContextMenu,
46
55
  SpreadsheetEditor: () => SpreadsheetEditor,
47
56
  SpreadsheetGroupRow: () => SpreadsheetGroupRow,
48
57
  TemplateRenderer: () => TemplateRenderer,
49
58
  VariableForm: () => VariableForm,
50
- bindDirtyToHistory: () => import_editor12.bindDirtyToHistory,
51
- createDirtyTracker: () => import_editor12.createDirtyTracker,
52
- createHistoryManager: () => import_editor12.createHistoryManager,
53
- defaultIsEqual: () => import_editor12.defaultIsEqual,
59
+ bindDirtyToHistory: () => import_editor16.bindDirtyToHistory,
60
+ createDirtyTracker: () => import_editor16.createDirtyTracker,
61
+ createHistoryManager: () => import_editor16.createHistoryManager,
62
+ defaultIsEqual: () => import_editor16.defaultIsEqual,
54
63
  useAgentToolProvider: () => useAgentToolProvider,
64
+ useAutosave: () => useAutosave,
55
65
  useCommandPalette: () => useCommandPalette,
56
66
  useCommandShortcuts: () => useCommandShortcuts,
57
67
  useComments: () => useComments,
58
68
  useDismissGuard: () => useDismissGuard,
69
+ useDocumentCompare: () => useDocumentCompare,
59
70
  useEditor: () => useEditor2,
60
71
  useEditorSlots: () => useEditorSlots,
61
72
  useFileImport: () => useFileImport,
62
73
  useFillHandle: () => useFillHandle,
74
+ useFindReplace: () => useFindReplace,
75
+ useGenerationPlan: () => useGenerationPlan,
63
76
  useHistory: () => useHistory,
64
77
  useHistoryDirty: () => useHistoryDirty,
65
78
  useHistoryShortcuts: () => useHistoryShortcuts,
66
79
  useImageEditor: () => useImageEditor,
80
+ useImageHistogram: () => useImageHistogram,
81
+ useJobProgress: () => useJobProgress,
82
+ useMergePreview: () => useMergePreview,
67
83
  useNavigationGuard: () => useNavigationGuard,
68
84
  useOutline: () => useOutline,
69
85
  usePromptEditor: () => usePromptEditor,
@@ -71,7 +87,7 @@ __export(src_exports, {
71
87
  useSpreadsheetClipboard: () => useSpreadsheetClipboard,
72
88
  useSpreadsheetEditor: () => useSpreadsheetEditor,
73
89
  useTrackChanges: () => useTrackChanges,
74
- withoutDirty: () => import_editor12.withoutDirty
90
+ withoutDirty: () => import_editor16.withoutDirty
75
91
  });
76
92
  module.exports = __toCommonJS(src_exports);
77
93
 
@@ -695,7 +711,6 @@ var BubbleMenu = ({
695
711
  var import_react13 = require("react");
696
712
  var import_editor6 = require("@pixerate/editor");
697
713
  var import_jsx_runtime2 = require("react/jsx-runtime");
698
- var CONDITIONAL_PREFIX = { if: "?", unless: "!", end: "/" };
699
714
  var TemplateRenderer = ({
700
715
  content,
701
716
  templates = [],
@@ -778,8 +793,8 @@ var TemplateRenderer = ({
778
793
  {
779
794
  "data-conditional": token.conditional,
780
795
  className: "inline-flex items-center px-1 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-amber-400/40 bg-amber-400/15 text-amber-700 dark:text-amber-300",
781
- title: token.conditional === "end" ? `End of section: ${token.value}` : token.conditional === "if" ? `Shown when "${token.value}" has a value` : `Shown when "${token.value}" is empty`,
782
- children: `{${CONDITIONAL_PREFIX[token.conditional]}${token.value}}`
796
+ title: (0, import_editor6.describeConditionalTag)(token),
797
+ children: (0, import_editor6.formatConditionalTag)(token)
783
798
  },
784
799
  index
785
800
  );
@@ -884,17 +899,305 @@ var VariableForm = ({
884
899
  }) });
885
900
  };
886
901
 
887
- // src/hooks/useOutline.ts
902
+ // src/components/MergePreview.tsx
903
+ var import_react16 = require("react");
904
+
905
+ // src/hooks/useMergePreview.ts
888
906
  var import_react15 = require("react");
889
907
  var import_editor8 = require("@pixerate/editor");
908
+ function useMergePreview(options) {
909
+ const { template, templates, bindings, parseOptions } = options;
910
+ const [sourceText, setSourceTextState] = (0, import_react15.useState)(options.initialSource ?? "");
911
+ const [overrides, setOverrides] = (0, import_react15.useState)({});
912
+ const [index, setIndexState] = (0, import_react15.useState)(0);
913
+ const [result, setResult] = (0, import_react15.useState)(null);
914
+ const source = (0, import_react15.useMemo)(
915
+ () => (0, import_editor8.parseDataSource)(sourceText, parseOptions),
916
+ [sourceText, parseOptions]
917
+ );
918
+ const variables = (0, import_react15.useMemo)(
919
+ () => (0, import_editor8.extractVariables)(template, { templates, bindings }).map((v) => v.name),
920
+ [template, templates, bindings]
921
+ );
922
+ const mapping = (0, import_react15.useMemo)(() => {
923
+ const merged = {
924
+ ...(0, import_editor8.matchFields)(variables, source.fields).mapping
925
+ };
926
+ for (const [variable, field] of Object.entries(overrides)) {
927
+ if (variables.includes(variable) && (field === null || source.fields.includes(field))) {
928
+ merged[variable] = field;
929
+ }
930
+ }
931
+ return merged;
932
+ }, [variables, source.fields, overrides]);
933
+ const count = source.records.length;
934
+ const current = count === 0 ? 0 : Math.min(index, count - 1);
935
+ const record = source.records[current];
936
+ const values = (0, import_react15.useMemo)(
937
+ () => record ? (0, import_editor8.recordToValues)(record, mapping) : {},
938
+ [record, mapping]
939
+ );
940
+ const setSourceText = (0, import_react15.useCallback)((text) => {
941
+ setSourceTextState(text);
942
+ setIndexState(0);
943
+ setResult(null);
944
+ }, []);
945
+ const loadFile = (0, import_react15.useCallback)(
946
+ async (file) => setSourceText(await file.text()),
947
+ [setSourceText]
948
+ );
949
+ const setIndex = (0, import_react15.useCallback)(
950
+ (next) => setIndexState(Math.max(0, Math.min(next, count - 1))),
951
+ [count]
952
+ );
953
+ const setFieldMapping = (0, import_react15.useCallback)((variable, field) => {
954
+ setOverrides((previous) => ({ ...previous, [variable]: field }));
955
+ setResult(null);
956
+ }, []);
957
+ const resetMapping = (0, import_react15.useCallback)(() => setOverrides({}), []);
958
+ const merge = (0, import_react15.useCallback)(() => {
959
+ const merged = (0, import_editor8.mergeRecords)(template, source, {
960
+ templates,
961
+ bindings,
962
+ mapping,
963
+ skipIf: options.skipIf,
964
+ separator: options.separator,
965
+ maxOutputs: options.maxOutputs
966
+ });
967
+ setResult(merged);
968
+ return merged;
969
+ }, [template, source, templates, bindings, mapping, options.skipIf, options.separator, options.maxOutputs]);
970
+ const serialize = (0, import_react15.useCallback)(
971
+ (format) => result ? (0, import_editor8.serializeMergeOutputs)(result.outputs, format, options.separator) : "",
972
+ [result, options.separator]
973
+ );
974
+ return {
975
+ sourceText,
976
+ setSourceText,
977
+ loadFile,
978
+ source,
979
+ variables,
980
+ mapping,
981
+ setFieldMapping,
982
+ resetMapping,
983
+ index: current,
984
+ count,
985
+ setIndex,
986
+ next: () => setIndex(current + 1),
987
+ prev: () => setIndex(current - 1),
988
+ record,
989
+ values,
990
+ merge,
991
+ result,
992
+ serialize
993
+ };
994
+ }
995
+
996
+ // src/components/MergePreview.tsx
997
+ var import_jsx_runtime4 = require("react/jsx-runtime");
998
+ function downloadText(text, fileName, type) {
999
+ if (typeof document === "undefined" || typeof URL.createObjectURL !== "function") {
1000
+ return;
1001
+ }
1002
+ const url = URL.createObjectURL(new Blob([text], { type }));
1003
+ const link = document.createElement("a");
1004
+ link.href = url;
1005
+ link.download = fileName;
1006
+ link.click();
1007
+ setTimeout(() => URL.revokeObjectURL(url), 0);
1008
+ }
1009
+ var MergePreview = ({
1010
+ content,
1011
+ templates,
1012
+ bindings,
1013
+ templateColorMap,
1014
+ defaultSource,
1015
+ parseOptions,
1016
+ skipIf,
1017
+ separator,
1018
+ maxOutputs,
1019
+ onMerge,
1020
+ downloadName = "merged",
1021
+ className = "flex flex-col gap-3 text-sm"
1022
+ }) => {
1023
+ const id = (0, import_react16.useId)();
1024
+ const merge = useMergePreview({
1025
+ template: content,
1026
+ templates,
1027
+ bindings,
1028
+ initialSource: defaultSource,
1029
+ parseOptions,
1030
+ skipIf,
1031
+ separator,
1032
+ maxOutputs
1033
+ });
1034
+ const [copied, setCopied] = (0, import_react16.useState)(false);
1035
+ const { source, result } = merge;
1036
+ const copy = async () => {
1037
+ try {
1038
+ await navigator.clipboard.writeText(merge.serialize("text"));
1039
+ setCopied(true);
1040
+ } catch {
1041
+ setCopied(false);
1042
+ }
1043
+ };
1044
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, "data-merge-preview": "", children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { htmlFor: `${id}-source`, className: "flex flex-col gap-1", children: [
1046
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "font-medium", children: "Data source (CSV, TSV or JSON)" }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1048
+ "textarea",
1049
+ {
1050
+ id: `${id}-source`,
1051
+ "data-merge-source": "",
1052
+ value: merge.sourceText,
1053
+ rows: 5,
1054
+ spellCheck: false,
1055
+ onChange: (event) => merge.setSourceText(event.target.value),
1056
+ className: "px-2 py-1 font-mono text-xs rounded-md border border-violet-400/40 bg-transparent"
1057
+ }
1058
+ )
1059
+ ] }),
1060
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
1061
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1062
+ "input",
1063
+ {
1064
+ type: "file",
1065
+ accept: ".csv,.tsv,.txt,.json,text/csv,application/json",
1066
+ "aria-label": "Load data source file",
1067
+ "data-merge-file": "",
1068
+ onChange: (event) => {
1069
+ const file = event.target.files?.[0];
1070
+ if (file) void merge.loadFile(file);
1071
+ }
1072
+ }
1073
+ ),
1074
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-merge-summary": "", className: "opacity-70", children: `${source.records.length} record${source.records.length === 1 ? "" : "s"} \xB7 ${source.fields.length} field${source.fields.length === 1 ? "" : "s"}` })
1075
+ ] }),
1076
+ source.warnings.length > 0 && merge.sourceText.trim() !== "" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { "data-merge-warnings": "", className: "text-amber-600 text-xs", children: source.warnings.map((warning, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: warning.message }, i)) }),
1077
+ merge.variables.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("fieldset", { className: "flex flex-col gap-1", children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("legend", { className: "font-medium", children: "Field mapping" }),
1079
+ merge.variables.map((variable) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1080
+ "label",
1081
+ {
1082
+ className: "flex items-center gap-2",
1083
+ "data-merge-mapping": variable,
1084
+ children: [
1085
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-24 font-mono", children: `{${variable}}` }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1087
+ "select",
1088
+ {
1089
+ value: merge.mapping[variable] ?? "",
1090
+ "aria-label": `Field for ${variable}`,
1091
+ onChange: (event) => merge.setFieldMapping(variable, event.target.value || null),
1092
+ className: "px-1 py-0.5 rounded-md border border-violet-400/40 bg-transparent",
1093
+ children: [
1094
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "(none)" }),
1095
+ source.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: field, children: field }, field))
1096
+ ]
1097
+ }
1098
+ )
1099
+ ]
1100
+ },
1101
+ variable
1102
+ ))
1103
+ ] }),
1104
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
1105
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1106
+ "button",
1107
+ {
1108
+ type: "button",
1109
+ "data-merge-prev": "",
1110
+ onClick: merge.prev,
1111
+ disabled: merge.index <= 0,
1112
+ children: "Prev"
1113
+ }
1114
+ ),
1115
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-merge-position": "", children: merge.count === 0 ? "No records" : `Record ${merge.index + 1} of ${merge.count}` }),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1117
+ "button",
1118
+ {
1119
+ type: "button",
1120
+ "data-merge-next": "",
1121
+ onClick: merge.next,
1122
+ disabled: merge.index >= merge.count - 1,
1123
+ children: "Next"
1124
+ }
1125
+ )
1126
+ ] }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-merge-output": "", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1128
+ TemplateRenderer,
1129
+ {
1130
+ content,
1131
+ templates,
1132
+ bindings,
1133
+ templateColorMap,
1134
+ resolve: !!templates && templates.length > 0,
1135
+ values: merge.values
1136
+ }
1137
+ ) }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
1139
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1140
+ "button",
1141
+ {
1142
+ type: "button",
1143
+ "data-merge-all": "",
1144
+ disabled: merge.count === 0,
1145
+ onClick: () => {
1146
+ const merged = merge.merge();
1147
+ setCopied(false);
1148
+ onMerge?.(merged);
1149
+ },
1150
+ children: "Merge all"
1151
+ }
1152
+ ),
1153
+ result && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1154
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-merge-result": "", children: `${result.outputs.length} merged${result.skipped.length ? `, ${result.skipped.length} skipped` : ""}${result.truncated ? " (stopped early)" : ""}` }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "data-merge-copy": "", onClick: copy, children: copied ? "Copied" : "Copy" }),
1156
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1157
+ "button",
1158
+ {
1159
+ type: "button",
1160
+ "data-merge-download": "text",
1161
+ onClick: () => downloadText(merge.serialize("text"), `${downloadName}.txt`, "text/plain"),
1162
+ children: "Download .txt"
1163
+ }
1164
+ ),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1166
+ "button",
1167
+ {
1168
+ type: "button",
1169
+ "data-merge-download": "json",
1170
+ onClick: () => downloadText(merge.serialize("json"), `${downloadName}.json`, "application/json"),
1171
+ children: "Download .json"
1172
+ }
1173
+ )
1174
+ ] })
1175
+ ] }),
1176
+ result && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1177
+ "textarea",
1178
+ {
1179
+ readOnly: true,
1180
+ "aria-label": "Merged output",
1181
+ "data-merge-text": "",
1182
+ rows: 6,
1183
+ value: merge.serialize("text"),
1184
+ className: "px-2 py-1 font-mono text-xs rounded-md border border-violet-400/40 bg-transparent"
1185
+ }
1186
+ )
1187
+ ] });
1188
+ };
1189
+
1190
+ // src/hooks/useOutline.ts
1191
+ var import_react17 = require("react");
1192
+ var import_editor9 = require("@pixerate/editor");
890
1193
  function useOutline(editor) {
891
- const store = (0, import_react15.useMemo)(() => (0, import_editor8.createOutlineStore)(editor), [editor]);
892
- return (0, import_react15.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
1194
+ const store = (0, import_react17.useMemo)(() => (0, import_editor9.createOutlineStore)(editor), [editor]);
1195
+ return (0, import_react17.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
893
1196
  }
894
1197
 
895
1198
  // src/components/OutlinePanel.tsx
896
- var import_editor9 = require("@pixerate/editor");
897
- var import_jsx_runtime4 = require("react/jsx-runtime");
1199
+ var import_editor10 = require("@pixerate/editor");
1200
+ var import_jsx_runtime5 = require("react/jsx-runtime");
898
1201
  var OutlinePanel = ({
899
1202
  editor,
900
1203
  title = "Outline",
@@ -908,11 +1211,11 @@ var OutlinePanel = ({
908
1211
  }) => {
909
1212
  const { headings, activeId } = useOutline(editor);
910
1213
  const visible = headings.filter((h) => h.level <= maxLevel);
911
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("nav", { className, "aria-label": "Document outline", "data-outline-panel": "", children: [
912
- title != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pixerate-outline-title", children: title }),
913
- visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pixerate-outline-empty", children: emptyState }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { role: "list", style: { listStyle: "none", margin: 0, padding: 0 }, children: visible.map((heading) => {
1214
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("nav", { className, "aria-label": "Document outline", "data-outline-panel": "", children: [
1215
+ title != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pixerate-outline-title", children: title }),
1216
+ visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pixerate-outline-empty", children: emptyState }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { role: "list", style: { listStyle: "none", margin: 0, padding: 0 }, children: visible.map((heading) => {
914
1217
  const active = heading.id === activeId;
915
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { "data-level": heading.level, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1218
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { "data-level": heading.level, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
916
1219
  "button",
917
1220
  {
918
1221
  type: "button",
@@ -922,7 +1225,7 @@ var OutlinePanel = ({
922
1225
  "data-heading-id": heading.id,
923
1226
  onClick: () => {
924
1227
  if (!editor) return;
925
- if ((0, import_editor9.navigateToHeading)(editor, heading, { focus: focusOnNavigate })) {
1228
+ if ((0, import_editor10.navigateToHeading)(editor, heading, { focus: focusOnNavigate })) {
926
1229
  onNavigate?.(heading);
927
1230
  }
928
1231
  },
@@ -934,16 +1237,16 @@ var OutlinePanel = ({
934
1237
  };
935
1238
 
936
1239
  // src/index.ts
937
- var import_editor12 = require("@pixerate/editor");
1240
+ var import_editor16 = require("@pixerate/editor");
938
1241
 
939
1242
  // src/spreadsheet/useSpreadsheetEditor.ts
940
- var import_react16 = require("react");
1243
+ var import_react18 = require("react");
941
1244
  var import_spreadsheet = require("@pixerate/editor/spreadsheet");
942
1245
  function useSpreadsheetEditor(options = {}) {
943
- const optionsRef = (0, import_react16.useRef)(options);
1246
+ const optionsRef = (0, import_react18.useRef)(options);
944
1247
  optionsRef.current = options;
945
- const settersRef = (0, import_react16.useRef)({});
946
- const [controller] = (0, import_react16.useState)(
1248
+ const settersRef = (0, import_react18.useRef)({});
1249
+ const [controller] = (0, import_react18.useState)(
947
1250
  () => new import_spreadsheet.SpreadsheetController({
948
1251
  ...options,
949
1252
  onDocumentChange: (updated) => {
@@ -960,97 +1263,97 @@ function useSpreadsheetEditor(options = {}) {
960
1263
  }
961
1264
  })
962
1265
  );
963
- const [doc, setDoc] = (0, import_react16.useState)(() => ({ ...controller.document }));
964
- const [activeCell, setActiveCell] = (0, import_react16.useState)(controller.activeCell);
965
- const [selectedRange, setSelectedRange] = (0, import_react16.useState)(controller.selectedRange);
966
- const [editingCell, setEditingCell] = (0, import_react16.useState)(null);
967
- const [draftValue, setDraftState] = (0, import_react16.useState)("");
1266
+ const [doc, setDoc] = (0, import_react18.useState)(() => ({ ...controller.document }));
1267
+ const [activeCell, setActiveCell] = (0, import_react18.useState)(controller.activeCell);
1268
+ const [selectedRange, setSelectedRange] = (0, import_react18.useState)(controller.selectedRange);
1269
+ const [editingCell, setEditingCell] = (0, import_react18.useState)(null);
1270
+ const [draftValue, setDraftState] = (0, import_react18.useState)("");
968
1271
  settersRef.current = { setDoc, setActiveCell, setSelectedRange };
969
- const setDraftValue = (0, import_react16.useCallback)((value) => {
1272
+ const setDraftValue = (0, import_react18.useCallback)((value) => {
970
1273
  controller.draftValue = value;
971
1274
  setDraftState(value);
972
1275
  }, [controller]);
973
- const setCellValue = (0, import_react16.useCallback)((rowId, colId, raw) => {
1276
+ const setCellValue = (0, import_react18.useCallback)((rowId, colId, raw) => {
974
1277
  controller.setCellValue(rowId, colId, raw);
975
1278
  setDoc({ ...controller.document });
976
1279
  }, [controller]);
977
- const selectCell = (0, import_react16.useCallback)((row, col, extendSelection = false) => {
1280
+ const selectCell = (0, import_react18.useCallback)((row, col, extendSelection = false) => {
978
1281
  controller.selectCell(row, col, extendSelection);
979
1282
  setActiveCell(controller.activeCell);
980
1283
  setSelectedRange(controller.selectedRange);
981
1284
  }, [controller]);
982
- const selectRange = (0, import_react16.useCallback)((range) => {
1285
+ const selectRange = (0, import_react18.useCallback)((range) => {
983
1286
  controller.selectRange(range);
984
1287
  setSelectedRange(controller.selectedRange);
985
1288
  }, [controller]);
986
- const startEditing = (0, import_react16.useCallback)((row, col, initialVal) => {
1289
+ const startEditing = (0, import_react18.useCallback)((row, col, initialVal) => {
987
1290
  controller.startEditing(row, col, initialVal);
988
1291
  setActiveCell(controller.activeCell);
989
1292
  setEditingCell(controller.editingCell);
990
1293
  setDraftValue(controller.draftValue);
991
1294
  }, [controller]);
992
- const commitEditing = (0, import_react16.useCallback)((val) => {
1295
+ const commitEditing = (0, import_react18.useCallback)((val) => {
993
1296
  controller.commitEditing(val);
994
1297
  setEditingCell(null);
995
1298
  setDraftValue("");
996
1299
  setDoc({ ...controller.document });
997
1300
  }, [controller]);
998
- const cancelEditing = (0, import_react16.useCallback)(() => {
1301
+ const cancelEditing = (0, import_react18.useCallback)(() => {
999
1302
  controller.cancelEditing();
1000
1303
  setEditingCell(null);
1001
1304
  setDraftValue("");
1002
1305
  }, [controller]);
1003
- const moveCursor = (0, import_react16.useCallback)((dir, extend = false) => {
1306
+ const moveCursor = (0, import_react18.useCallback)((dir, extend = false) => {
1004
1307
  controller.moveCursor(dir, extend);
1005
1308
  setActiveCell(controller.activeCell);
1006
1309
  setSelectedRange(controller.selectedRange);
1007
1310
  }, [controller]);
1008
- const insertColumn = (0, import_react16.useCallback)((index, config) => {
1311
+ const insertColumn = (0, import_react18.useCallback)((index, config) => {
1009
1312
  controller.insertColumn(index, config);
1010
1313
  setDoc({ ...controller.document });
1011
1314
  }, [controller]);
1012
- const deleteColumn = (0, import_react16.useCallback)((colId) => {
1315
+ const deleteColumn = (0, import_react18.useCallback)((colId) => {
1013
1316
  controller.deleteColumn(colId);
1014
1317
  setDoc({ ...controller.document });
1015
1318
  }, [controller]);
1016
- const insertRow = (0, import_react16.useCallback)((index, config) => {
1319
+ const insertRow = (0, import_react18.useCallback)((index, config) => {
1017
1320
  controller.insertRow(index, config);
1018
1321
  setDoc({ ...controller.document });
1019
1322
  }, [controller]);
1020
- const deleteRow = (0, import_react16.useCallback)((rowId) => {
1323
+ const deleteRow = (0, import_react18.useCallback)((rowId) => {
1021
1324
  controller.deleteRow(rowId);
1022
1325
  setDoc({ ...controller.document });
1023
1326
  }, [controller]);
1024
- const setColumnWidth = (0, import_react16.useCallback)((colId, width) => {
1327
+ const setColumnWidth = (0, import_react18.useCallback)((colId, width) => {
1025
1328
  controller.setColumnWidth(colId, width);
1026
1329
  setDoc({ ...controller.document });
1027
1330
  }, [controller]);
1028
- const autoFitColumnWidth = (0, import_react16.useCallback)((colId, customWidth) => {
1331
+ const autoFitColumnWidth = (0, import_react18.useCallback)((colId, customWidth) => {
1029
1332
  controller.autoFitColumnWidth(colId, customWidth);
1030
1333
  setDoc({ ...controller.document });
1031
1334
  }, [controller]);
1032
- const registerDataSource = (0, import_react16.useCallback)((ds) => {
1335
+ const registerDataSource = (0, import_react18.useCallback)((ds) => {
1033
1336
  controller.registerDataSource(ds);
1034
1337
  setDoc({ ...controller.document });
1035
1338
  }, [controller]);
1036
- const setPrimaryDataSource = (0, import_react16.useCallback)((id) => {
1339
+ const setPrimaryDataSource = (0, import_react18.useCallback)((id) => {
1037
1340
  controller.setPrimaryDataSource(id);
1038
1341
  setDoc({ ...controller.document });
1039
1342
  }, [controller]);
1040
- const recalculateAll = (0, import_react16.useCallback)(() => {
1343
+ const recalculateAll = (0, import_react18.useCallback)(() => {
1041
1344
  controller.recalculateAll();
1042
1345
  setDoc({ ...controller.document });
1043
1346
  }, [controller]);
1044
- const undo = (0, import_react16.useCallback)(() => {
1347
+ const undo = (0, import_react18.useCallback)(() => {
1045
1348
  controller.undo();
1046
1349
  setDoc({ ...controller.document });
1047
1350
  }, [controller]);
1048
- const redo = (0, import_react16.useCallback)(() => {
1351
+ const redo = (0, import_react18.useCallback)(() => {
1049
1352
  controller.redo();
1050
1353
  setDoc({ ...controller.document });
1051
1354
  }, [controller]);
1052
- const tableActions = (0, import_react16.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
1053
- const viewModel = (0, import_react16.useMemo)(() => controller.getViewModel(), [controller, doc]);
1355
+ const tableActions = (0, import_react18.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
1356
+ const viewModel = (0, import_react18.useMemo)(() => controller.getViewModel(), [controller, doc]);
1054
1357
  return {
1055
1358
  document: doc,
1056
1359
  activeCell,
@@ -1102,20 +1405,20 @@ function useSpreadsheetEditor(options = {}) {
1102
1405
  }
1103
1406
 
1104
1407
  // src/spreadsheet/SpreadsheetEditor.tsx
1105
- var import_react20 = require("react");
1408
+ var import_react22 = require("react");
1106
1409
  var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
1107
1410
 
1108
1411
  // src/spreadsheet/SpreadsheetContextMenu.tsx
1109
- var import_react17 = __toESM(require("react"), 1);
1412
+ var import_react19 = __toESM(require("react"), 1);
1110
1413
  var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
1111
- var import_jsx_runtime5 = require("react/jsx-runtime");
1414
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1112
1415
  var VIEWPORT_MARGIN = 4;
1113
1416
  var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1114
- const menuRef = (0, import_react17.useRef)(null);
1115
- const onCloseRef = (0, import_react17.useRef)(onClose);
1417
+ const menuRef = (0, import_react19.useRef)(null);
1418
+ const onCloseRef = (0, import_react19.useRef)(onClose);
1116
1419
  onCloseRef.current = onClose;
1117
- const [position, setPosition] = (0, import_react17.useState)({ x, y });
1118
- (0, import_react17.useLayoutEffect)(() => {
1420
+ const [position, setPosition] = (0, import_react19.useState)({ x, y });
1421
+ (0, import_react19.useLayoutEffect)(() => {
1119
1422
  const menu = menuRef.current;
1120
1423
  if (!menu || typeof window === "undefined") return;
1121
1424
  const { width, height } = menu.getBoundingClientRect();
@@ -1127,7 +1430,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1127
1430
  );
1128
1431
  setPosition((prev) => prev.x === next.x && prev.y === next.y ? prev : next);
1129
1432
  }, [x, y, items]);
1130
- (0, import_react17.useEffect)(() => {
1433
+ (0, import_react19.useEffect)(() => {
1131
1434
  const handlePointer = (e) => {
1132
1435
  if (!menuRef.current?.contains(e.target)) onCloseRef.current();
1133
1436
  };
@@ -1141,7 +1444,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1141
1444
  window.removeEventListener("keydown", handleKey);
1142
1445
  };
1143
1446
  }, []);
1144
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1447
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1145
1448
  "div",
1146
1449
  {
1147
1450
  ref: menuRef,
@@ -1157,19 +1460,20 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1157
1460
  },
1158
1461
  onContextMenu: (e) => e.preventDefault(),
1159
1462
  onKeyDown: (e) => e.stopPropagation(),
1160
- children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react17.default.Fragment, { children: [
1161
- item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
1162
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1463
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react19.default.Fragment, { children: [
1464
+ item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
1465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1163
1466
  "button",
1164
1467
  {
1165
1468
  type: "button",
1166
1469
  role: "menuitem",
1167
1470
  "data-action": item.id,
1471
+ "data-checked": item.checked ? "" : void 0,
1168
1472
  disabled: item.disabled,
1169
- className: "block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent",
1473
+ className: `block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent ${item.checked ? "font-semibold" : ""}`,
1170
1474
  onClick: () => {
1171
1475
  onClose();
1172
- item.run();
1476
+ (0, import_spreadsheet2.runSpreadsheetMenuItem)(item);
1173
1477
  },
1174
1478
  children: item.label
1175
1479
  }
@@ -1180,7 +1484,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1180
1484
  };
1181
1485
 
1182
1486
  // src/spreadsheet/SpreadsheetGroupRow.tsx
1183
- var import_jsx_runtime6 = require("react/jsx-runtime");
1487
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1184
1488
  var formatSummary = (value) => value === null ? "\u2013" : String(value);
1185
1489
  var SpreadsheetGroupRow = ({
1186
1490
  group,
@@ -1190,8 +1494,8 @@ var SpreadsheetGroupRow = ({
1190
1494
  trailingCell
1191
1495
  }) => {
1192
1496
  const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
1193
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
1194
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1497
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
1498
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1195
1499
  "button",
1196
1500
  {
1197
1501
  type: "button",
@@ -1207,22 +1511,22 @@ var SpreadsheetGroupRow = ({
1207
1511
  ) }),
1208
1512
  columns.map(({ column }) => {
1209
1513
  const summary = group.summaries[column.id];
1210
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-r border-border px-2 py-1 truncate max-w-0 text-foreground", style: { maxWidth: 0, overflow: "hidden" }, children: column.id === labelColId ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "truncate", children: [
1514
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-r border-border px-2 py-1 truncate max-w-0 text-foreground", style: { maxWidth: 0, overflow: "hidden" }, children: column.id === labelColId ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "truncate", children: [
1211
1515
  group.label,
1212
1516
  " ",
1213
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
1517
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
1214
1518
  "(",
1215
1519
  group.rowIndices.length,
1216
1520
  ")"
1217
1521
  ] })
1218
- ] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
1522
+ ] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
1219
1523
  }),
1220
- trailingCell && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1524
+ trailingCell && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1221
1525
  ] });
1222
1526
  };
1223
1527
 
1224
1528
  // src/spreadsheet/useSpreadsheetTableInteractions.ts
1225
- var import_react18 = require("react");
1529
+ var import_react20 = require("react");
1226
1530
  var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
1227
1531
  function gridHasFocus(grid) {
1228
1532
  if (!grid || typeof document === "undefined") return false;
@@ -1231,12 +1535,12 @@ function gridHasFocus(grid) {
1231
1535
  return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
1232
1536
  }
1233
1537
  function useSpreadsheetClipboard(gridRef, state, readOnly) {
1234
- const stateRef = (0, import_react18.useRef)(state);
1538
+ const stateRef = (0, import_react20.useRef)(state);
1235
1539
  stateRef.current = state;
1236
- const readOnlyRef = (0, import_react18.useRef)(readOnly);
1540
+ const readOnlyRef = (0, import_react20.useRef)(readOnly);
1237
1541
  readOnlyRef.current = readOnly;
1238
- const copiedRef = (0, import_react18.useRef)(false);
1239
- (0, import_react18.useEffect)(() => {
1542
+ const copiedRef = (0, import_react20.useRef)(false);
1543
+ (0, import_react20.useEffect)(() => {
1240
1544
  if (typeof document === "undefined") return;
1241
1545
  const handleCopy = (e) => {
1242
1546
  const s = stateRef.current;
@@ -1259,7 +1563,7 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
1259
1563
  document.removeEventListener("paste", handlePaste);
1260
1564
  };
1261
1565
  }, [gridRef]);
1262
- const copyShortcutFallback = (0, import_react18.useCallback)(() => {
1566
+ const copyShortcutFallback = (0, import_react20.useCallback)(() => {
1263
1567
  copiedRef.current = false;
1264
1568
  setTimeout(() => {
1265
1569
  if (copiedRef.current) return;
@@ -1271,13 +1575,13 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
1271
1575
  return { copyShortcutFallback };
1272
1576
  }
1273
1577
  function useFillHandle(state) {
1274
- const [drag, setDrag] = (0, import_react18.useState)(null);
1275
- const dragRef = (0, import_react18.useRef)(drag);
1578
+ const [drag, setDrag] = (0, import_react20.useState)(null);
1579
+ const dragRef = (0, import_react20.useRef)(drag);
1276
1580
  dragRef.current = drag;
1277
- const stateRef = (0, import_react18.useRef)(state);
1581
+ const stateRef = (0, import_react20.useRef)(state);
1278
1582
  stateRef.current = state;
1279
1583
  const dragging = drag !== null;
1280
- (0, import_react18.useEffect)(() => {
1584
+ (0, import_react20.useEffect)(() => {
1281
1585
  if (!dragging) return;
1282
1586
  const handleMouseUp = () => {
1283
1587
  const current = dragRef.current;
@@ -1287,13 +1591,13 @@ function useFillHandle(state) {
1287
1591
  window.addEventListener("mouseup", handleMouseUp);
1288
1592
  return () => window.removeEventListener("mouseup", handleMouseUp);
1289
1593
  }, [dragging]);
1290
- const startFill = (0, import_react18.useCallback)((e) => {
1594
+ const startFill = (0, import_react20.useCallback)((e) => {
1291
1595
  e.preventDefault();
1292
1596
  e.stopPropagation();
1293
1597
  const range = stateRef.current.selectedRange;
1294
1598
  if (range) setDrag({ source: (0, import_spreadsheet3.normalizeRange)(range), target: null });
1295
1599
  }, []);
1296
- const extendFill = (0, import_react18.useCallback)((row, col) => {
1600
+ const extendFill = (0, import_react20.useCallback)((row, col) => {
1297
1601
  const current = dragRef.current;
1298
1602
  if (!current) return;
1299
1603
  setDrag({ source: current.source, target: (0, import_spreadsheet3.computeFillTarget)(current.source, { row, col }) });
@@ -1302,24 +1606,24 @@ function useFillHandle(state) {
1302
1606
  }
1303
1607
 
1304
1608
  // src/spreadsheet/FormulaHints.tsx
1305
- var import_react19 = require("react");
1609
+ var import_react21 = require("react");
1306
1610
  var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
1307
- var import_jsx_runtime7 = require("react/jsx-runtime");
1611
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1308
1612
  var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1309
- const [caret, setCaret] = (0, import_react19.useState)(null);
1310
- const [focused, setFocused] = (0, import_react19.useState)(false);
1311
- const [selected, setSelected] = (0, import_react19.useState)(0);
1312
- const [dismissed, setDismissed] = (0, import_react19.useState)(false);
1313
- const [position, setPosition] = (0, import_react19.useState)(null);
1314
- const pendingCaret = (0, import_react19.useRef)(null);
1315
- const hints = (0, import_react19.useMemo)(
1613
+ const [caret, setCaret] = (0, import_react21.useState)(null);
1614
+ const [focused, setFocused] = (0, import_react21.useState)(false);
1615
+ const [selected, setSelected] = (0, import_react21.useState)(0);
1616
+ const [dismissed, setDismissed] = (0, import_react21.useState)(false);
1617
+ const [position, setPosition] = (0, import_react21.useState)(null);
1618
+ const pendingCaret = (0, import_react21.useRef)(null);
1619
+ const hints = (0, import_react21.useMemo)(
1316
1620
  () => (0, import_spreadsheet4.getFormulaHints)(value, caret ?? value.length, { limit }),
1317
1621
  [value, caret, limit]
1318
1622
  );
1319
1623
  const suggestions = dismissed ? [] : hints.suggestions;
1320
1624
  const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
1321
1625
  const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
1322
- const latest = (0, import_react19.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
1626
+ const latest = (0, import_react21.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
1323
1627
  latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
1324
1628
  const accept = (index) => {
1325
1629
  const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
@@ -1329,7 +1633,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1329
1633
  pendingCaret.current = next.caret;
1330
1634
  commit(next.value);
1331
1635
  };
1332
- (0, import_react19.useEffect)(() => {
1636
+ (0, import_react21.useEffect)(() => {
1333
1637
  const input = inputRef.current;
1334
1638
  if (!input) return;
1335
1639
  const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
@@ -1379,7 +1683,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1379
1683
  input.removeEventListener("keydown", onKeyDown);
1380
1684
  };
1381
1685
  }, [inputRef]);
1382
- (0, import_react19.useLayoutEffect)(() => {
1686
+ (0, import_react21.useLayoutEffect)(() => {
1383
1687
  const input = inputRef.current;
1384
1688
  const at = pendingCaret.current;
1385
1689
  if (!input || at === null || input.value !== value) return;
@@ -1388,7 +1692,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1388
1692
  setCaret(at);
1389
1693
  setSelected(0);
1390
1694
  }, [value, inputRef]);
1391
- (0, import_react19.useLayoutEffect)(() => {
1695
+ (0, import_react21.useLayoutEffect)(() => {
1392
1696
  const input = inputRef.current;
1393
1697
  if (!visible || !input) return;
1394
1698
  const rect = input.getBoundingClientRect();
@@ -1396,7 +1700,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1396
1700
  }, [visible, value, inputRef]);
1397
1701
  if (!visible) return null;
1398
1702
  const { activeCall } = hints;
1399
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1400
1704
  "div",
1401
1705
  {
1402
1706
  "data-formula-hints": "",
@@ -1412,8 +1716,8 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1412
1716
  },
1413
1717
  onMouseDown: (e) => e.preventDefault(),
1414
1718
  children: [
1415
- activeCall && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
1416
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1719
+ activeCall && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
1720
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1417
1721
  "span",
1418
1722
  {
1419
1723
  "data-active": part.active ? "" : void 0,
@@ -1422,13 +1726,13 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1422
1726
  },
1423
1727
  i
1424
1728
  )) }),
1425
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1426
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
1729
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1730
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
1427
1731
  ": ",
1428
1732
  activeCall.argument.description
1429
1733
  ] }) : activeCall.description })
1430
1734
  ] }),
1431
- suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { role: "listbox", "aria-label": "Formula suggestions", className: "max-h-64 overflow-auto py-1 m-0 list-none p-0", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1735
+ suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { role: "listbox", "aria-label": "Formula suggestions", className: "max-h-64 overflow-auto py-1 m-0 list-none p-0", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1432
1736
  "li",
1433
1737
  {
1434
1738
  role: "option",
@@ -1440,8 +1744,8 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1440
1744
  accept(i);
1441
1745
  },
1442
1746
  children: [
1443
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
1444
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
1747
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
1445
1749
  ]
1446
1750
  },
1447
1751
  s.name
@@ -1452,7 +1756,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1452
1756
  };
1453
1757
 
1454
1758
  // src/spreadsheet/SpreadsheetEditor.tsx
1455
- var import_jsx_runtime8 = require("react/jsx-runtime");
1759
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1456
1760
  var COLUMN_HEADER_HEIGHT = 28;
1457
1761
  var ROW_HEADER_WIDTH = 48;
1458
1762
  function frozenCellStyle(viewRow, viewCol) {
@@ -1479,6 +1783,19 @@ function spillBorderStyle(range, row, col) {
1479
1783
  borderRight: col === range.endCol ? line : void 0
1480
1784
  };
1481
1785
  }
1786
+ function CellText({ cell }) {
1787
+ const shown = (0, import_spreadsheet5.formatCellForDisplay)(cell);
1788
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1789
+ "span",
1790
+ {
1791
+ className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
1792
+ title: cell.error ? cell.errorMessage ?? cell.error : void 0,
1793
+ "data-numeric": shown.numeric ? "" : void 0,
1794
+ style: shown.numeric || shown.color ? { ...shown.numeric ? { textAlign: "right" } : {}, ...shown.color ? { color: (0, import_spreadsheet5.numberFormatColorToCss)(shown.color) } : {} } : void 0,
1795
+ children: shown.text
1796
+ }
1797
+ );
1798
+ }
1482
1799
  var SpreadsheetEditor = ({
1483
1800
  state: externalState,
1484
1801
  readOnly = false,
@@ -1489,23 +1806,23 @@ var SpreadsheetEditor = ({
1489
1806
  }) => {
1490
1807
  const defaultState = useSpreadsheetEditor();
1491
1808
  const state = externalState || defaultState;
1492
- const gridRef = (0, import_react20.useRef)(null);
1493
- const formulaBarRef = (0, import_react20.useRef)(null);
1494
- const cellInputRef = (0, import_react20.useRef)(null);
1495
- const [resizingColId, setResizingColId] = (0, import_react20.useState)(null);
1496
- const [hoveredColId, setHoveredColId] = (0, import_react20.useState)(null);
1497
- const [resizeStartX, setResizeStartX] = (0, import_react20.useState)(0);
1498
- const [resizeStartWidth, setResizeStartWidth] = (0, import_react20.useState)(0);
1809
+ const gridRef = (0, import_react22.useRef)(null);
1810
+ const formulaBarRef = (0, import_react22.useRef)(null);
1811
+ const cellInputRef = (0, import_react22.useRef)(null);
1812
+ const [resizingColId, setResizingColId] = (0, import_react22.useState)(null);
1813
+ const [hoveredColId, setHoveredColId] = (0, import_react22.useState)(null);
1814
+ const [resizeStartX, setResizeStartX] = (0, import_react22.useState)(0);
1815
+ const [resizeStartWidth, setResizeStartWidth] = (0, import_react22.useState)(0);
1499
1816
  const view = state.viewModel;
1500
1817
  const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
1501
1818
  const { fillTarget, startFill, extendFill } = useFillHandle(state);
1502
- const [menu, setMenu] = (0, import_react20.useState)(null);
1819
+ const [menu, setMenu] = (0, import_react22.useState)(null);
1503
1820
  const filter = state.document.table?.filter;
1504
- const filteredColIds = (0, import_react20.useMemo)(
1821
+ const filteredColIds = (0, import_react22.useMemo)(
1505
1822
  () => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
1506
1823
  [filter]
1507
1824
  );
1508
- const handleCellContextMenu = (0, import_react20.useCallback)((rIdx, cIdx, e) => {
1825
+ const handleCellContextMenu = (0, import_react22.useCallback)((rIdx, cIdx, e) => {
1509
1826
  if (readOnly) return;
1510
1827
  e.preventDefault();
1511
1828
  if (state.editingCell) state.commitEditing();
@@ -1514,28 +1831,28 @@ var SpreadsheetEditor = ({
1514
1831
  }
1515
1832
  setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet5.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
1516
1833
  }, [readOnly, state]);
1517
- const activeRawValue = (0, import_react20.useMemo)(() => {
1834
+ const activeRawValue = (0, import_react22.useMemo)(() => {
1518
1835
  if (!state.activeCell) return "";
1519
1836
  const row = state.document.rows[state.activeCell.row];
1520
1837
  const col = state.document.columns[state.activeCell.col];
1521
1838
  if (!row || !col) return "";
1522
1839
  return state.getCell(row.id, col.id).raw;
1523
1840
  }, [state.activeCell, state.document]);
1524
- const activeSpill = (0, import_react20.useMemo)(
1841
+ const activeSpill = (0, import_react22.useMemo)(
1525
1842
  () => state.activeCell ? state.controller.getSpillAt(state.activeCell.row, state.activeCell.col) : null,
1526
1843
  // eslint-disable-next-line react-hooks/exhaustive-deps
1527
1844
  [state.controller, state.activeCell, state.document]
1528
1845
  );
1529
1846
  const spillFormula = activeSpill && !activeRawValue && activeSpill.key !== coordKey(state.activeCell) ? activeSpill.formula : "";
1530
- const coordLabel = (0, import_react20.useMemo)(() => {
1847
+ const coordLabel = (0, import_react22.useMemo)(() => {
1531
1848
  if (!state.activeCell) return "--";
1532
1849
  const colName = (0, import_spreadsheet5.indexToColName)(state.activeCell.col);
1533
1850
  return `${colName}${state.activeCell.row + 1}`;
1534
1851
  }, [state.activeCell]);
1535
- const [formulaDraft, setFormulaDraft] = (0, import_react20.useState)(null);
1852
+ const [formulaDraft, setFormulaDraft] = (0, import_react22.useState)(null);
1536
1853
  const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
1537
1854
  const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
1538
- (0, import_react20.useEffect)(() => {
1855
+ (0, import_react22.useEffect)(() => {
1539
1856
  setFormulaDraft(null);
1540
1857
  }, [state.activeCell?.row, state.activeCell?.col]);
1541
1858
  const commitFormulaBar = () => {
@@ -1551,19 +1868,19 @@ var SpreadsheetEditor = ({
1551
1868
  }
1552
1869
  setFormulaDraft(null);
1553
1870
  };
1554
- const handleCellClick = (0, import_react20.useCallback)((rIdx, cIdx, e) => {
1871
+ const handleCellClick = (0, import_react22.useCallback)((rIdx, cIdx, e) => {
1555
1872
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
1556
1873
  state.commitEditing();
1557
1874
  }
1558
1875
  state.selectCell(rIdx, cIdx, e.shiftKey);
1559
1876
  }, [state]);
1560
- const handleCellDblClick = (0, import_react20.useCallback)((rIdx, cIdx) => {
1877
+ const handleCellDblClick = (0, import_react22.useCallback)((rIdx, cIdx) => {
1561
1878
  if (readOnly) return;
1562
1879
  const col = state.document.columns[cIdx];
1563
1880
  if (col?.readOnly) return;
1564
1881
  state.startEditing(rIdx, cIdx);
1565
1882
  }, [readOnly, state]);
1566
- const handleKeyDown = (0, import_react20.useCallback)((e) => {
1883
+ const handleKeyDown = (0, import_react22.useCallback)((e) => {
1567
1884
  if (state.editingCell) {
1568
1885
  if (e.key === "Enter") {
1569
1886
  e.preventDefault();
@@ -1632,11 +1949,11 @@ var SpreadsheetEditor = ({
1632
1949
  }
1633
1950
  }
1634
1951
  }, [readOnly, state, copyShortcutFallback]);
1635
- const stateRef = (0, import_react20.useRef)(state);
1952
+ const stateRef = (0, import_react22.useRef)(state);
1636
1953
  stateRef.current = state;
1637
- const onColumnResizeRef = (0, import_react20.useRef)(onColumnResize);
1954
+ const onColumnResizeRef = (0, import_react22.useRef)(onColumnResize);
1638
1955
  onColumnResizeRef.current = onColumnResize;
1639
- (0, import_react20.useEffect)(() => {
1956
+ (0, import_react22.useEffect)(() => {
1640
1957
  if (!resizingColId) return;
1641
1958
  if (typeof document !== "undefined") {
1642
1959
  document.body.style.cursor = "col-resize";
@@ -1687,7 +2004,7 @@ var SpreadsheetEditor = ({
1687
2004
  window.removeEventListener("mouseup", handleMouseUp);
1688
2005
  };
1689
2006
  }, [resizingColId, resizeStartX, resizeStartWidth]);
1690
- const measureElementContentWidth = (0, import_react20.useCallback)((el, container) => {
2007
+ const measureElementContentWidth = (0, import_react22.useCallback)((el, container) => {
1691
2008
  if (typeof document === "undefined") return 0;
1692
2009
  let maxW = el.scrollWidth || 0;
1693
2010
  const firstChild = el.firstElementChild;
@@ -1735,7 +2052,7 @@ var SpreadsheetEditor = ({
1735
2052
  }
1736
2053
  return maxW;
1737
2054
  }, []);
1738
- const autoFitColumn = (0, import_react20.useCallback)((colId, colIndex) => {
2055
+ const autoFitColumn = (0, import_react22.useCallback)((colId, colIndex) => {
1739
2056
  const col = state.document.columns.find((c) => c.id === colId);
1740
2057
  if (!col) return;
1741
2058
  let calculatedWidth = 0;
@@ -1771,12 +2088,12 @@ var SpreadsheetEditor = ({
1771
2088
  onColumnResize(updatedCol.id, updatedCol.width || 130);
1772
2089
  }
1773
2090
  }, [state, measureElementContentWidth, onColumnResize]);
1774
- const handleResizeDblClick = (0, import_react20.useCallback)((colId, colIndex, e) => {
2091
+ const handleResizeDblClick = (0, import_react22.useCallback)((colId, colIndex, e) => {
1775
2092
  e.preventDefault();
1776
2093
  e.stopPropagation();
1777
2094
  autoFitColumn(colId, colIndex);
1778
2095
  }, [autoFitColumn]);
1779
- const handleHeaderDblClick = (0, import_react20.useCallback)((colId, colIndex, e) => {
2096
+ const handleHeaderDblClick = (0, import_react22.useCallback)((colId, colIndex, e) => {
1780
2097
  const target = e.currentTarget;
1781
2098
  if (target) {
1782
2099
  const rect = target.getBoundingClientRect();
@@ -1798,7 +2115,7 @@ var SpreadsheetEditor = ({
1798
2115
  const isActiveCell = (rIdx, cIdx) => {
1799
2116
  return state.activeCell?.row === rIdx && state.activeCell?.col === cIdx;
1800
2117
  };
1801
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1802
2119
  "div",
1803
2120
  {
1804
2121
  ref: gridRef,
@@ -1808,10 +2125,10 @@ var SpreadsheetEditor = ({
1808
2125
  role: "grid",
1809
2126
  "aria-label": "Spreadsheet",
1810
2127
  children: [
1811
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
1812
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
1813
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
1814
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2128
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
2129
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
2130
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
2131
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1815
2132
  "input",
1816
2133
  {
1817
2134
  ref: formulaBarRef,
@@ -1847,7 +2164,7 @@ var SpreadsheetEditor = ({
1847
2164
  }
1848
2165
  }
1849
2166
  ),
1850
- !formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2167
+ !formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1851
2168
  FormulaHints,
1852
2169
  {
1853
2170
  inputRef: formulaBarRef,
@@ -1856,10 +2173,10 @@ var SpreadsheetEditor = ({
1856
2173
  }
1857
2174
  )
1858
2175
  ] }),
1859
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
1860
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { className: "h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs", children: [
1861
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
1862
- view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2176
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
2177
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { className: "h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs", children: [
2178
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
2179
+ view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1863
2180
  "th",
1864
2181
  {
1865
2182
  "data-col": cIdx,
@@ -1874,16 +2191,16 @@ var SpreadsheetEditor = ({
1874
2191
  onClick: () => onColumnHeaderClick?.(col),
1875
2192
  onDoubleClick: (e) => handleHeaderDblClick(col.id, cIdx, e),
1876
2193
  children: [
1877
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
1878
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
1879
- filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
1880
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
2194
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
2196
+ filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
2197
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
1881
2198
  "(",
1882
2199
  col.key,
1883
2200
  ")"
1884
2201
  ] })
1885
2202
  ] }),
1886
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2203
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1887
2204
  "div",
1888
2205
  {
1889
2206
  className: "col-resize-handle",
@@ -1900,7 +2217,7 @@ var SpreadsheetEditor = ({
1900
2217
  onDoubleClick: (e) => handleResizeDblClick(col.id, cIdx, e),
1901
2218
  role: "separator",
1902
2219
  "aria-orientation": "vertical",
1903
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2220
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1904
2221
  "div",
1905
2222
  {
1906
2223
  className: "col-resize-handle-line",
@@ -1919,19 +2236,19 @@ var SpreadsheetEditor = ({
1919
2236
  },
1920
2237
  col.id
1921
2238
  )),
1922
- onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2239
+ onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1923
2240
  "th",
1924
2241
  {
1925
2242
  className: "w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors",
1926
2243
  onClick: onAddColumnClick,
1927
2244
  title: "Add Column",
1928
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
2245
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
1929
2246
  }
1930
2247
  )
1931
2248
  ] }) }),
1932
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tbody", { children: view.rows.map((viewRow) => {
2249
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: view.rows.map((viewRow) => {
1933
2250
  if (viewRow.kind === "group") {
1934
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2251
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1935
2252
  SpreadsheetGroupRow,
1936
2253
  {
1937
2254
  group: viewRow.group,
@@ -1945,14 +2262,14 @@ var SpreadsheetEditor = ({
1945
2262
  }
1946
2263
  const displayRowIndex = viewRow.index;
1947
2264
  const selection = state.selectedRange ? (0, import_spreadsheet5.normalizeRange)(state.selectedRange) : null;
1948
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2265
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1949
2266
  "tr",
1950
2267
  {
1951
2268
  "data-row": displayRowIndex,
1952
2269
  className: `border-b border-border hover:bg-muted/10 transition-colors ${viewRow.section !== "body" ? "bg-muted/30 font-semibold" : ""}`,
1953
2270
  style: { height: viewRow.row.height || 32 },
1954
2271
  children: [
1955
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2272
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1956
2273
  "td",
1957
2274
  {
1958
2275
  className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
@@ -1973,7 +2290,7 @@ var SpreadsheetEditor = ({
1973
2290
  const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1974
2291
  const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet5.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1975
2292
  const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet5.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1976
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2293
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1977
2294
  "td",
1978
2295
  {
1979
2296
  "data-row": rIdx,
@@ -1992,8 +2309,8 @@ var SpreadsheetEditor = ({
1992
2309
  onContextMenu: (e) => handleCellContextMenu(rIdx, cIdx, e),
1993
2310
  onMouseOver: () => extendFill(displayRowIndex, viewCol.index),
1994
2311
  children: [
1995
- isEditing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1996
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2312
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2313
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1997
2314
  "input",
1998
2315
  {
1999
2316
  ref: cellInputRef,
@@ -2009,17 +2326,10 @@ var SpreadsheetEditor = ({
2009
2326
  autoFocus: true
2010
2327
  }
2011
2328
  ),
2012
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
2013
- ] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2014
- "span",
2015
- {
2016
- className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
2017
- title: cell.error ? cell.errorMessage ?? cell.error : void 0,
2018
- children: (0, import_spreadsheet5.formatCellDisplay)(cell)
2019
- }
2020
- ),
2021
- activeSpill && (0, import_spreadsheet5.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
2022
- showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2329
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
2330
+ ] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CellText, { cell }),
2331
+ activeSpill && (0, import_spreadsheet5.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
2332
+ showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2023
2333
  "div",
2024
2334
  {
2025
2335
  className: "spreadsheet-fill-handle",
@@ -2034,14 +2344,14 @@ var SpreadsheetEditor = ({
2034
2344
  viewCol.column.id
2035
2345
  );
2036
2346
  }),
2037
- onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
2347
+ onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
2038
2348
  ]
2039
2349
  },
2040
2350
  viewRow.row.id
2041
2351
  );
2042
2352
  }) })
2043
2353
  ] }) }),
2044
- menu && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2354
+ menu && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2045
2355
  SpreadsheetContextMenu,
2046
2356
  {
2047
2357
  items: menu.items,
@@ -2053,7 +2363,7 @@ var SpreadsheetEditor = ({
2053
2363
  }
2054
2364
  }
2055
2365
  ),
2056
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: `
2366
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("style", { children: `
2057
2367
  .col-resize-handle:hover .col-resize-handle-line {
2058
2368
  background-color: var(--primary, #6366f1);
2059
2369
  opacity: 0.75;
@@ -2076,10 +2386,10 @@ var SpreadsheetEditor = ({
2076
2386
  };
2077
2387
 
2078
2388
  // src/slots.tsx
2079
- var import_react21 = require("react");
2080
- var import_jsx_runtime9 = require("react/jsx-runtime");
2389
+ var import_react23 = require("react");
2390
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2081
2391
  var defaultSlots = {
2082
- Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2392
+ Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2083
2393
  "button",
2084
2394
  {
2085
2395
  type: "button",
@@ -2089,112 +2399,128 @@ var defaultSlots = {
2089
2399
  children: props.children
2090
2400
  }
2091
2401
  ),
2092
- Tooltip: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
2093
- NodeHeader: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
2094
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
2402
+ Tooltip: (props) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
2403
+ NodeHeader: (props) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
2404
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
2095
2405
  props.icon,
2096
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold", children: props.title })
2406
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold", children: props.title })
2097
2407
  ] }),
2098
- props.onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
2408
+ props.onClose && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
2099
2409
  ] })
2100
2410
  };
2101
- var EditorSlotsContext = (0, import_react21.createContext)(defaultSlots);
2411
+ var EditorSlotsContext = (0, import_react23.createContext)(defaultSlots);
2102
2412
  function EditorSlotsProvider({ slots, children }) {
2103
- const mergedSlots = (0, import_react21.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
2104
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
2413
+ const mergedSlots = (0, import_react23.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
2414
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
2105
2415
  }
2106
2416
  function useEditorSlots() {
2107
- return (0, import_react21.useContext)(EditorSlotsContext);
2417
+ return (0, import_react23.useContext)(EditorSlotsContext);
2108
2418
  }
2109
2419
 
2110
2420
  // src/image-editor/useImageEditor.ts
2111
- var import_react22 = require("react");
2421
+ var import_react24 = require("react");
2112
2422
  var import_image_editor = require("@pixerate/editor/image-editor");
2113
2423
  function useImageEditor(options = {}) {
2114
- const controllerRef = (0, import_react22.useRef)(null);
2424
+ const controllerRef = (0, import_react24.useRef)(null);
2115
2425
  if (!controllerRef.current) {
2116
2426
  controllerRef.current = new import_image_editor.ImageEditorController(options);
2117
2427
  }
2118
2428
  const controller = controllerRef.current;
2119
- const [state, setState] = (0, import_react22.useState)(() => controller.getState());
2120
- const [historyTicks, setHistoryTicks] = (0, import_react22.useState)(0);
2121
- (0, import_react22.useEffect)(() => {
2429
+ const [state, setState] = (0, import_react24.useState)(() => controller.getState());
2430
+ const [historyTicks, setHistoryTicks] = (0, import_react24.useState)(0);
2431
+ const [histogram, setHistogram] = (0, import_react24.useState)(() => controller.getHistogram());
2432
+ (0, import_react24.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
2433
+ (0, import_react24.useEffect)(() => {
2122
2434
  const unsub = controller.subscribe((next) => {
2123
2435
  setState(next);
2124
2436
  setHistoryTicks((t) => t + 1);
2125
2437
  });
2126
2438
  return unsub;
2127
2439
  }, [controller]);
2128
- const loadedImageRef = (0, import_react22.useRef)(options.image);
2129
- (0, import_react22.useEffect)(() => {
2440
+ const loadedImageRef = (0, import_react24.useRef)(options.image);
2441
+ (0, import_react24.useEffect)(() => {
2130
2442
  if (options.image === loadedImageRef.current) return;
2131
2443
  loadedImageRef.current = options.image;
2132
2444
  if (options.image) {
2133
2445
  controller.loadImage(options.image);
2134
2446
  }
2135
2447
  }, [options.image, controller]);
2136
- const setTool = (0, import_react22.useCallback)((tool) => controller.setTool(tool), [controller]);
2137
- const setCrop = (0, import_react22.useCallback)(
2448
+ const setTool = (0, import_react24.useCallback)((tool) => controller.setTool(tool), [controller]);
2449
+ const setCrop = (0, import_react24.useCallback)(
2138
2450
  (crop, addToHistory = true) => controller.setCrop(crop, addToHistory),
2139
2451
  [controller]
2140
2452
  );
2141
- const applyCropPreset = (0, import_react22.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
2142
- const resetCrop = (0, import_react22.useCallback)(() => controller.resetCrop(), [controller]);
2143
- const rotate = (0, import_react22.useCallback)((deg) => controller.rotate(deg), [controller]);
2144
- const setRotation = (0, import_react22.useCallback)((deg) => controller.setRotation(deg), [controller]);
2145
- const flipHorizontal = (0, import_react22.useCallback)(() => controller.flipHorizontal(), [controller]);
2146
- const flipVertical = (0, import_react22.useCallback)(() => controller.flipVertical(), [controller]);
2147
- const setAdjustments = (0, import_react22.useCallback)(
2453
+ const applyCropPreset = (0, import_react24.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
2454
+ const resetCrop = (0, import_react24.useCallback)(() => controller.resetCrop(), [controller]);
2455
+ const rotate = (0, import_react24.useCallback)((deg) => controller.rotate(deg), [controller]);
2456
+ const setRotation = (0, import_react24.useCallback)((deg) => controller.setRotation(deg), [controller]);
2457
+ const flipHorizontal = (0, import_react24.useCallback)(() => controller.flipHorizontal(), [controller]);
2458
+ const flipVertical = (0, import_react24.useCallback)(() => controller.flipVertical(), [controller]);
2459
+ const setAdjustments = (0, import_react24.useCallback)(
2148
2460
  (partial, commitToHistory = true) => controller.setAdjustments(partial, commitToHistory),
2149
2461
  [controller]
2150
2462
  );
2151
- const resetAdjustments = (0, import_react22.useCallback)(() => controller.resetAdjustments(), [controller]);
2152
- const upgradeProcess = (0, import_react22.useCallback)(() => controller.upgradeProcess(), [controller]);
2153
- const setDepthMask = (0, import_react22.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
2154
- const resetDepthMask = (0, import_react22.useCallback)(() => controller.resetDepthMask(), [controller]);
2155
- const addAnnotation = (0, import_react22.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
2156
- const updateAnnotation = (0, import_react22.useCallback)(
2463
+ const resetAdjustments = (0, import_react24.useCallback)(() => controller.resetAdjustments(), [controller]);
2464
+ const upgradeProcess = (0, import_react24.useCallback)(() => controller.upgradeProcess(), [controller]);
2465
+ const addAdjustmentLayer = (0, import_react24.useCallback)(
2466
+ (init, index) => controller.addAdjustmentLayer(init, index),
2467
+ [controller]
2468
+ );
2469
+ const updateAdjustmentLayer = (0, import_react24.useCallback)(
2470
+ (id, update, commitToHistory = true) => controller.updateAdjustmentLayer(id, update, commitToHistory),
2471
+ [controller]
2472
+ );
2473
+ const removeAdjustmentLayer = (0, import_react24.useCallback)((id) => controller.removeAdjustmentLayer(id), [controller]);
2474
+ const moveAdjustmentLayer = (0, import_react24.useCallback)((id, to) => controller.moveAdjustmentLayer(id, to), [controller]);
2475
+ const toggleAdjustmentLayer = (0, import_react24.useCallback)(
2476
+ (id, enabled) => controller.toggleAdjustmentLayer(id, enabled),
2477
+ [controller]
2478
+ );
2479
+ const setDepthMask = (0, import_react24.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
2480
+ const resetDepthMask = (0, import_react24.useCallback)(() => controller.resetDepthMask(), [controller]);
2481
+ const addAnnotation = (0, import_react24.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
2482
+ const updateAnnotation = (0, import_react24.useCallback)(
2157
2483
  (id, partial, commitToHistory = true) => controller.updateAnnotation(id, partial, commitToHistory),
2158
2484
  [controller]
2159
2485
  );
2160
- const removeAnnotation = (0, import_react22.useCallback)((id) => controller.removeAnnotation(id), [controller]);
2161
- const clearAnnotations = (0, import_react22.useCallback)(() => controller.clearAnnotations(), [controller]);
2162
- const selectAnnotation = (0, import_react22.useCallback)((id) => controller.selectAnnotation(id), [controller]);
2163
- const undo = (0, import_react22.useCallback)(() => controller.undo(), [controller]);
2164
- const redo = (0, import_react22.useCallback)(() => controller.redo(), [controller]);
2165
- const reset = (0, import_react22.useCallback)(() => controller.reset(), [controller]);
2166
- const zoomIn = (0, import_react22.useCallback)((factor) => controller.zoomIn(factor), [controller]);
2167
- const zoomOut = (0, import_react22.useCallback)((factor) => controller.zoomOut(factor), [controller]);
2168
- const resetZoom = (0, import_react22.useCallback)(() => controller.resetZoom(), [controller]);
2169
- const setZoom = (0, import_react22.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
2170
- const setPan = (0, import_react22.useCallback)((pan) => controller.setPan(pan), [controller]);
2171
- const setComparing = (0, import_react22.useCallback)((comp) => controller.setComparing(comp), [controller]);
2172
- const loadImage = (0, import_react22.useCallback)((src) => controller.loadImage(src), [controller]);
2173
- const toDataURL = (0, import_react22.useCallback)((opts) => controller.toDataURL(opts), [controller]);
2174
- const toBlob = (0, import_react22.useCallback)((opts) => controller.toBlob(opts), [controller]);
2175
- const toJSON = (0, import_react22.useCallback)(() => controller.toJSON(), [controller]);
2176
- const loadJSON = (0, import_react22.useCallback)((data) => controller.loadJSON(data), [controller]);
2177
- const renderMask = (0, import_react22.useCallback)(
2486
+ const removeAnnotation = (0, import_react24.useCallback)((id) => controller.removeAnnotation(id), [controller]);
2487
+ const clearAnnotations = (0, import_react24.useCallback)(() => controller.clearAnnotations(), [controller]);
2488
+ const selectAnnotation = (0, import_react24.useCallback)((id) => controller.selectAnnotation(id), [controller]);
2489
+ const undo = (0, import_react24.useCallback)(() => controller.undo(), [controller]);
2490
+ const redo = (0, import_react24.useCallback)(() => controller.redo(), [controller]);
2491
+ const reset = (0, import_react24.useCallback)(() => controller.reset(), [controller]);
2492
+ const zoomIn = (0, import_react24.useCallback)((factor) => controller.zoomIn(factor), [controller]);
2493
+ const zoomOut = (0, import_react24.useCallback)((factor) => controller.zoomOut(factor), [controller]);
2494
+ const resetZoom = (0, import_react24.useCallback)(() => controller.resetZoom(), [controller]);
2495
+ const setZoom = (0, import_react24.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
2496
+ const setPan = (0, import_react24.useCallback)((pan) => controller.setPan(pan), [controller]);
2497
+ const setComparing = (0, import_react24.useCallback)((comp) => controller.setComparing(comp), [controller]);
2498
+ const loadImage = (0, import_react24.useCallback)((src) => controller.loadImage(src), [controller]);
2499
+ const toDataURL = (0, import_react24.useCallback)((opts) => controller.toDataURL(opts), [controller]);
2500
+ const toBlob = (0, import_react24.useCallback)((opts) => controller.toBlob(opts), [controller]);
2501
+ const toJSON = (0, import_react24.useCallback)(() => controller.toJSON(), [controller]);
2502
+ const loadJSON = (0, import_react24.useCallback)((data) => controller.loadJSON(data), [controller]);
2503
+ const renderMask = (0, import_react24.useCallback)(
2178
2504
  (opts) => controller.renderMask(opts),
2179
2505
  [controller]
2180
2506
  );
2181
- const toMaskDataURL = (0, import_react22.useCallback)(
2507
+ const toMaskDataURL = (0, import_react24.useCallback)(
2182
2508
  (opts) => controller.toMaskDataURL(opts),
2183
2509
  [controller]
2184
2510
  );
2185
- const toSourceDataURL = (0, import_react22.useCallback)(
2511
+ const toSourceDataURL = (0, import_react24.useCallback)(
2186
2512
  (opts) => controller.toSourceDataURL(opts),
2187
2513
  [controller]
2188
2514
  );
2189
- const applyInpaintedImage = (0, import_react22.useCallback)(
2515
+ const applyInpaintedImage = (0, import_react24.useCallback)(
2190
2516
  (newDataUrl) => controller.applyInpaintedImage(newDataUrl),
2191
2517
  [controller]
2192
2518
  );
2193
- const applyBackgroundRemovedImage = (0, import_react22.useCallback)(
2519
+ const applyBackgroundRemovedImage = (0, import_react24.useCallback)(
2194
2520
  (newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
2195
2521
  [controller]
2196
2522
  );
2197
- return (0, import_react22.useMemo)(
2523
+ return (0, import_react24.useMemo)(
2198
2524
  () => ({
2199
2525
  controller,
2200
2526
  state,
@@ -2211,6 +2537,12 @@ function useImageEditor(options = {}) {
2211
2537
  setAdjustments,
2212
2538
  resetAdjustments,
2213
2539
  upgradeProcess,
2540
+ addAdjustmentLayer,
2541
+ updateAdjustmentLayer,
2542
+ removeAdjustmentLayer,
2543
+ moveAdjustmentLayer,
2544
+ toggleAdjustmentLayer,
2545
+ histogram,
2214
2546
  setDepthMask,
2215
2547
  resetDepthMask,
2216
2548
  addAnnotation,
@@ -2239,14 +2571,445 @@ function useImageEditor(options = {}) {
2239
2571
  applyBackgroundRemovedImage
2240
2572
  }),
2241
2573
  // eslint-disable-next-line react-hooks/exhaustive-deps
2242
- [controller, state, historyTicks]
2574
+ [controller, state, historyTicks, histogram]
2243
2575
  );
2244
2576
  }
2245
2577
 
2246
2578
  // src/image-editor/ImageEditor.tsx
2247
- var import_react23 = require("react");
2579
+ var import_react28 = __toESM(require("react"), 1);
2580
+ var import_image_editor6 = require("@pixerate/editor/image-editor");
2581
+
2582
+ // src/image-editor/ControlSlider.tsx
2248
2583
  var import_image_editor2 = require("@pixerate/editor/image-editor");
2249
- var import_jsx_runtime10 = require("react/jsx-runtime");
2584
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2585
+ var ControlSlider = ({ spec, value, onChange, disabled, label }) => {
2586
+ const name = label ?? spec.label;
2587
+ const track = (0, import_image_editor2.controlTrackBackground)(spec);
2588
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "data-control-id": spec.id, children: [
2589
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2590
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: name }),
2591
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-mono", children: (0, import_image_editor2.formatControlValue)(spec, value) })
2592
+ ] }),
2593
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2594
+ "input",
2595
+ {
2596
+ type: "range",
2597
+ min: spec.min,
2598
+ max: spec.max,
2599
+ step: spec.step,
2600
+ "aria-label": name,
2601
+ disabled,
2602
+ value,
2603
+ onChange: (e) => onChange(Number(e.target.value)),
2604
+ className: "w-full accent-indigo-500 disabled:opacity-40"
2605
+ }
2606
+ ),
2607
+ track && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "h-0.5 rounded-full opacity-70", style: { background: track } })
2608
+ ] });
2609
+ };
2610
+
2611
+ // src/image-editor/Histogram.tsx
2612
+ var import_react25 = require("react");
2613
+ var import_image_editor3 = require("@pixerate/editor/image-editor");
2614
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2615
+ var FILL = {
2616
+ luminance: "rgba(148, 163, 184, 0.45)",
2617
+ red: "rgba(239, 68, 68, 0.45)",
2618
+ green: "rgba(34, 197, 94, 0.45)",
2619
+ blue: "rgba(59, 130, 246, 0.45)"
2620
+ };
2621
+ var W = 256;
2622
+ var Histogram = ({ histogram, channel = "rgb", height = 64, className = "", style }) => {
2623
+ const channels = channel === "rgb" ? ["luminance", "red", "green", "blue"] : [channel];
2624
+ const paths = (0, import_react25.useMemo)(
2625
+ () => histogram && histogram.samples > 0 ? channels.map((c) => ({ c, d: (0, import_image_editor3.histogramPath)(histogram, c, W, height) })) : [],
2626
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2627
+ [histogram, channel, height]
2628
+ );
2629
+ const clipLow = histogram && histogram.samples ? Math.max(...histogram.shadows) / histogram.samples : 0;
2630
+ const clipHigh = histogram && histogram.samples ? Math.max(...histogram.highlights) / histogram.samples : 0;
2631
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2632
+ "svg",
2633
+ {
2634
+ viewBox: `0 0 ${W} ${height}`,
2635
+ preserveAspectRatio: "none",
2636
+ role: "img",
2637
+ "aria-label": histogram ? `Histogram of ${histogram.samples} pixels` : "Histogram (no data)",
2638
+ "data-testid": "image-editor-histogram",
2639
+ className: `w-full rounded bg-slate-950/60 ${className}`,
2640
+ style: { height, ...style },
2641
+ children: [
2642
+ paths.map(({ c, d }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d, fill: FILL[c], stroke: "none" }, c)),
2643
+ clipLow > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: 0, y: 0, width: 3, height, fill: "#3b82f6", "data-testid": "histogram-clip-shadows" }),
2644
+ clipHigh > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: W - 3, y: 0, width: 3, height, fill: "#ef4444", "data-testid": "histogram-clip-highlights" })
2645
+ ]
2646
+ }
2647
+ );
2648
+ };
2649
+ function useImageHistogram(controller) {
2650
+ const [histogram, setHistogram] = (0, import_react25.useState)(() => controller.getHistogram());
2651
+ (0, import_react25.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
2652
+ return histogram;
2653
+ }
2654
+
2655
+ // src/image-editor/AdjustmentLayersPanel.tsx
2656
+ var import_react27 = require("react");
2657
+ var import_image_editor5 = require("@pixerate/editor/image-editor");
2658
+
2659
+ // src/image-editor/CurvesEditor.tsx
2660
+ var import_react26 = require("react");
2661
+ var import_image_editor4 = require("@pixerate/editor/image-editor");
2662
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2663
+ var BOX = 256;
2664
+ var STROKE = { master: "#e2e8f0", red: "#ef4444", green: "#22c55e", blue: "#3b82f6" };
2665
+ var CurvesEditor = ({ points, onChange, histogram, channel = "master", size = 200 }) => {
2666
+ const svgRef = (0, import_react26.useRef)(null);
2667
+ const [drag, setDrag] = (0, import_react26.useState)(null);
2668
+ const toBox = (e) => {
2669
+ const rect = svgRef.current?.getBoundingClientRect();
2670
+ if (!rect || !rect.width || !rect.height) return { x: 0, y: 0 };
2671
+ return { x: (e.clientX - rect.left) / rect.width * BOX, y: (e.clientY - rect.top) / rect.height * BOX };
2672
+ };
2673
+ const onPointerDown = (e) => {
2674
+ const { x, y } = toBox(e);
2675
+ let index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
2676
+ if (index === -1) {
2677
+ const inserted = (0, import_image_editor4.insertCurvePoint)(points, (0, import_image_editor4.boxToCurvePoint)(x, y, BOX, BOX));
2678
+ onChange(inserted.points, true);
2679
+ index = inserted.index;
2680
+ } else {
2681
+ onChange(points, true);
2682
+ }
2683
+ setDrag(index);
2684
+ e.target.setPointerCapture?.(e.pointerId);
2685
+ };
2686
+ const onPointerMove = (e) => {
2687
+ if (drag === null) return;
2688
+ onChange((0, import_image_editor4.moveCurvePoint)(points, drag, (0, import_image_editor4.boxToCurvePoint)(toBox(e).x, toBox(e).y, BOX, BOX)), false);
2689
+ };
2690
+ const onDoubleClick = (e) => {
2691
+ const { x, y } = toBox(e);
2692
+ const index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
2693
+ if (index !== -1 && points.length > 2) onChange((0, import_image_editor4.removeCurvePoint)(points, index), true);
2694
+ };
2695
+ const histChannel = channel === "master" ? "luminance" : channel;
2696
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2697
+ "svg",
2698
+ {
2699
+ ref: svgRef,
2700
+ viewBox: `0 0 ${BOX} ${BOX}`,
2701
+ width: size,
2702
+ height: size,
2703
+ role: "application",
2704
+ "aria-label": `${channel} curve`,
2705
+ "data-testid": "curves-editor",
2706
+ className: "rounded bg-slate-950/70 touch-none select-none cursor-crosshair",
2707
+ onPointerDown,
2708
+ onPointerMove,
2709
+ onPointerUp: () => setDrag(null),
2710
+ onPointerCancel: () => setDrag(null),
2711
+ onDoubleClick,
2712
+ children: [
2713
+ histogram && histogram.samples > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: (0, import_image_editor4.histogramPath)(histogram, histChannel, BOX, BOX), fill: "rgba(148, 163, 184, 0.25)" }),
2714
+ [64, 128, 192].map((v) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { stroke: "rgba(148, 163, 184, 0.2)", strokeWidth: 1, children: [
2715
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("line", { x1: v, y1: 0, x2: v, y2: BOX }),
2716
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("line", { x1: 0, y1: v, x2: BOX, y2: v })
2717
+ ] }, v)),
2718
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("line", { x1: 0, y1: BOX, x2: BOX, y2: 0, stroke: "rgba(148, 163, 184, 0.35)", strokeDasharray: "4 4" }),
2719
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: (0, import_image_editor4.curveSvgPath)(points, BOX, BOX), fill: "none", stroke: STROKE[channel], strokeWidth: 2 }),
2720
+ points.map((p, i) => {
2721
+ const b = (0, import_image_editor4.curvePointToBox)(p, BOX, BOX);
2722
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2723
+ "circle",
2724
+ {
2725
+ cx: b.x,
2726
+ cy: b.y,
2727
+ r: 5,
2728
+ fill: drag === i ? STROKE[channel] : "#0f172a",
2729
+ stroke: STROKE[channel],
2730
+ strokeWidth: 2,
2731
+ "aria-label": `Point ${p.input} to ${p.output}`
2732
+ },
2733
+ i
2734
+ );
2735
+ })
2736
+ ]
2737
+ }
2738
+ );
2739
+ };
2740
+
2741
+ // src/image-editor/AdjustmentLayersPanel.tsx
2742
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2743
+ var CHANNELS = [
2744
+ { id: "master", label: "RGB" },
2745
+ { id: "red", label: "Red" },
2746
+ { id: "green", label: "Green" },
2747
+ { id: "blue", label: "Blue" }
2748
+ ];
2749
+ var TONES = [
2750
+ { id: "shadows", label: "Shadows" },
2751
+ { id: "midtones", label: "Midtones" },
2752
+ { id: "highlights", label: "Highlights" }
2753
+ ];
2754
+ var selectClass = "bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200";
2755
+ var chipClass = (on) => `px-2 py-0.5 rounded text-[10px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-400 hover:text-slate-200"}`;
2756
+ var HueRangeStrip = ({ bounds }) => {
2757
+ const pts = Array.from({ length: 73 }, (_, i) => `${i / 72 * 360},${20 - (0, import_image_editor5.hueRangeWeight)({ bounds }, i * 5) * 18}`).join(" ");
2758
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 360 20", preserveAspectRatio: "none", className: "w-full h-4 rounded", "data-testid": "hue-range-strip", children: [
2759
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("linearGradient", { id: "pixerate-hue-strip", children: [0, 60, 120, 180, 240, 300, 360].map((h) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("stop", { offset: `${h / 360 * 100}%`, stopColor: `hsl(${h}, 90%, 50%)` }, h)) }) }),
2760
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("rect", { width: 360, height: 20, fill: "url(#pixerate-hue-strip)", opacity: 0.6 }),
2761
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("polyline", { points: pts, fill: "none", stroke: "#fff", strokeWidth: 1.5 })
2762
+ ] });
2763
+ };
2764
+ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
2765
+ const follow = layersProp === void 0;
2766
+ const [, setTick] = (0, import_react27.useState)(0);
2767
+ (0, import_react27.useEffect)(() => follow ? controller.subscribe(() => setTick((n) => n + 1)) : void 0, [controller, follow]);
2768
+ const layers = layersProp ?? controller.getAdjustmentLayers();
2769
+ const [newKind, setNewKind] = (0, import_react27.useState)("curves");
2770
+ const [selectedId, setSelectedId] = (0, import_react27.useState)(null);
2771
+ const [channel, setChannel] = (0, import_react27.useState)("master");
2772
+ const [range, setRange] = (0, import_react27.useState)(-1);
2773
+ const [tone, setTone] = (0, import_react27.useState)("midtones");
2774
+ const selected = layers.find((l) => l.id === selectedId) ?? null;
2775
+ const topFirst = [...layers].reverse();
2776
+ const setParam = (layer, path, value, commit = true) => controller.updateAdjustmentLayer(layer.id, { params: (0, import_image_editor5.setControlPath)(layer.params, path, value) }, commit);
2777
+ const slider = (layer, spec, label) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2778
+ ControlSlider,
2779
+ {
2780
+ spec,
2781
+ label,
2782
+ value: Number((0, import_image_editor5.getControlPath)(layer.params, spec.id) ?? spec.default),
2783
+ onChange: (v) => setParam(layer, spec.id, v)
2784
+ },
2785
+ spec.id
2786
+ );
2787
+ const controlsOf = (layer, filter = () => true) => import_image_editor5.ADJUSTMENT_LAYER_CONTROLS[layer.kind].filter(filter);
2788
+ const checkbox = (layer, path, label) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
2789
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2790
+ "input",
2791
+ {
2792
+ type: "checkbox",
2793
+ checked: Boolean((0, import_image_editor5.getControlPath)(layer.params, path)),
2794
+ onChange: (e) => setParam(layer, path, e.target.checked),
2795
+ className: "accent-indigo-500"
2796
+ }
2797
+ ),
2798
+ label
2799
+ ] });
2800
+ const editor = (layer) => {
2801
+ switch (layer.kind) {
2802
+ case "curves":
2803
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
2804
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Curve channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
2805
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2806
+ CurvesEditor,
2807
+ {
2808
+ points: layer.params[channel],
2809
+ channel,
2810
+ histogram,
2811
+ onChange: (points, commit) => setParam(layer, channel, points, commit)
2812
+ }
2813
+ )
2814
+ ] });
2815
+ case "levels":
2816
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
2817
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Levels channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
2818
+ controlsOf(layer, (s) => s.id.startsWith(`${channel}.`)).map((s) => slider(layer, s))
2819
+ ] });
2820
+ case "hueSaturation":
2821
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
2822
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Hue range", children: [
2823
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(range === -1), onClick: () => setRange(-1), children: "Master" }),
2824
+ import_image_editor5.HUE_RANGE_LABELS.map((name, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(range === i), onClick: () => setRange(i), disabled: layer.params.colorize, children: name }, name))
2825
+ ] }),
2826
+ range >= 0 && !layer.params.colorize && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HueRangeStrip, { bounds: layer.params.ranges[range].bounds }),
2827
+ controlsOf(layer, (s) => range === -1 || layer.params.colorize ? !s.id.startsWith("ranges.") : s.id.startsWith(`ranges.${range}.`)).map(
2828
+ (s) => slider(layer, s)
2829
+ ),
2830
+ range === -1 && checkbox(layer, "colorize", "Colorize")
2831
+ ] });
2832
+ case "colorBalance":
2833
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
2834
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Tone", children: TONES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(tone === t.id), onClick: () => setTone(t.id), children: t.label }, t.id)) }),
2835
+ controlsOf(layer, (s) => s.id.startsWith(`${tone}.`)).map((s) => slider(layer, s)),
2836
+ checkbox(layer, "preserveLuminosity", "Preserve luminosity")
2837
+ ] });
2838
+ case "blackWhite":
2839
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
2840
+ controlsOf(layer).map((s) => slider(layer, s)),
2841
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
2842
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2843
+ "input",
2844
+ {
2845
+ type: "checkbox",
2846
+ checked: layer.params.tint !== null,
2847
+ onChange: (e) => setParam(layer, "tint", e.target.checked ? "#e1d3b3" : null),
2848
+ className: "accent-indigo-500"
2849
+ }
2850
+ ),
2851
+ "Tint",
2852
+ layer.params.tint && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2853
+ "input",
2854
+ {
2855
+ type: "color",
2856
+ "aria-label": "Tint colour",
2857
+ value: layer.params.tint,
2858
+ onChange: (e) => setParam(layer, "tint", e.target.value),
2859
+ className: "h-5 w-8 bg-transparent"
2860
+ }
2861
+ )
2862
+ ] })
2863
+ ] });
2864
+ case "photoFilter":
2865
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
2866
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
2867
+ "Filter colour",
2868
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2869
+ "input",
2870
+ {
2871
+ type: "color",
2872
+ "aria-label": "Filter colour",
2873
+ value: layer.params.color,
2874
+ onChange: (e) => setParam(layer, "color", e.target.value),
2875
+ className: "h-5 w-8 bg-transparent"
2876
+ }
2877
+ )
2878
+ ] }),
2879
+ controlsOf(layer).map((s) => slider(layer, s)),
2880
+ checkbox(layer, "preserveLuminosity", "Preserve luminosity")
2881
+ ] });
2882
+ case "invert":
2883
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-slate-500 text-[11px]", children: "Invert has no settings." });
2884
+ default:
2885
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex flex-col gap-2", children: controlsOf(layer).map((s) => slider(layer, s)) });
2886
+ }
2887
+ };
2888
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2 text-xs", "data-testid": "adjustment-layers-panel", children: [
2889
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Adjustment Layers" }) }),
2890
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1.5", children: [
2891
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2892
+ "select",
2893
+ {
2894
+ "aria-label": "New adjustment layer kind",
2895
+ className: `${selectClass} flex-1`,
2896
+ value: newKind,
2897
+ onChange: (e) => setNewKind(e.target.value),
2898
+ children: import_image_editor5.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: k.kind, children: k.label }, k.kind))
2899
+ }
2900
+ ),
2901
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2902
+ "button",
2903
+ {
2904
+ type: "button",
2905
+ className: "px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500",
2906
+ onClick: () => {
2907
+ setSelectedId(controller.addAdjustmentLayer(newKind));
2908
+ setChannel("master");
2909
+ setRange(-1);
2910
+ },
2911
+ children: "Add Layer"
2912
+ }
2913
+ )
2914
+ ] }),
2915
+ topFirst.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "flex flex-col gap-1", "aria-label": "Adjustment layers", children: topFirst.map((layer) => {
2916
+ const index = layers.indexOf(layer);
2917
+ const label = layer.name || (0, import_image_editor5.adjustmentKindLabel)(layer.kind);
2918
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2919
+ "li",
2920
+ {
2921
+ className: `flex items-center gap-1.5 rounded px-1.5 py-1 ${layer.id === selectedId ? "bg-indigo-600/20 ring-1 ring-indigo-500/50" : "bg-slate-800/60"}`,
2922
+ children: [
2923
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2924
+ "input",
2925
+ {
2926
+ type: "checkbox",
2927
+ "aria-label": `Show ${label}`,
2928
+ checked: layer.enabled,
2929
+ onChange: () => controller.toggleAdjustmentLayer(layer.id),
2930
+ className: "accent-indigo-500"
2931
+ }
2932
+ ),
2933
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2934
+ "button",
2935
+ {
2936
+ type: "button",
2937
+ className: `flex-1 text-left truncate ${layer.enabled ? "text-slate-200" : "text-slate-500 line-through"}`,
2938
+ onClick: () => setSelectedId(layer.id === selectedId ? null : layer.id),
2939
+ "aria-pressed": layer.id === selectedId,
2940
+ children: label
2941
+ }
2942
+ ),
2943
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2944
+ "button",
2945
+ {
2946
+ type: "button",
2947
+ "aria-label": `Move ${label} up`,
2948
+ disabled: index === layers.length - 1,
2949
+ onClick: () => controller.moveAdjustmentLayer(layer.id, index + 1),
2950
+ className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
2951
+ children: "\u2191"
2952
+ }
2953
+ ),
2954
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2955
+ "button",
2956
+ {
2957
+ type: "button",
2958
+ "aria-label": `Move ${label} down`,
2959
+ disabled: index === 0,
2960
+ onClick: () => controller.moveAdjustmentLayer(layer.id, index - 1),
2961
+ className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
2962
+ children: "\u2193"
2963
+ }
2964
+ ),
2965
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2966
+ "button",
2967
+ {
2968
+ type: "button",
2969
+ "aria-label": `Remove ${label}`,
2970
+ onClick: () => {
2971
+ controller.removeAdjustmentLayer(layer.id);
2972
+ if (selectedId === layer.id) setSelectedId(null);
2973
+ },
2974
+ className: "px-1 text-rose-400 hover:text-rose-300",
2975
+ children: "\u2715"
2976
+ }
2977
+ )
2978
+ ]
2979
+ },
2980
+ layer.id
2981
+ );
2982
+ }) }),
2983
+ selected && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2 rounded border border-slate-700/60 p-2", "data-testid": "adjustment-layer-editor", children: [
2984
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
2985
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-slate-400", children: "Blend" }),
2986
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2987
+ "select",
2988
+ {
2989
+ "aria-label": "Blend mode",
2990
+ className: `${selectClass} flex-1`,
2991
+ value: selected.blend,
2992
+ onChange: (e) => controller.updateAdjustmentLayer(selected.id, { blend: e.target.value }),
2993
+ children: import_image_editor5.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: m.id, children: m.label }, m.id))
2994
+ }
2995
+ )
2996
+ ] }),
2997
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2998
+ ControlSlider,
2999
+ {
3000
+ spec: import_image_editor5.ADJUSTMENT_LAYER_OPACITY_CONTROL,
3001
+ label: "Layer opacity",
3002
+ value: selected.opacity,
3003
+ onChange: (v) => controller.updateAdjustmentLayer(selected.id, { opacity: v })
3004
+ }
3005
+ ),
3006
+ editor(selected)
3007
+ ] })
3008
+ ] });
3009
+ };
3010
+
3011
+ // src/image-editor/ImageEditor.tsx
3012
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2250
3013
  var ImageEditor = ({
2251
3014
  editor: externalEditor,
2252
3015
  src,
@@ -2261,17 +3024,17 @@ var ImageEditor = ({
2261
3024
  const internalEditor = useImageEditor({ image: src });
2262
3025
  const editor = externalEditor || internalEditor;
2263
3026
  const { state, controller } = editor;
2264
- const legacyProcess = (0, import_image_editor2.normalizeProcessVersion)(state.adjustments.process) < import_image_editor2.LATEST_PROCESS_VERSION;
2265
- const canvasRef = (0, import_react23.useRef)(null);
2266
- const containerRef = (0, import_react23.useRef)(null);
2267
- const [activeTab, setActiveTab] = (0, import_react23.useState)("crop");
2268
- const [activeCropRatio, setActiveCropRatio] = (0, import_react23.useState)(null);
2269
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react23.useState)("");
2270
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react23.useState)("annotations");
2271
- const [isRemovingBg, setIsRemovingBg] = (0, import_react23.useState)(false);
2272
- const [isInpainting, setIsInpainting] = (0, import_react23.useState)(false);
2273
- const [aiError, setAiError] = (0, import_react23.useState)(null);
2274
- const [aiSuccess, setAiSuccess] = (0, import_react23.useState)(null);
3027
+ const legacyProcess = (0, import_image_editor6.normalizeProcessVersion)(state.adjustments.process) < import_image_editor6.LATEST_PROCESS_VERSION;
3028
+ const canvasRef = (0, import_react28.useRef)(null);
3029
+ const containerRef = (0, import_react28.useRef)(null);
3030
+ const [activeTab, setActiveTab] = (0, import_react28.useState)("crop");
3031
+ const [activeCropRatio, setActiveCropRatio] = (0, import_react28.useState)(null);
3032
+ const [inpaintPrompt, setInpaintPrompt] = (0, import_react28.useState)("");
3033
+ const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react28.useState)("annotations");
3034
+ const [isRemovingBg, setIsRemovingBg] = (0, import_react28.useState)(false);
3035
+ const [isInpainting, setIsInpainting] = (0, import_react28.useState)(false);
3036
+ const [aiError, setAiError] = (0, import_react28.useState)(null);
3037
+ const [aiSuccess, setAiSuccess] = (0, import_react28.useState)(null);
2275
3038
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
2276
3039
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
2277
3040
  const handleRemoveBackground = async () => {
@@ -2333,18 +3096,18 @@ var ImageEditor = ({
2333
3096
  setIsInpainting(false);
2334
3097
  }
2335
3098
  };
2336
- const [activeColor, setActiveColor] = (0, import_react23.useState)("#ef4444");
2337
- const [strokeWidth, setStrokeWidth] = (0, import_react23.useState)(3);
2338
- const [fontSize, setFontSize] = (0, import_react23.useState)(24);
2339
- const [textInput, setTextInput] = (0, import_react23.useState)("");
2340
- const draftAnnotationRef = (0, import_react23.useRef)(null);
2341
- const draftCropRef = (0, import_react23.useRef)(null);
2342
- const cropDragInfoRef = (0, import_react23.useRef)(null);
2343
- const annotationDragInfoRef = (0, import_react23.useRef)(null);
2344
- const isDrawingRef = (0, import_react23.useRef)(false);
2345
- const currentPathRef = (0, import_react23.useRef)([]);
2346
- const dragStartPointRef = (0, import_react23.useRef)(null);
2347
- const renderCanvas = (0, import_react23.useCallback)(
3099
+ const [activeColor, setActiveColor] = (0, import_react28.useState)("#ef4444");
3100
+ const [strokeWidth, setStrokeWidth] = (0, import_react28.useState)(3);
3101
+ const [fontSize, setFontSize] = (0, import_react28.useState)(24);
3102
+ const [textInput, setTextInput] = (0, import_react28.useState)("");
3103
+ const draftAnnotationRef = (0, import_react28.useRef)(null);
3104
+ const draftCropRef = (0, import_react28.useRef)(null);
3105
+ const cropDragInfoRef = (0, import_react28.useRef)(null);
3106
+ const annotationDragInfoRef = (0, import_react28.useRef)(null);
3107
+ const isDrawingRef = (0, import_react28.useRef)(false);
3108
+ const currentPathRef = (0, import_react28.useRef)([]);
3109
+ const dragStartPointRef = (0, import_react28.useRef)(null);
3110
+ const renderCanvas = (0, import_react28.useCallback)(
2348
3111
  async (draftAnnotation, draftCrop) => {
2349
3112
  const canvas = canvasRef.current;
2350
3113
  const container = containerRef.current;
@@ -2363,7 +3126,7 @@ var ImageEditor = ({
2363
3126
  },
2364
3127
  [controller, activeTab]
2365
3128
  );
2366
- (0, import_react23.useEffect)(() => {
3129
+ (0, import_react28.useEffect)(() => {
2367
3130
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
2368
3131
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
2369
3132
  const animId = raf(() => {
@@ -2371,12 +3134,12 @@ var ImageEditor = ({
2371
3134
  });
2372
3135
  return () => caf(animId);
2373
3136
  }, [renderCanvas, state]);
2374
- (0, import_react23.useEffect)(() => {
3137
+ (0, import_react28.useEffect)(() => {
2375
3138
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
2376
3139
  editor.setTool("crop");
2377
3140
  }
2378
3141
  }, [activeTab, editor]);
2379
- (0, import_react23.useEffect)(() => {
3142
+ (0, import_react28.useEffect)(() => {
2380
3143
  const container = containerRef.current;
2381
3144
  if (!container) return;
2382
3145
  if (typeof ResizeObserver !== "undefined") {
@@ -2413,8 +3176,8 @@ var ImageEditor = ({
2413
3176
  width: state.imageDimensions.width,
2414
3177
  height: state.imageDimensions.height
2415
3178
  };
2416
- const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
2417
- const startImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
3179
+ const handle = (0, import_image_editor6.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
3180
+ const startImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
2418
3181
  if (handle) {
2419
3182
  cropDragInfoRef.current = {
2420
3183
  handle,
@@ -2433,12 +3196,12 @@ var ImageEditor = ({
2433
3196
  return;
2434
3197
  }
2435
3198
  if (activeTab === "annotate") {
2436
- const imgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
3199
+ const imgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
2437
3200
  dragStartPointRef.current = imgPt;
2438
3201
  if (state.activeTool === "select") {
2439
3202
  for (let i = state.annotations.length - 1; i >= 0; i--) {
2440
3203
  const ann = state.annotations[i];
2441
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, ann)) {
3204
+ if ((0, import_image_editor6.hitTestAnnotation)(imgPt, ann)) {
2442
3205
  editor.selectAnnotation(ann.id);
2443
3206
  annotationDragInfoRef.current = {
2444
3207
  annId: ann.id,
@@ -2475,7 +3238,7 @@ var ImageEditor = ({
2475
3238
  if (activeTab === "crop") {
2476
3239
  const drag = cropDragInfoRef.current;
2477
3240
  if (drag) {
2478
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
3241
+ const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
2479
3242
  let newCrop;
2480
3243
  if (drag.handle === "new") {
2481
3244
  const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
@@ -2489,7 +3252,7 @@ var ImageEditor = ({
2489
3252
  height: Math.round(Math.max(20, maxY - minY))
2490
3253
  };
2491
3254
  } else {
2492
- newCrop = (0, import_image_editor2.calculateCropDrag)(
3255
+ newCrop = (0, import_image_editor6.calculateCropDrag)(
2493
3256
  drag.startCrop,
2494
3257
  drag.handle,
2495
3258
  drag.startImgPt,
@@ -2509,15 +3272,15 @@ var ImageEditor = ({
2509
3272
  width: state.imageDimensions.width,
2510
3273
  height: state.imageDimensions.height
2511
3274
  };
2512
- const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
2513
- canvas.style.cursor = (0, import_image_editor2.getCropCursor)(handle, state.transform);
3275
+ const handle = (0, import_image_editor6.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
3276
+ canvas.style.cursor = (0, import_image_editor6.getCropCursor)(handle, state.transform);
2514
3277
  }
2515
3278
  return;
2516
3279
  }
2517
3280
  if (activeTab === "annotate") {
2518
3281
  if (annotationDragInfoRef.current) {
2519
3282
  const drag = annotationDragInfoRef.current;
2520
- const currentImgPt2 = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
3283
+ const currentImgPt2 = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
2521
3284
  const dx = currentImgPt2.x - drag.startImgPt.x;
2522
3285
  const dy = currentImgPt2.y - drag.startImgPt.y;
2523
3286
  editor.updateAnnotation(drag.annId, {
@@ -2529,10 +3292,10 @@ var ImageEditor = ({
2529
3292
  const startPt = dragStartPointRef.current;
2530
3293
  if (!startPt) {
2531
3294
  if (state.activeTool === "select") {
2532
- const imgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
3295
+ const imgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
2533
3296
  let hovered = false;
2534
3297
  for (let i = state.annotations.length - 1; i >= 0; i--) {
2535
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, state.annotations[i])) {
3298
+ if ((0, import_image_editor6.hitTestAnnotation)(imgPt, state.annotations[i])) {
2536
3299
  hovered = true;
2537
3300
  break;
2538
3301
  }
@@ -2543,7 +3306,7 @@ var ImageEditor = ({
2543
3306
  }
2544
3307
  return;
2545
3308
  }
2546
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
3309
+ const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
2547
3310
  if (state.activeTool === "pen" && isDrawingRef.current) {
2548
3311
  currentPathRef.current.push(currentImgPt);
2549
3312
  const draft = {
@@ -2638,7 +3401,7 @@ var ImageEditor = ({
2638
3401
  }
2639
3402
  const startPt = dragStartPointRef.current;
2640
3403
  if (startPt) {
2641
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
3404
+ const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
2642
3405
  if (state.activeTool === "pen" && isDrawingRef.current) {
2643
3406
  if (currentPathRef.current.length > 1) {
2644
3407
  editor.addAnnotation({
@@ -2734,15 +3497,15 @@ var ImageEditor = ({
2734
3497
  link.click();
2735
3498
  }
2736
3499
  };
2737
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3500
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2738
3501
  "div",
2739
3502
  {
2740
3503
  className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
2741
3504
  style,
2742
3505
  children: [
2743
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
2744
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
2745
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3506
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
3507
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
3508
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2746
3509
  "button",
2747
3510
  {
2748
3511
  type: "button",
@@ -2754,7 +3517,7 @@ var ImageEditor = ({
2754
3517
  children: "Transform & Crop"
2755
3518
  }
2756
3519
  ),
2757
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3520
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2758
3521
  "button",
2759
3522
  {
2760
3523
  type: "button",
@@ -2766,7 +3529,7 @@ var ImageEditor = ({
2766
3529
  children: "Adjustments"
2767
3530
  }
2768
3531
  ),
2769
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3532
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2770
3533
  "button",
2771
3534
  {
2772
3535
  type: "button",
@@ -2778,7 +3541,7 @@ var ImageEditor = ({
2778
3541
  children: "Annotations"
2779
3542
  }
2780
3543
  ),
2781
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3544
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2782
3545
  "button",
2783
3546
  {
2784
3547
  type: "button",
@@ -2790,7 +3553,7 @@ var ImageEditor = ({
2790
3553
  children: "Depth Mask"
2791
3554
  }
2792
3555
  ),
2793
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3556
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2794
3557
  "button",
2795
3558
  {
2796
3559
  type: "button",
@@ -2800,14 +3563,14 @@ var ImageEditor = ({
2800
3563
  },
2801
3564
  className: `px-3 py-1.5 rounded-md font-medium transition-colors flex items-center gap-1.5 ${activeTab === "ai" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
2802
3565
  children: [
2803
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
3566
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
2804
3567
  "AI Tools"
2805
3568
  ]
2806
3569
  }
2807
3570
  )
2808
3571
  ] }),
2809
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
2810
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3572
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
3573
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2811
3574
  "button",
2812
3575
  {
2813
3576
  type: "button",
@@ -2818,7 +3581,7 @@ var ImageEditor = ({
2818
3581
  children: "\u21A9"
2819
3582
  }
2820
3583
  ),
2821
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3584
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2822
3585
  "button",
2823
3586
  {
2824
3587
  type: "button",
@@ -2829,7 +3592,7 @@ var ImageEditor = ({
2829
3592
  children: "\u21AA"
2830
3593
  }
2831
3594
  ),
2832
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3595
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2833
3596
  "button",
2834
3597
  {
2835
3598
  type: "button",
@@ -2838,8 +3601,8 @@ var ImageEditor = ({
2838
3601
  children: "Reset"
2839
3602
  }
2840
3603
  ),
2841
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
2842
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3604
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
3605
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2843
3606
  "button",
2844
3607
  {
2845
3608
  type: "button",
@@ -2849,11 +3612,11 @@ var ImageEditor = ({
2849
3612
  children: "-"
2850
3613
  }
2851
3614
  ),
2852
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
3615
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
2853
3616
  Math.round(state.zoom * 100),
2854
3617
  "%"
2855
3618
  ] }),
2856
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3619
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2857
3620
  "button",
2858
3621
  {
2859
3622
  type: "button",
@@ -2863,8 +3626,8 @@ var ImageEditor = ({
2863
3626
  children: "+"
2864
3627
  }
2865
3628
  ),
2866
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
2867
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3629
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
3630
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2868
3631
  "button",
2869
3632
  {
2870
3633
  type: "button",
@@ -2875,7 +3638,7 @@ var ImageEditor = ({
2875
3638
  children: "Hold Compare"
2876
3639
  }
2877
3640
  ),
2878
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3641
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2879
3642
  "button",
2880
3643
  {
2881
3644
  type: "button",
@@ -2886,13 +3649,13 @@ var ImageEditor = ({
2886
3649
  )
2887
3650
  ] })
2888
3651
  ] }),
2889
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
2890
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3652
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
3653
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2891
3654
  "div",
2892
3655
  {
2893
3656
  ref: containerRef,
2894
3657
  className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
2895
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3658
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2896
3659
  "canvas",
2897
3660
  {
2898
3661
  ref: canvasRef,
@@ -2905,11 +3668,11 @@ var ImageEditor = ({
2905
3668
  )
2906
3669
  }
2907
3670
  ),
2908
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
2909
- activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
2910
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
2911
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
2912
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3671
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
3672
+ activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
3673
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
3674
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
3675
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2913
3676
  "button",
2914
3677
  {
2915
3678
  type: "button",
@@ -2921,7 +3684,7 @@ var ImageEditor = ({
2921
3684
  children: "Freeform"
2922
3685
  }
2923
3686
  ),
2924
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3687
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2925
3688
  "button",
2926
3689
  {
2927
3690
  type: "button",
@@ -2933,7 +3696,7 @@ var ImageEditor = ({
2933
3696
  children: "1:1 Square"
2934
3697
  }
2935
3698
  ),
2936
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3699
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2937
3700
  "button",
2938
3701
  {
2939
3702
  type: "button",
@@ -2945,7 +3708,7 @@ var ImageEditor = ({
2945
3708
  children: "4:3 Standard"
2946
3709
  }
2947
3710
  ),
2948
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3711
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2949
3712
  "button",
2950
3713
  {
2951
3714
  type: "button",
@@ -2957,7 +3720,7 @@ var ImageEditor = ({
2957
3720
  children: "16:9 Wide"
2958
3721
  }
2959
3722
  ),
2960
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3723
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2961
3724
  "button",
2962
3725
  {
2963
3726
  type: "button",
@@ -2969,7 +3732,7 @@ var ImageEditor = ({
2969
3732
  children: "3:2 Photo"
2970
3733
  }
2971
3734
  ),
2972
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3735
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2973
3736
  "button",
2974
3737
  {
2975
3738
  type: "button",
@@ -2982,10 +3745,10 @@ var ImageEditor = ({
2982
3745
  }
2983
3746
  )
2984
3747
  ] }),
2985
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
2986
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
2987
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
2988
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3748
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3749
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
3750
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
3751
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2989
3752
  "button",
2990
3753
  {
2991
3754
  type: "button",
@@ -2994,7 +3757,7 @@ var ImageEditor = ({
2994
3757
  children: "Rotate -90\xB0"
2995
3758
  }
2996
3759
  ),
2997
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3760
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2998
3761
  "button",
2999
3762
  {
3000
3763
  type: "button",
@@ -3003,7 +3766,7 @@ var ImageEditor = ({
3003
3766
  children: "Rotate +90\xB0"
3004
3767
  }
3005
3768
  ),
3006
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3769
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3007
3770
  "button",
3008
3771
  {
3009
3772
  type: "button",
@@ -3012,7 +3775,7 @@ var ImageEditor = ({
3012
3775
  children: "Flip Horizontal"
3013
3776
  }
3014
3777
  ),
3015
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3778
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3016
3779
  "button",
3017
3780
  {
3018
3781
  type: "button",
@@ -3023,10 +3786,10 @@ var ImageEditor = ({
3023
3786
  )
3024
3787
  ] })
3025
3788
  ] }),
3026
- activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3027
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3028
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
3029
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3789
+ activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3790
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
3791
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
3792
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3030
3793
  "button",
3031
3794
  {
3032
3795
  type: "button",
@@ -3036,174 +3799,49 @@ var ImageEditor = ({
3036
3799
  }
3037
3800
  )
3038
3801
  ] }),
3039
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3040
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3041
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Brightness" }),
3042
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.brightness })
3043
- ] }),
3044
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3045
- "input",
3046
- {
3047
- type: "range",
3048
- min: "-100",
3049
- max: "100",
3050
- value: state.adjustments.brightness,
3051
- onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
3052
- className: "w-full accent-indigo-500"
3053
- }
3054
- )
3055
- ] }),
3056
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3057
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3058
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Contrast" }),
3059
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
3060
- ] }),
3061
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3062
- "input",
3802
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Histogram, { histogram: editor.histogram }),
3803
+ import_image_editor6.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react28.default.Fragment, { children: [
3804
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3805
+ ControlSlider,
3063
3806
  {
3064
- type: "range",
3065
- min: "-100",
3066
- max: "100",
3067
- value: state.adjustments.contrast,
3068
- onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
3069
- className: "w-full accent-indigo-500"
3807
+ spec,
3808
+ value: (0, import_image_editor6.readAdjustmentControls)(state.adjustments)[spec.id],
3809
+ disabled: spec.requiresProcess !== void 0 && legacyProcess,
3810
+ onChange: (v) => editor.setAdjustments({ [spec.id]: v })
3070
3811
  }
3071
- )
3072
- ] }),
3073
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3074
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3075
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Saturation" }),
3076
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
3077
- ] }),
3078
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3079
- "input",
3080
- {
3081
- type: "range",
3082
- min: "-100",
3083
- max: "100",
3084
- value: state.adjustments.saturation,
3085
- onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
3086
- className: "w-full accent-indigo-500"
3087
- }
3088
- )
3089
- ] }),
3090
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3091
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3092
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Exposure" }),
3093
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
3094
- ] }),
3095
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3096
- "input",
3097
- {
3098
- type: "range",
3099
- min: "-100",
3100
- max: "100",
3101
- value: state.adjustments.exposure,
3102
- onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
3103
- className: "w-full accent-indigo-500"
3104
- }
3105
- )
3106
- ] }),
3107
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3108
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3109
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Warmth / Temperature" }),
3110
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
3111
- ] }),
3112
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3113
- "input",
3114
- {
3115
- type: "range",
3116
- min: "-100",
3117
- max: "100",
3118
- value: state.adjustments.temperature,
3119
- onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
3120
- className: "w-full accent-indigo-500"
3121
- }
3122
- )
3123
- ] }),
3124
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3125
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3126
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Tint" }),
3127
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.tint ?? 0 })
3128
- ] }),
3129
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3130
- "input",
3812
+ ),
3813
+ spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3814
+ "div",
3131
3815
  {
3132
- type: "range",
3133
- min: "-100",
3134
- max: "100",
3135
- "aria-label": "Tint",
3136
- disabled: legacyProcess,
3137
- value: state.adjustments.tint ?? 0,
3138
- onChange: (e) => editor.setAdjustments({ tint: Number(e.target.value) }),
3139
- className: "w-full accent-indigo-500 disabled:opacity-40"
3816
+ className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
3817
+ "data-testid": "image-editor-process-notice",
3818
+ children: [
3819
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
3820
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3821
+ "button",
3822
+ {
3823
+ type: "button",
3824
+ onClick: () => editor.upgradeProcess(),
3825
+ className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
3826
+ children: "Update"
3827
+ }
3828
+ )
3829
+ ]
3140
3830
  }
3141
3831
  )
3142
- ] }),
3143
- legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3144
- "div",
3832
+ ] }, spec.id)),
3833
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3834
+ AdjustmentLayersPanel,
3145
3835
  {
3146
- className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
3147
- "data-testid": "image-editor-process-notice",
3148
- children: [
3149
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
3150
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3151
- "button",
3152
- {
3153
- type: "button",
3154
- onClick: () => editor.upgradeProcess(),
3155
- className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
3156
- children: "Update"
3157
- }
3158
- )
3159
- ]
3836
+ controller,
3837
+ layers: state.adjustments.layers,
3838
+ histogram: editor.histogram
3160
3839
  }
3161
- ),
3162
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3163
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3164
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Blur" }),
3165
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
3166
- state.adjustments.blur,
3167
- "px"
3168
- ] })
3169
- ] }),
3170
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3171
- "input",
3172
- {
3173
- type: "range",
3174
- min: "0",
3175
- max: "30",
3176
- value: state.adjustments.blur,
3177
- onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
3178
- className: "w-full accent-indigo-500"
3179
- }
3180
- )
3181
- ] }),
3182
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3183
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3184
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Opacity" }),
3185
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
3186
- Math.round(state.adjustments.opacity * 100),
3187
- "%"
3188
- ] })
3189
- ] }),
3190
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3191
- "input",
3192
- {
3193
- type: "range",
3194
- min: "0",
3195
- max: "1",
3196
- step: "0.05",
3197
- value: state.adjustments.opacity,
3198
- onChange: (e) => editor.setAdjustments({ opacity: Number(e.target.value) }),
3199
- className: "w-full accent-indigo-500"
3200
- }
3201
- )
3202
- ] })
3840
+ ) })
3203
3841
  ] }),
3204
- activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3205
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
3206
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3842
+ activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3843
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
3844
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3207
3845
  "button",
3208
3846
  {
3209
3847
  type: "button",
@@ -3213,9 +3851,9 @@ var ImageEditor = ({
3213
3851
  },
3214
3852
  t
3215
3853
  )) }),
3216
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
3217
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-slate-400", children: "Color:" }),
3218
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3854
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
3855
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { className: "text-slate-400", children: "Color:" }),
3856
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3219
3857
  "input",
3220
3858
  {
3221
3859
  type: "color",
@@ -3224,15 +3862,15 @@ var ImageEditor = ({
3224
3862
  className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
3225
3863
  }
3226
3864
  ),
3227
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex-1 ml-2", children: [
3228
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
3229
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Width:" }),
3230
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
3865
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex-1 ml-2", children: [
3866
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
3867
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Width:" }),
3868
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-mono", children: [
3231
3869
  strokeWidth,
3232
3870
  "px"
3233
3871
  ] })
3234
3872
  ] }),
3235
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3873
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3236
3874
  "input",
3237
3875
  {
3238
3876
  type: "range",
@@ -3245,10 +3883,10 @@ var ImageEditor = ({
3245
3883
  )
3246
3884
  ] })
3247
3885
  ] }),
3248
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3249
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
3250
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2", children: [
3251
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3886
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3887
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
3888
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-2", children: [
3889
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3252
3890
  "input",
3253
3891
  {
3254
3892
  type: "text",
@@ -3259,7 +3897,7 @@ var ImageEditor = ({
3259
3897
  className: "flex-1 bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-indigo-500"
3260
3898
  }
3261
3899
  ),
3262
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3900
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3263
3901
  "button",
3264
3902
  {
3265
3903
  type: "button",
@@ -3269,14 +3907,14 @@ var ImageEditor = ({
3269
3907
  }
3270
3908
  )
3271
3909
  ] }),
3272
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
3273
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Font Size:" }),
3274
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
3910
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
3911
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Font Size:" }),
3912
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-mono", children: [
3275
3913
  fontSize,
3276
3914
  "px"
3277
3915
  ] })
3278
3916
  ] }),
3279
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3917
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3280
3918
  "input",
3281
3919
  {
3282
3920
  type: "range",
@@ -3287,14 +3925,14 @@ var ImageEditor = ({
3287
3925
  className: "w-full accent-indigo-500"
3288
3926
  }
3289
3927
  ),
3290
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3291
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3292
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-slate-400", children: [
3928
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3929
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
3930
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "text-slate-400", children: [
3293
3931
  "Layers (",
3294
3932
  state.annotations.length,
3295
3933
  ")"
3296
3934
  ] }),
3297
- state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3935
+ state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3298
3936
  "button",
3299
3937
  {
3300
3938
  type: "button",
@@ -3303,7 +3941,7 @@ var ImageEditor = ({
3303
3941
  children: "Delete Selected"
3304
3942
  }
3305
3943
  ),
3306
- state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3944
+ state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3307
3945
  "button",
3308
3946
  {
3309
3947
  type: "button",
@@ -3314,10 +3952,10 @@ var ImageEditor = ({
3314
3952
  )
3315
3953
  ] })
3316
3954
  ] }),
3317
- activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3318
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3319
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
3320
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3955
+ activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3956
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
3957
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
3958
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3321
3959
  "button",
3322
3960
  {
3323
3961
  type: "button",
@@ -3327,8 +3965,8 @@ var ImageEditor = ({
3327
3965
  }
3328
3966
  )
3329
3967
  ] }),
3330
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
3331
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3968
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
3969
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3332
3970
  "input",
3333
3971
  {
3334
3972
  type: "checkbox",
@@ -3337,12 +3975,12 @@ var ImageEditor = ({
3337
3975
  className: "rounded accent-indigo-500"
3338
3976
  }
3339
3977
  ),
3340
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Enable Depth / Alpha Mask" })
3978
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Enable Depth / Alpha Mask" })
3341
3979
  ] }),
3342
- state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3343
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3344
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
3345
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3980
+ state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
3981
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
3982
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
3983
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3346
3984
  "input",
3347
3985
  {
3348
3986
  type: "text",
@@ -3353,8 +3991,8 @@ var ImageEditor = ({
3353
3991
  }
3354
3992
  )
3355
3993
  ] }),
3356
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2", children: [
3357
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3994
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-2", children: [
3995
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3358
3996
  "button",
3359
3997
  {
3360
3998
  type: "button",
@@ -3363,7 +4001,7 @@ var ImageEditor = ({
3363
4001
  children: "Threshold Mode"
3364
4002
  }
3365
4003
  ),
3366
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4004
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3367
4005
  "button",
3368
4006
  {
3369
4007
  type: "button",
@@ -3373,12 +4011,12 @@ var ImageEditor = ({
3373
4011
  }
3374
4012
  )
3375
4013
  ] }),
3376
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3377
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3378
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Depth Start (Lower):" }),
3379
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
4014
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
4015
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
4016
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Depth Start (Lower):" }),
4017
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
3380
4018
  ] }),
3381
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4019
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3382
4020
  "input",
3383
4021
  {
3384
4022
  type: "range",
@@ -3392,12 +4030,12 @@ var ImageEditor = ({
3392
4030
  }
3393
4031
  )
3394
4032
  ] }),
3395
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3396
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3397
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Depth Stop (Upper):" }),
3398
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
4033
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
4034
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
4035
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Depth Stop (Upper):" }),
4036
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
3399
4037
  ] }),
3400
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4038
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3401
4039
  "input",
3402
4040
  {
3403
4041
  type: "range",
@@ -3411,12 +4049,12 @@ var ImageEditor = ({
3411
4049
  }
3412
4050
  )
3413
4051
  ] }),
3414
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3415
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3416
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Softness:" }),
3417
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
4052
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
4053
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
4054
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Softness:" }),
4055
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
3418
4056
  ] }),
3419
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4057
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3420
4058
  "input",
3421
4059
  {
3422
4060
  type: "range",
@@ -3428,8 +4066,8 @@ var ImageEditor = ({
3428
4066
  }
3429
4067
  )
3430
4068
  ] }),
3431
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
3432
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4069
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
4070
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3433
4071
  "input",
3434
4072
  {
3435
4073
  type: "checkbox",
@@ -3438,52 +4076,52 @@ var ImageEditor = ({
3438
4076
  className: "rounded accent-indigo-500"
3439
4077
  }
3440
4078
  ),
3441
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Invert Mask" })
4079
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Invert Mask" })
3442
4080
  ] })
3443
4081
  ] })
3444
4082
  ] }),
3445
- activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
3446
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
3447
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3448
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
3449
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
4083
+ activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
4084
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
4085
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
4086
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
4087
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
3450
4088
  "Background Removal"
3451
4089
  ] }),
3452
- isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
4090
+ isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
3453
4091
  ] }),
3454
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
3455
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4092
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
4093
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3456
4094
  "button",
3457
4095
  {
3458
4096
  type: "button",
3459
4097
  onClick: handleRemoveBackground,
3460
4098
  disabled: isRemovingBg || isInpainting,
3461
4099
  className: "mt-1 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
3462
- children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3463
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
4100
+ children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
4101
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
3464
4102
  "Removing Background..."
3465
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3466
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
4103
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
4104
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
3467
4105
  "Remove Background"
3468
4106
  ] })
3469
4107
  }
3470
4108
  )
3471
4109
  ] }),
3472
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
3473
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3474
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
3475
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
3476
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
3477
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m14 7 3 3" })
4110
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
4111
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
4112
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
4113
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
4114
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
4115
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m14 7 3 3" })
3478
4116
  ] }),
3479
4117
  "Generative Inpainting"
3480
4118
  ] }),
3481
- isInpainting && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
4119
+ isInpainting && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
3482
4120
  ] }),
3483
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
3484
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
3485
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
3486
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4121
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
4122
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
4123
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
4124
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3487
4125
  "textarea",
3488
4126
  {
3489
4127
  id: "ai-inpaint-prompt-react",
@@ -3495,10 +4133,10 @@ var ImageEditor = ({
3495
4133
  }
3496
4134
  )
3497
4135
  ] }),
3498
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
3499
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
3500
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
3501
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4136
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
4137
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
4138
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
4139
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3502
4140
  "button",
3503
4141
  {
3504
4142
  type: "button",
@@ -3511,7 +4149,7 @@ var ImageEditor = ({
3511
4149
  ]
3512
4150
  }
3513
4151
  ),
3514
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4152
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3515
4153
  "button",
3516
4154
  {
3517
4155
  type: "button",
@@ -3526,25 +4164,25 @@ var ImageEditor = ({
3526
4164
  )
3527
4165
  ] })
3528
4166
  ] }),
3529
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4167
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3530
4168
  "button",
3531
4169
  {
3532
4170
  type: "button",
3533
4171
  onClick: handleInpaint,
3534
4172
  disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
3535
4173
  className: "mt-1 px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
3536
- children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3537
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
4174
+ children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
4175
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
3538
4176
  "Generating Inpaint..."
3539
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3540
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
4177
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
4178
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
3541
4179
  "Apply Inpainting"
3542
4180
  ] })
3543
4181
  }
3544
4182
  )
3545
4183
  ] }),
3546
- aiError && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
3547
- aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
4184
+ aiError && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
4185
+ aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
3548
4186
  ] })
3549
4187
  ] })
3550
4188
  ] })
@@ -3554,8 +4192,8 @@ var ImageEditor = ({
3554
4192
  };
3555
4193
 
3556
4194
  // src/review/useReview.ts
3557
- var import_react24 = require("react");
3558
- var import_editor10 = require("@pixerate/editor");
4195
+ var import_react29 = require("react");
4196
+ var import_editor11 = require("@pixerate/editor");
3559
4197
  var EMPTY_COMMENTS = Object.freeze({
3560
4198
  threads: [],
3561
4199
  activeThreadId: null
@@ -3572,24 +4210,24 @@ function isCommentStore(value) {
3572
4210
  return !!value && typeof value.getSnapshot === "function" && typeof value.createThread === "function";
3573
4211
  }
3574
4212
  function useComments(source) {
3575
- const store = isCommentStore(source) ? source : (0, import_editor10.getCommentStore)(source);
3576
- const subscribe = (0, import_react24.useCallback)(
4213
+ const store = isCommentStore(source) ? source : (0, import_editor11.getCommentStore)(source);
4214
+ const subscribe = (0, import_react29.useCallback)(
3577
4215
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3578
4216
  [store]
3579
4217
  );
3580
4218
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
3581
- const snapshot = (0, import_react24.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3582
- return (0, import_react24.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
4219
+ const snapshot = (0, import_react29.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
4220
+ return (0, import_react29.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
3583
4221
  }
3584
4222
  function useTrackChanges(editor) {
3585
- const store = (0, import_editor10.getTrackChangesStore)(editor ?? null);
3586
- const subscribe = (0, import_react24.useCallback)(
4223
+ const store = (0, import_editor11.getTrackChangesStore)(editor ?? null);
4224
+ const subscribe = (0, import_react29.useCallback)(
3587
4225
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3588
4226
  [store]
3589
4227
  );
3590
4228
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
3591
- const snapshot = (0, import_react24.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3592
- return (0, import_react24.useMemo)(() => {
4229
+ const snapshot = (0, import_react29.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
4230
+ return (0, import_react29.useMemo)(() => {
3593
4231
  const run = (fn) => () => {
3594
4232
  if (editor && !editor.isDestroyed) fn(editor);
3595
4233
  };
@@ -3609,9 +4247,9 @@ function useTrackChanges(editor) {
3609
4247
  }
3610
4248
 
3611
4249
  // src/review/CommentsPanel.tsx
3612
- var import_react25 = require("react");
3613
- var import_editor11 = require("@pixerate/editor");
3614
- var import_jsx_runtime11 = require("react/jsx-runtime");
4250
+ var import_react30 = require("react");
4251
+ var import_editor12 = require("@pixerate/editor");
4252
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3615
4253
  var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
3616
4254
  var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
3617
4255
  var textareaClass = "w-full resize-y rounded-md border border-input bg-background p-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary";
@@ -3621,22 +4259,22 @@ var CommentsPanel = ({
3621
4259
  author: authorProp,
3622
4260
  showResolved: showResolvedInitial = false,
3623
4261
  className = "flex flex-col gap-2 text-sm text-foreground",
3624
- formatTime = (t) => (0, import_editor11.formatReviewTime)(t),
4262
+ formatTime = (t) => (0, import_editor12.formatReviewTime)(t),
3625
4263
  emptyMessage = "No comments yet. Select text and add a comment."
3626
4264
  }) => {
3627
4265
  const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
3628
- const [showResolved, setShowResolved] = (0, import_react25.useState)(showResolvedInitial);
3629
- const author = authorProp ?? (0, import_editor11.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
4266
+ const [showResolved, setShowResolved] = (0, import_react30.useState)(showResolvedInitial);
4267
+ const author = authorProp ?? (0, import_editor12.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
3630
4268
  const visible = threads.filter((t) => showResolved || !t.resolved);
3631
4269
  const resolvedCount = threads.filter((t) => t.resolved).length;
3632
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("aside", { className, "aria-label": "Comments", children: [
3633
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
3634
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
4270
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("aside", { className, "aria-label": "Comments", children: [
4271
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
4272
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
3635
4273
  threads.length - resolvedCount,
3636
4274
  " open",
3637
4275
  resolvedCount > 0 ? ` \xB7 ${resolvedCount} resolved` : ""
3638
4276
  ] }),
3639
- resolvedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4277
+ resolvedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3640
4278
  "button",
3641
4279
  {
3642
4280
  type: "button",
@@ -3647,7 +4285,7 @@ var CommentsPanel = ({
3647
4285
  }
3648
4286
  )
3649
4287
  ] }),
3650
- visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4288
+ visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3651
4289
  CommentThreadCard,
3652
4290
  {
3653
4291
  thread,
@@ -3669,7 +4307,7 @@ var CommentThreadCard = ({
3669
4307
  author,
3670
4308
  formatTime
3671
4309
  }) => {
3672
- const [draft, setDraft] = (0, import_react25.useState)("");
4310
+ const [draft, setDraft] = (0, import_react30.useState)("");
3673
4311
  const isDraft = thread.body === "";
3674
4312
  const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
3675
4313
  const live = editor && !editor.isDestroyed ? editor : null;
@@ -3695,7 +4333,7 @@ var CommentThreadCard = ({
3695
4333
  setDraft("");
3696
4334
  };
3697
4335
  const stop = (e) => e.stopPropagation();
3698
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4336
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3699
4337
  "li",
3700
4338
  {
3701
4339
  className: `flex flex-col gap-2 rounded-md border bg-background p-3 cursor-pointer ${active ? "border-primary ring-1 ring-primary" : "border-border"} ${thread.resolved ? "opacity-70" : ""}`,
@@ -3711,24 +4349,24 @@ var CommentThreadCard = ({
3711
4349
  }
3712
4350
  },
3713
4351
  children: [
3714
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
3715
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-medium", children: thread.author.name }),
3716
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
4352
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
4353
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: thread.author.name }),
4354
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
3717
4355
  ] }),
3718
- quote && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
3719
- thread.orphaned && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
3720
- thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
3721
- !isDraft && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
3722
- thread.replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
3723
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-xs", children: [
3724
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-medium", children: reply.author.name }),
4356
+ quote && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
4357
+ thread.orphaned && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
4358
+ thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
4359
+ !isDraft && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
4360
+ thread.replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
4361
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-xs", children: [
4362
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: reply.author.name }),
3725
4363
  " ",
3726
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
4364
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
3727
4365
  ] }),
3728
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
4366
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
3729
4367
  ] }, reply.id)) }),
3730
- (active || isDraft) && !thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-1", onClick: stop, children: [
3731
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4368
+ (active || isDraft) && !thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-1", onClick: stop, children: [
4369
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3732
4370
  "textarea",
3733
4371
  {
3734
4372
  className: textareaClass,
@@ -3746,9 +4384,9 @@ var CommentThreadCard = ({
3746
4384
  }
3747
4385
  }
3748
4386
  ),
3749
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-end gap-1", children: [
3750
- isDraft && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
3751
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4387
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex justify-end gap-1", children: [
4388
+ isDraft && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
4389
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3752
4390
  "button",
3753
4391
  {
3754
4392
  type: "button",
@@ -3760,9 +4398,9 @@ var CommentThreadCard = ({
3760
4398
  )
3761
4399
  ] })
3762
4400
  ] }),
3763
- !isDraft && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-end gap-1", onClick: stop, children: [
3764
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
3765
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4401
+ !isDraft && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex justify-end gap-1", onClick: stop, children: [
4402
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
4403
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3766
4404
  "button",
3767
4405
  {
3768
4406
  type: "button",
@@ -3778,7 +4416,7 @@ var CommentThreadCard = ({
3778
4416
  };
3779
4417
 
3780
4418
  // src/review/ReviewToolbar.tsx
3781
- var import_jsx_runtime12 = require("react/jsx-runtime");
4419
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3782
4420
  var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
3783
4421
  var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
3784
4422
  var ReviewToolbar = ({
@@ -3791,8 +4429,8 @@ var ReviewToolbar = ({
3791
4429
  const count = review.changes.length;
3792
4430
  const current = review.currentChange;
3793
4431
  const index = current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0;
3794
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className, role: "toolbar", "aria-label": "Review", children: [
3795
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4432
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className, role: "toolbar", "aria-label": "Review", children: [
4433
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3796
4434
  "button",
3797
4435
  {
3798
4436
  type: "button",
@@ -3803,8 +4441,8 @@ var ReviewToolbar = ({
3803
4441
  children: review.enabled ? "Tracking on" : "Track changes"
3804
4442
  }
3805
4443
  ),
3806
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
3807
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4444
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
4445
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3808
4446
  "button",
3809
4447
  {
3810
4448
  type: "button",
@@ -3815,8 +4453,8 @@ var ReviewToolbar = ({
3815
4453
  children: "\u2039"
3816
4454
  }
3817
4455
  ),
3818
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
3819
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4456
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
4457
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3820
4458
  "button",
3821
4459
  {
3822
4460
  type: "button",
@@ -3827,7 +4465,7 @@ var ReviewToolbar = ({
3827
4465
  children: "\u203A"
3828
4466
  }
3829
4467
  ),
3830
- showDescription && current && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4468
+ showDescription && current && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3831
4469
  "span",
3832
4470
  {
3833
4471
  className: descriptionClassName,
@@ -3839,8 +4477,8 @@ var ReviewToolbar = ({
3839
4477
  ]
3840
4478
  }
3841
4479
  ),
3842
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
3843
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4480
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
4481
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3844
4482
  "button",
3845
4483
  {
3846
4484
  type: "button",
@@ -3850,7 +4488,7 @@ var ReviewToolbar = ({
3850
4488
  children: "Accept"
3851
4489
  }
3852
4490
  ),
3853
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4491
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3854
4492
  "button",
3855
4493
  {
3856
4494
  type: "button",
@@ -3860,18 +4498,18 @@ var ReviewToolbar = ({
3860
4498
  children: "Reject"
3861
4499
  }
3862
4500
  ),
3863
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
3864
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
4501
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
4502
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
3865
4503
  ] });
3866
4504
  };
3867
4505
 
3868
4506
  // src/review/CommentButton.tsx
3869
- var import_jsx_runtime13 = require("react/jsx-runtime");
4507
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3870
4508
  var CommentButton = ({
3871
4509
  editor,
3872
4510
  className = "rounded px-2 py-1 text-xs text-popover-foreground hover:bg-muted",
3873
4511
  children = "Comment"
3874
- }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4512
+ }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3875
4513
  "button",
3876
4514
  {
3877
4515
  type: "button",
@@ -3882,13 +4520,36 @@ var CommentButton = ({
3882
4520
  }
3883
4521
  );
3884
4522
 
4523
+ // src/review/useDocumentCompare.ts
4524
+ var import_react31 = require("react");
4525
+ var import_editor13 = require("@pixerate/editor");
4526
+ function useDocumentCompare(editor, options) {
4527
+ const [summary, setSummary] = (0, import_react31.useState)(null);
4528
+ const defaults = (0, import_react31.useRef)(options);
4529
+ defaults.current = options;
4530
+ const compare = (0, import_react31.useCallback)(
4531
+ (original, revised, overrides) => {
4532
+ if (!editor || editor.isDestroyed) return null;
4533
+ const result = (0, import_editor13.applyDocumentComparison)(editor, original, revised, {
4534
+ ...defaults.current,
4535
+ ...overrides
4536
+ });
4537
+ setSummary(result.summary);
4538
+ return result.summary;
4539
+ },
4540
+ [editor]
4541
+ );
4542
+ const clear = (0, import_react31.useCallback)(() => setSummary(null), []);
4543
+ return (0, import_react31.useMemo)(() => ({ summary, compare, clear }), [summary, compare, clear]);
4544
+ }
4545
+
3885
4546
  // src/commands/useCommandPalette.ts
3886
- var import_react26 = require("react");
4547
+ var import_react32 = require("react");
3887
4548
  var import_commands2 = require("@pixerate/editor/commands");
3888
4549
  function useCommandPalette(registry, options = {}) {
3889
- const optionsRef = (0, import_react26.useRef)(options);
4550
+ const optionsRef = (0, import_react32.useRef)(options);
3890
4551
  optionsRef.current = options;
3891
- const controller = (0, import_react26.useMemo)(
4552
+ const controller = (0, import_react32.useMemo)(
3892
4553
  () => (0, import_commands2.createCommandPaletteController)(registry, {
3893
4554
  context: () => {
3894
4555
  const c = optionsRef.current.context;
@@ -3909,9 +4570,9 @@ function useCommandPalette(registry, options = {}) {
3909
4570
  // eslint-disable-next-line react-hooks/exhaustive-deps
3910
4571
  [registry]
3911
4572
  );
3912
- const state = (0, import_react26.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
4573
+ const state = (0, import_react32.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
3913
4574
  const { hotkey, hotkeyTarget } = options;
3914
- (0, import_react26.useEffect)(() => {
4575
+ (0, import_react32.useEffect)(() => {
3915
4576
  if (!hotkey) return;
3916
4577
  const target = hotkeyTarget ?? (typeof window !== "undefined" ? window : null);
3917
4578
  if (!target) return;
@@ -3939,13 +4600,13 @@ function useCommandPalette(registry, options = {}) {
3939
4600
  }
3940
4601
 
3941
4602
  // src/commands/CommandPalette.tsx
3942
- var import_react27 = __toESM(require("react"), 1);
3943
- var import_jsx_runtime14 = require("react/jsx-runtime");
4603
+ var import_react33 = __toESM(require("react"), 1);
4604
+ var import_jsx_runtime19 = require("react/jsx-runtime");
3944
4605
  function HighlightedLabel({ item }) {
3945
- if (item.labelIndices.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: item.label });
4606
+ if (item.labelIndices.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, { children: item.label });
3946
4607
  const marked = new Set(item.labelIndices);
3947
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: Array.from(item.label).map(
3948
- (ch, i) => marked.has(i) ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-semibold text-foreground", "data-match": "", children: ch }, i) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react27.default.Fragment, { children: ch }, i)
4608
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, { children: Array.from(item.label).map(
4609
+ (ch, i) => marked.has(i) ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-semibold text-foreground", "data-match": "", children: ch }, i) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react33.default.Fragment, { children: ch }, i)
3949
4610
  ) });
3950
4611
  }
3951
4612
  var CommandPalette = ({
@@ -3954,22 +4615,22 @@ var CommandPalette = ({
3954
4615
  emptyMessage = "No matching commands",
3955
4616
  className = ""
3956
4617
  }) => {
3957
- const inputRef = (0, import_react27.useRef)(null);
3958
- const listRef = (0, import_react27.useRef)(null);
3959
- const baseId = (0, import_react27.useId)();
4618
+ const inputRef = (0, import_react33.useRef)(null);
4619
+ const listRef = (0, import_react33.useRef)(null);
4620
+ const baseId = (0, import_react33.useId)();
3960
4621
  const listId = `${baseId}-list`;
3961
4622
  const optionId = (index) => `${baseId}-option-${index}`;
3962
4623
  const { open, query, items, activeIndex } = palette;
3963
- (0, import_react27.useEffect)(() => {
4624
+ (0, import_react33.useEffect)(() => {
3964
4625
  if (open) inputRef.current?.focus();
3965
4626
  }, [open]);
3966
- (0, import_react27.useEffect)(() => {
4627
+ (0, import_react33.useEffect)(() => {
3967
4628
  if (!open) return;
3968
4629
  const active = listRef.current?.querySelector('[aria-selected="true"]');
3969
4630
  active?.scrollIntoView?.({ block: "nearest" });
3970
4631
  }, [open, activeIndex]);
3971
4632
  if (!open) return null;
3972
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4633
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3973
4634
  "div",
3974
4635
  {
3975
4636
  className: "fixed inset-0 z-50 flex items-start justify-center bg-black/30 px-4 pt-[15vh]",
@@ -3977,7 +4638,7 @@ var CommandPalette = ({
3977
4638
  onMouseDown: (e) => {
3978
4639
  if (e.target === e.currentTarget) palette.setOpen(false);
3979
4640
  },
3980
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
4641
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
3981
4642
  "div",
3982
4643
  {
3983
4644
  role: "dialog",
@@ -3986,7 +4647,7 @@ var CommandPalette = ({
3986
4647
  className: `w-full max-w-lg overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-xl ${className}`,
3987
4648
  style: { background: "var(--popover, var(--background, #fff))" },
3988
4649
  children: [
3989
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4650
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3990
4651
  "input",
3991
4652
  {
3992
4653
  ref: inputRef,
@@ -4006,7 +4667,7 @@ var CommandPalette = ({
4006
4667
  className: "w-full border-b border-border bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
4007
4668
  }
4008
4669
  ),
4009
- items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { ref: listRef, id: listId, role: "listbox", "aria-label": "Commands", className: "max-h-80 overflow-y-auto py-1", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
4670
+ items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { ref: listRef, id: listId, role: "listbox", "aria-label": "Commands", className: "max-h-80 overflow-y-auto py-1", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4010
4671
  "li",
4011
4672
  {
4012
4673
  id: optionId(index),
@@ -4021,12 +4682,12 @@ var CommandPalette = ({
4021
4682
  onClick: () => palette.run(item.id),
4022
4683
  className: `flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm ${index === activeIndex ? "bg-muted" : ""} ${item.enabled ? "" : "cursor-default opacity-50"}`,
4023
4684
  children: [
4024
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
4025
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HighlightedLabel, { item }),
4026
- item.disabledReason && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ml-2 text-xs text-muted-foreground", "data-disabled-reason": "", children: item.disabledReason })
4685
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
4686
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(HighlightedLabel, { item }),
4687
+ item.disabledReason && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ml-2 text-xs text-muted-foreground", "data-disabled-reason": "", children: item.disabledReason })
4027
4688
  ] }),
4028
- item.group && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.group }),
4029
- item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("kbd", { className: "shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground", children: item.shortcut })
4689
+ item.group && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.group }),
4690
+ item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("kbd", { className: "shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground", children: item.shortcut })
4030
4691
  ]
4031
4692
  },
4032
4693
  item.id
@@ -4039,12 +4700,12 @@ var CommandPalette = ({
4039
4700
  };
4040
4701
 
4041
4702
  // src/commands/useCommandShortcuts.ts
4042
- var import_react28 = require("react");
4703
+ var import_react34 = require("react");
4043
4704
  function useCommandShortcuts(registry, options = {}) {
4044
- const optionsRef = (0, import_react28.useRef)(options);
4705
+ const optionsRef = (0, import_react34.useRef)(options);
4045
4706
  optionsRef.current = options;
4046
4707
  const { enabled = true, target } = options;
4047
- (0, import_react28.useEffect)(() => {
4708
+ (0, import_react34.useEffect)(() => {
4048
4709
  if (!registry || !enabled) return;
4049
4710
  const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
4050
4711
  if (!eventTarget) return;
@@ -4062,24 +4723,589 @@ function useCommandShortcuts(registry, options = {}) {
4062
4723
  }, [registry, enabled, target]);
4063
4724
  }
4064
4725
  function useRegisterCommands(registry, specs) {
4065
- (0, import_react28.useEffect)(() => {
4726
+ (0, import_react34.useEffect)(() => {
4066
4727
  if (!registry || !specs || specs.length === 0) return;
4067
4728
  return registry.register(specs);
4068
4729
  }, [registry, specs]);
4069
4730
  }
4731
+
4732
+ // src/persistence/useAutosave.ts
4733
+ var import_react35 = require("react");
4734
+ var import_persistence = require("@pixerate/editor/persistence");
4735
+ var IDLE = {
4736
+ status: "idle",
4737
+ error: null,
4738
+ failures: 0,
4739
+ nextAttemptAt: null,
4740
+ lastSavedAt: null,
4741
+ ready: false
4742
+ };
4743
+ function useAutosave(options) {
4744
+ const optionsRef = (0, import_react35.useRef)(options);
4745
+ optionsRef.current = options;
4746
+ const { storage, key, history, dirty } = options;
4747
+ const recover = options.recover !== false;
4748
+ const flushOnHide = options.flushOnHide !== false;
4749
+ const ready = options.ready !== false;
4750
+ const [controller, setController] = (0, import_react35.useState)(null);
4751
+ const [status, setStatus] = (0, import_react35.useState)(IDLE);
4752
+ const [recovery, setRecovery] = (0, import_react35.useState)(null);
4753
+ const [pendingRecovery, setPendingRecovery] = (0, import_react35.useState)(recover);
4754
+ const revisionsRef = (0, import_react35.useRef)(null);
4755
+ (0, import_react35.useEffect)(() => {
4756
+ const revisions = (0, import_persistence.createRevisionTracker)({ history, dirty });
4757
+ const o = optionsRef.current;
4758
+ const instance = (0, import_persistence.createAutosaveController)({
4759
+ storage,
4760
+ key,
4761
+ getSnapshot: () => optionsRef.current.getSnapshot(),
4762
+ serialize: o.serialize,
4763
+ deserialize: o.deserialize,
4764
+ name: () => optionsRef.current.name,
4765
+ revision: revisions.revision,
4766
+ savedRevision: revisions.savedRevision,
4767
+ interval: o.interval,
4768
+ debounceMs: o.debounceMs,
4769
+ retryBaseMs: o.retryBaseMs,
4770
+ retryMaxMs: o.retryMaxMs
4771
+ });
4772
+ const unsubscribeStatus = instance.subscribe(setStatus);
4773
+ const unsubscribeRevisions = revisions.subscribe(() => instance.notify());
4774
+ revisionsRef.current = revisions;
4775
+ setController(instance);
4776
+ setStatus(instance.getStatus());
4777
+ let active = true;
4778
+ if (recover) {
4779
+ setPendingRecovery(true);
4780
+ instance.checkRecovery().catch(() => null).then((found) => {
4781
+ if (!active) return;
4782
+ setRecovery(found);
4783
+ setPendingRecovery(found !== null);
4784
+ });
4785
+ } else {
4786
+ setRecovery(null);
4787
+ setPendingRecovery(false);
4788
+ }
4789
+ return () => {
4790
+ active = false;
4791
+ unsubscribeStatus();
4792
+ unsubscribeRevisions();
4793
+ revisions.destroy();
4794
+ instance.destroy();
4795
+ if (revisionsRef.current === revisions) revisionsRef.current = null;
4796
+ };
4797
+ }, [storage, key, history, dirty, recover]);
4798
+ (0, import_react35.useEffect)(() => {
4799
+ if (controller && ready && !pendingRecovery) controller.ready();
4800
+ }, [controller, ready, pendingRecovery]);
4801
+ (0, import_react35.useEffect)(() => {
4802
+ if (!controller || !flushOnHide || typeof window === "undefined") return;
4803
+ const flush = () => void controller.flushNow();
4804
+ const onVisibility = () => {
4805
+ if (document.visibilityState === "hidden") flush();
4806
+ };
4807
+ window.addEventListener("pagehide", flush);
4808
+ document.addEventListener("visibilitychange", onVisibility);
4809
+ return () => {
4810
+ window.removeEventListener("pagehide", flush);
4811
+ document.removeEventListener("visibilitychange", onVisibility);
4812
+ };
4813
+ }, [controller, flushOnHide]);
4814
+ const restore = (0, import_react35.useCallback)(() => {
4815
+ const snapshot = recovery?.snapshot;
4816
+ setRecovery(null);
4817
+ setPendingRecovery(false);
4818
+ return snapshot;
4819
+ }, [recovery]);
4820
+ const discard = (0, import_react35.useCallback)(async () => {
4821
+ if (controller) await controller.discardRecovery();
4822
+ setRecovery(null);
4823
+ setPendingRecovery(false);
4824
+ }, [controller]);
4825
+ const flushNow = (0, import_react35.useCallback)(async () => {
4826
+ await controller?.flushNow();
4827
+ }, [controller]);
4828
+ const markClean = (0, import_react35.useCallback)(async () => {
4829
+ revisionsRef.current?.markSaved();
4830
+ await controller?.markClean();
4831
+ }, [controller]);
4832
+ const notifyChange = (0, import_react35.useCallback)(() => revisionsRef.current?.bump(), []);
4833
+ return { status, recovery, pendingRecovery, restore, discard, flushNow, markClean, notifyChange, controller };
4834
+ }
4835
+
4836
+ // src/persistence/RecoveryPrompt.tsx
4837
+ var import_persistence2 = require("@pixerate/editor/persistence");
4838
+ var import_jsx_runtime20 = require("react/jsx-runtime");
4839
+ var RecoveryPrompt = ({
4840
+ recovery,
4841
+ onRestore,
4842
+ onDiscard,
4843
+ formatTime,
4844
+ restoreLabel = "Restore",
4845
+ discardLabel = "Discard",
4846
+ className = ""
4847
+ }) => {
4848
+ if (!recovery) return null;
4849
+ const { name } = recovery.meta;
4850
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4851
+ "div",
4852
+ {
4853
+ role: "alertdialog",
4854
+ "aria-label": "Unsaved changes found",
4855
+ "data-recovery-prompt": "",
4856
+ className: `flex flex-wrap items-center gap-3 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm ${className}`,
4857
+ children: [
4858
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "min-w-0 flex-1", children: [
4859
+ (0, import_persistence2.formatRecoveryMessage)(recovery.meta, formatTime),
4860
+ name ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "ml-1 text-xs text-muted-foreground", children: [
4861
+ "(",
4862
+ name,
4863
+ ")"
4864
+ ] }) : null
4865
+ ] }),
4866
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4867
+ "button",
4868
+ {
4869
+ type: "button",
4870
+ "data-recovery-restore": "",
4871
+ onClick: onRestore,
4872
+ className: "rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground hover:opacity-90",
4873
+ children: restoreLabel
4874
+ }
4875
+ ),
4876
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4877
+ "button",
4878
+ {
4879
+ type: "button",
4880
+ "data-recovery-discard": "",
4881
+ onClick: onDiscard,
4882
+ className: "rounded-md border border-border px-3 py-1 text-xs font-medium hover:bg-muted",
4883
+ children: discardLabel
4884
+ }
4885
+ )
4886
+ ]
4887
+ }
4888
+ );
4889
+ };
4890
+
4891
+ // src/generation/useGenerationPlan.ts
4892
+ var import_react36 = require("react");
4893
+ var import_generation = require("@pixerate/editor/generation");
4894
+ function useGenerationPlan(request, capabilities, options = {}) {
4895
+ const key = JSON.stringify([request, capabilities, options]);
4896
+ return (0, import_react36.useMemo)(() => (0, import_generation.planGenerationRequest)(request, capabilities, options), [key]);
4897
+ }
4898
+ function useJobProgress(options) {
4899
+ const { startedAt, expectedMs, done, now, intervalMs = 1e3 } = options;
4900
+ const [tick, setTick] = (0, import_react36.useState)(() => Date.now());
4901
+ const ticking = !done && now === void 0;
4902
+ (0, import_react36.useEffect)(() => {
4903
+ if (!ticking) return;
4904
+ setTick(Date.now());
4905
+ const id = setInterval(() => setTick(Date.now()), Math.max(16, intervalMs));
4906
+ return () => clearInterval(id);
4907
+ }, [ticking, intervalMs]);
4908
+ return (0, import_generation.estimateJobProgress)({ startedAt, expectedMs, done, now: now ?? tick });
4909
+ }
4910
+
4911
+ // src/generation/GenerationSettings.tsx
4912
+ var import_react37 = require("react");
4913
+ var import_generation2 = require("@pixerate/editor/generation");
4914
+ var import_jsx_runtime21 = require("react/jsx-runtime");
4915
+ var inputClass = "w-full rounded-md border border-border bg-background px-2 py-1 text-sm outline-none focus:ring-1 focus:ring-ring";
4916
+ function Control({
4917
+ control,
4918
+ value,
4919
+ id,
4920
+ onValue
4921
+ }) {
4922
+ const shown = value ?? control.defaultValue;
4923
+ if (control.kind === "checkbox") {
4924
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4925
+ "input",
4926
+ {
4927
+ id,
4928
+ type: "checkbox",
4929
+ "data-field": control.key,
4930
+ checked: Boolean(shown),
4931
+ onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.checked))
4932
+ }
4933
+ );
4934
+ }
4935
+ if (control.kind === "select") {
4936
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4937
+ "select",
4938
+ {
4939
+ id,
4940
+ "data-field": control.key,
4941
+ className: inputClass,
4942
+ value: shown === void 0 ? "" : String(shown),
4943
+ onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value)),
4944
+ children: [
4945
+ shown === void 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "", children: "Default" }),
4946
+ control.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: String(option.value), children: option.label }, String(option.value)))
4947
+ ]
4948
+ }
4949
+ );
4950
+ }
4951
+ if (control.kind === "textarea") {
4952
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4953
+ "textarea",
4954
+ {
4955
+ id,
4956
+ "data-field": control.key,
4957
+ className: inputClass,
4958
+ rows: 2,
4959
+ maxLength: control.maxLength,
4960
+ value: typeof value === "string" ? value : "",
4961
+ onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value))
4962
+ }
4963
+ );
4964
+ }
4965
+ const numeric = typeof shown === "number" ? shown : void 0;
4966
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
4967
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4968
+ "input",
4969
+ {
4970
+ id,
4971
+ type: control.kind === "range" ? "range" : "number",
4972
+ "data-field": control.key,
4973
+ className: control.kind === "range" ? "flex-1" : inputClass,
4974
+ min: control.min,
4975
+ max: control.max,
4976
+ step: control.step ?? "any",
4977
+ value: numeric ?? (control.kind === "range" ? control.min ?? 0 : ""),
4978
+ onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value))
4979
+ }
4980
+ ),
4981
+ control.kind === "range" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-10 text-right tabular-nums text-muted-foreground", "data-field-value": "", children: numeric ?? "\u2013" })
4982
+ ] });
4983
+ }
4984
+ var GenerationSettings = ({
4985
+ capabilities,
4986
+ value,
4987
+ onChange,
4988
+ plan: planProp,
4989
+ strategy,
4990
+ showAdjustments = true,
4991
+ onAddReference,
4992
+ renderReference,
4993
+ className = ""
4994
+ }) => {
4995
+ const baseId = (0, import_react37.useId)();
4996
+ const controls = (0, import_react37.useMemo)(() => (0, import_generation2.getGenerationControls)(capabilities), [capabilities]);
4997
+ const computed = useGenerationPlan(value, capabilities, { strategy });
4998
+ const plan = planProp ?? computed;
4999
+ const slots = (0, import_generation2.getReferenceSlots)(capabilities, value);
5000
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `flex flex-col gap-3 text-sm ${className}`, "data-generation-settings": "", "data-model": capabilities.id, children: [
5001
+ controls.map((control) => {
5002
+ const id = `${baseId}-${control.key}`;
5003
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-1", "data-control": control.key, children: [
5004
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: id, className: "text-xs font-medium text-muted-foreground", title: control.description, children: control.label }),
5005
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5006
+ Control,
5007
+ {
5008
+ control,
5009
+ id,
5010
+ value: (0, import_generation2.getRequestValue)(value, control.key),
5011
+ onValue: (next) => onChange((0, import_generation2.setRequestValue)(value, control.key, next))
5012
+ }
5013
+ )
5014
+ ] }, control.key);
5015
+ }),
5016
+ slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("fieldset", { className: "flex flex-col gap-1", "data-reference-role": slot.role, children: [
5017
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("legend", { className: "text-xs font-medium text-muted-foreground", title: slot.description, children: [
5018
+ slot.label,
5019
+ " ",
5020
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "tabular-nums", "data-reference-count": "", children: [
5021
+ "(",
5022
+ slot.references.length,
5023
+ "/",
5024
+ slot.max,
5025
+ ")"
5026
+ ] }),
5027
+ slot.min > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-destructive", children: " *" })
5028
+ ] }),
5029
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("ul", { className: "flex flex-wrap gap-1", children: [
5030
+ slot.references.map((ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-1 rounded border border-border px-1.5 py-0.5", "data-reference-id": ref.id, children: [
5031
+ renderReference ? renderReference(ref) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "max-w-[10rem] truncate", children: ref.id }),
5032
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5033
+ "button",
5034
+ {
5035
+ type: "button",
5036
+ "aria-label": `Remove ${slot.label} ${ref.id}`,
5037
+ className: "text-muted-foreground hover:text-foreground",
5038
+ onClick: () => onChange((0, import_generation2.removeReference)(value, slot.role, ref.id)),
5039
+ children: "\xD7"
5040
+ }
5041
+ )
5042
+ ] }, ref.id)),
5043
+ onAddReference && slot.canAdd && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5044
+ "button",
5045
+ {
5046
+ type: "button",
5047
+ className: "rounded border border-dashed border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground",
5048
+ onClick: () => onAddReference(slot.role),
5049
+ children: "Add"
5050
+ }
5051
+ ) })
5052
+ ] })
5053
+ ] }, slot.role)),
5054
+ showAdjustments && plan.adjustments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { role: "status", className: "flex flex-col gap-0.5 text-xs text-muted-foreground", "data-generation-adjustments": "", children: plan.adjustments.map((adjustment, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { "data-adjustment-field": adjustment.field, "data-adjustment-kind": adjustment.kind, children: adjustment.reason }, index)) }),
5055
+ showAdjustments && plan.errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { role: "alert", className: "flex flex-col gap-0.5 text-xs text-destructive", "data-generation-errors": "", children: plan.errors.map((error, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { "data-error-field": error.field, children: error.message }, index)) })
5056
+ ] });
5057
+ };
5058
+
5059
+ // src/generation/JobProgress.tsx
5060
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5061
+ var JobProgress = ({ title, className = "", ...options }) => {
5062
+ const progress = useJobProgress(options);
5063
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `flex flex-col gap-1 text-sm ${className}`, "data-job-progress": "", "data-done": options.done ? "" : void 0, children: [
5064
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center justify-between gap-2 text-muted-foreground", children: [
5065
+ title && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-foreground", children: title }),
5066
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "data-job-progress-label": "", "aria-live": "polite", children: progress.label })
5067
+ ] }),
5068
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5069
+ "div",
5070
+ {
5071
+ role: "progressbar",
5072
+ "aria-label": title ?? "Generation progress",
5073
+ "aria-valuemin": 0,
5074
+ "aria-valuemax": 100,
5075
+ "aria-valuenow": progress.percent,
5076
+ "aria-valuetext": `${progress.percent}%, ${progress.label}`,
5077
+ className: "h-1.5 w-full overflow-hidden rounded-full bg-muted",
5078
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5079
+ "div",
5080
+ {
5081
+ className: "h-full rounded-full bg-primary transition-[width] duration-500",
5082
+ style: { width: `${progress.percent}%`, background: "var(--primary, currentColor)" }
5083
+ }
5084
+ )
5085
+ }
5086
+ )
5087
+ ] });
5088
+ };
5089
+
5090
+ // src/find-replace/useFindReplace.ts
5091
+ var import_react38 = require("react");
5092
+ var import_editor14 = require("@pixerate/editor");
5093
+ var noopUnsubscribe2 = () => {
5094
+ };
5095
+ function useFindReplace(editor) {
5096
+ const store = (0, import_editor14.getFindReplaceStore)(editor ?? null);
5097
+ const subscribe = (0, import_react38.useCallback)(
5098
+ (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe2,
5099
+ [store]
5100
+ );
5101
+ const getSnapshot = () => store?.getSnapshot() ?? import_editor14.EMPTY_FIND_REPLACE_SNAPSHOT;
5102
+ const snapshot = (0, import_react38.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
5103
+ return (0, import_react38.useMemo)(() => {
5104
+ const run = (fn) => {
5105
+ if (editor && !editor.isDestroyed && store) fn(editor);
5106
+ };
5107
+ return {
5108
+ ...snapshot,
5109
+ available: !!store,
5110
+ matchCount: snapshot.matches.length,
5111
+ show: (mode = "find") => run((e) => e.commands.openFindReplace({ mode })),
5112
+ close: () => run((e) => e.commands.closeFindReplace()),
5113
+ setQuery: (query) => run((e) => e.commands.setSearch({ query })),
5114
+ setReplacement: (replacement) => run((e) => e.commands.setSearch({ replacement })),
5115
+ setOptions: (options) => run((e) => e.commands.setSearch({ options })),
5116
+ toggleOption: (option) => run((e) => e.commands.setSearch({ options: { [option]: !snapshot.options[option] } })),
5117
+ next: () => run((e) => e.commands.findNext()),
5118
+ previous: () => run((e) => e.commands.findPrevious()),
5119
+ replace: () => run((e) => e.commands.replaceCurrent()),
5120
+ replaceAll: () => run((e) => e.commands.replaceAll()),
5121
+ clear: () => run((e) => e.commands.clearSearch())
5122
+ };
5123
+ }, [snapshot, editor, store]);
5124
+ }
5125
+
5126
+ // src/find-replace/FindReplaceBar.tsx
5127
+ var import_react39 = require("react");
5128
+ var import_editor15 = require("@pixerate/editor");
5129
+ var import_jsx_runtime23 = require("react/jsx-runtime");
5130
+ var inputClass2 = "min-w-0 flex-1 rounded border border-input bg-background px-2 py-1 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring";
5131
+ var buttonClass3 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
5132
+ var toggleClass = (on) => `${buttonClass3} font-mono ${on ? "bg-primary/10 text-primary" : "text-muted-foreground"}`;
5133
+ var isMod = (e) => e.metaKey || e.ctrlKey;
5134
+ var FindReplaceBar = ({
5135
+ editor,
5136
+ className = "flex flex-col gap-1 rounded-md border border-border bg-background p-1 text-sm shadow-sm",
5137
+ alwaysOpen = false,
5138
+ showMoreOptions = true,
5139
+ onClose
5140
+ }) => {
5141
+ const find = useFindReplace(editor);
5142
+ const findInput = (0, import_react39.useRef)(null);
5143
+ const [moreOpen, setMoreOpen] = (0, import_react39.useState)(false);
5144
+ const visible = find.available && (alwaysOpen || find.open);
5145
+ (0, import_react39.useEffect)(() => {
5146
+ if (!visible || find.focusRequest === 0) return;
5147
+ findInput.current?.focus();
5148
+ findInput.current?.select();
5149
+ }, [visible, find.focusRequest]);
5150
+ if (!visible) return null;
5151
+ const showReplace = find.mode === "replace";
5152
+ const status = (0, import_editor15.formatFindStatus)(find);
5153
+ const hasMatches = find.matchCount > 0;
5154
+ const close = () => {
5155
+ find.close();
5156
+ editor?.commands.focus();
5157
+ onClose?.();
5158
+ };
5159
+ const onBarKeyDown = (e) => {
5160
+ if (e.key === "Escape") {
5161
+ e.preventDefault();
5162
+ close();
5163
+ } else if (isMod(e) && (e.key === "f" || e.key === "F")) {
5164
+ e.preventDefault();
5165
+ find.show("find");
5166
+ } else if (isMod(e) && (e.key === "h" || e.key === "H")) {
5167
+ e.preventDefault();
5168
+ find.show("replace");
5169
+ }
5170
+ };
5171
+ const onFindKeyDown = (e) => {
5172
+ if (e.key !== "Enter") return;
5173
+ e.preventDefault();
5174
+ if (e.shiftKey) find.previous();
5175
+ else find.next();
5176
+ };
5177
+ const onReplaceKeyDown = (e) => {
5178
+ if (e.key !== "Enter") return;
5179
+ e.preventDefault();
5180
+ if (isMod(e)) find.replaceAll();
5181
+ else find.replace();
5182
+ };
5183
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className, role: "search", "aria-label": "Find and replace", onKeyDown: onBarKeyDown, children: [
5184
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1", children: [
5185
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5186
+ "button",
5187
+ {
5188
+ type: "button",
5189
+ className: buttonClass3,
5190
+ "aria-label": "Toggle replace",
5191
+ "aria-expanded": showReplace,
5192
+ onClick: () => find.show(showReplace ? "find" : "replace"),
5193
+ children: showReplace ? "\u25BE" : "\u25B8"
5194
+ }
5195
+ ),
5196
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5197
+ "input",
5198
+ {
5199
+ ref: findInput,
5200
+ type: "text",
5201
+ className: inputClass2,
5202
+ placeholder: "Find",
5203
+ "aria-label": "Find",
5204
+ "aria-invalid": !!find.error,
5205
+ value: find.query,
5206
+ onChange: (e) => find.setQuery(e.target.value),
5207
+ onKeyDown: onFindKeyDown
5208
+ }
5209
+ ),
5210
+ import_editor15.FIND_OPTION_TOGGLES.filter((t) => !t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5211
+ "button",
5212
+ {
5213
+ type: "button",
5214
+ className: toggleClass(!!find.options[t.key]),
5215
+ "aria-label": t.label,
5216
+ title: t.label,
5217
+ "aria-pressed": !!find.options[t.key],
5218
+ onClick: () => find.toggleOption(t.key),
5219
+ children: t.short
5220
+ },
5221
+ t.key
5222
+ )),
5223
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5224
+ "span",
5225
+ {
5226
+ className: `min-w-[5rem] px-1 text-center text-xs ${find.error ? "text-destructive" : "text-muted-foreground"}`,
5227
+ "aria-live": "polite",
5228
+ role: find.error ? "alert" : "status",
5229
+ "data-testid": "find-status",
5230
+ children: status
5231
+ }
5232
+ ),
5233
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Previous match", disabled: !hasMatches, onClick: find.previous, children: "\u2191" }),
5234
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Next match", disabled: !hasMatches, onClick: find.next, children: "\u2193" }),
5235
+ showMoreOptions && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5236
+ "button",
5237
+ {
5238
+ type: "button",
5239
+ className: buttonClass3,
5240
+ "aria-label": "More options",
5241
+ "aria-expanded": moreOpen,
5242
+ onClick: () => setMoreOpen((v) => !v),
5243
+ children: "\u2026"
5244
+ }
5245
+ ),
5246
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Close find", onClick: close, children: "\xD7" })
5247
+ ] }),
5248
+ showReplace && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1 pl-7", children: [
5249
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5250
+ "input",
5251
+ {
5252
+ type: "text",
5253
+ className: inputClass2,
5254
+ placeholder: "Replace",
5255
+ "aria-label": "Replace",
5256
+ value: find.replacement,
5257
+ onChange: (e) => find.setReplacement(e.target.value),
5258
+ onKeyDown: onReplaceKeyDown
5259
+ }
5260
+ ),
5261
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5262
+ "button",
5263
+ {
5264
+ type: "button",
5265
+ className: buttonClass3,
5266
+ disabled: !hasMatches || find.currentIndex < 0,
5267
+ onClick: find.replace,
5268
+ children: "Replace"
5269
+ }
5270
+ ),
5271
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, disabled: !hasMatches, onClick: find.replaceAll, children: "Replace all" }),
5272
+ find.lastReplaceCount !== null && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "px-1 text-xs text-muted-foreground", "aria-live": "polite", children: find.lastReplaceCount === 1 ? "1 replaced" : `${find.lastReplaceCount} replaced` })
5273
+ ] }),
5274
+ showMoreOptions && moreOpen && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 pl-7 text-xs text-muted-foreground", children: import_editor15.FIND_OPTION_TOGGLES.filter((t) => t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1", children: [
5275
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5276
+ "input",
5277
+ {
5278
+ type: "checkbox",
5279
+ checked: !!find.options[t.key],
5280
+ onChange: () => find.toggleOption(t.key)
5281
+ }
5282
+ ),
5283
+ t.label
5284
+ ] }, t.key)) })
5285
+ ] });
5286
+ };
4070
5287
  // Annotate the CommonJS export names for ESM import in node:
4071
5288
  0 && (module.exports = {
5289
+ AdjustmentLayersPanel,
4072
5290
  BubbleMenu,
4073
5291
  CommandPalette,
4074
5292
  CommentButton,
4075
5293
  CommentsPanel,
5294
+ ControlSlider,
5295
+ CurvesEditor,
4076
5296
  DirtyTracker,
4077
5297
  EditorContent,
4078
5298
  EditorSlotsProvider,
5299
+ FindReplaceBar,
4079
5300
  FormulaHints,
5301
+ GenerationSettings,
5302
+ Histogram,
4080
5303
  HistoryManager,
4081
5304
  ImageEditor,
5305
+ JobProgress,
5306
+ MergePreview,
4082
5307
  OutlinePanel,
5308
+ RecoveryPrompt,
4083
5309
  ReviewToolbar,
4084
5310
  SpreadsheetContextMenu,
4085
5311
  SpreadsheetEditor,
@@ -4091,18 +5317,25 @@ function useRegisterCommands(registry, specs) {
4091
5317
  createHistoryManager,
4092
5318
  defaultIsEqual,
4093
5319
  useAgentToolProvider,
5320
+ useAutosave,
4094
5321
  useCommandPalette,
4095
5322
  useCommandShortcuts,
4096
5323
  useComments,
4097
5324
  useDismissGuard,
5325
+ useDocumentCompare,
4098
5326
  useEditor,
4099
5327
  useEditorSlots,
4100
5328
  useFileImport,
4101
5329
  useFillHandle,
5330
+ useFindReplace,
5331
+ useGenerationPlan,
4102
5332
  useHistory,
4103
5333
  useHistoryDirty,
4104
5334
  useHistoryShortcuts,
4105
5335
  useImageEditor,
5336
+ useImageHistogram,
5337
+ useJobProgress,
5338
+ useMergePreview,
4106
5339
  useNavigationGuard,
4107
5340
  useOutline,
4108
5341
  usePromptEditor,