@pixerate/editor-react 0.13.0 → 0.15.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 (38) hide show
  1. package/README.md +5 -4
  2. package/dist/canvas/index.cjs +329 -84
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +82 -4
  5. package/dist/canvas/index.d.ts +82 -4
  6. package/dist/canvas/index.js +266 -18
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-4ZC2WCUO.js +196 -0
  9. package/dist/chunk-4ZC2WCUO.js.map +1 -0
  10. package/dist/{chunk-TL27COKM.js → chunk-LPFZMZ3L.js} +45 -2
  11. package/dist/chunk-LPFZMZ3L.js.map +1 -0
  12. package/dist/{chunk-MHTAR3RS.js → chunk-QVQYYHJ5.js} +87 -29
  13. package/dist/chunk-QVQYYHJ5.js.map +1 -0
  14. package/dist/commands/index.cjs +232 -0
  15. package/dist/commands/index.cjs.map +1 -0
  16. package/dist/commands/index.d.cts +69 -0
  17. package/dist/commands/index.d.ts +69 -0
  18. package/dist/commands/index.js +13 -0
  19. package/dist/commands/index.js.map +1 -0
  20. package/dist/image-editor/index.cjs +41 -0
  21. package/dist/image-editor/index.cjs.map +1 -1
  22. package/dist/image-editor/index.d.cts +2 -0
  23. package/dist/image-editor/index.d.ts +2 -0
  24. package/dist/image-editor/index.js +1 -1
  25. package/dist/index.cjs +624 -268
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +57 -9
  28. package/dist/index.d.ts +57 -9
  29. package/dist/index.js +147 -71
  30. package/dist/index.js.map +1 -1
  31. package/dist/spreadsheet/index.cjs +79 -22
  32. package/dist/spreadsheet/index.cjs.map +1 -1
  33. package/dist/spreadsheet/index.d.cts +2 -3
  34. package/dist/spreadsheet/index.d.ts +2 -3
  35. package/dist/spreadsheet/index.js +1 -1
  36. package/package.json +10 -2
  37. package/dist/chunk-MHTAR3RS.js.map +0 -1
  38. package/dist/chunk-TL27COKM.js.map +0 -1
package/dist/index.js CHANGED
@@ -6,11 +6,17 @@ import {
6
6
  useFillHandle,
7
7
  useSpreadsheetClipboard,
8
8
  useSpreadsheetEditor
9
- } from "./chunk-MHTAR3RS.js";
9
+ } from "./chunk-QVQYYHJ5.js";
10
10
  import {
11
11
  ImageEditor,
12
12
  useImageEditor
13
- } from "./chunk-TL27COKM.js";
13
+ } from "./chunk-LPFZMZ3L.js";
14
+ import {
15
+ CommandPalette,
16
+ useCommandPalette,
17
+ useCommandShortcuts,
18
+ useRegisterCommands
19
+ } from "./chunk-4ZC2WCUO.js";
14
20
 
15
21
  // src/hooks/usePromptEditor.ts
16
22
  import { useMemo, useEffect, useRef } from "react";
@@ -406,7 +412,7 @@ function useDismissGuard({
406
412
  }
407
413
 
408
414
  // src/hooks/useHistory.ts
409
- import { useSyncExternalStore, useRef as useRef4, useCallback as useCallback3 } from "react";
415
+ import { useSyncExternalStore, useRef as useRef4, useCallback as useCallback3, useMemo as useMemo2 } from "react";
410
416
  import {
411
417
  HistoryManager,
412
418
  createHistoryManager
@@ -428,6 +434,7 @@ function useHistory(managerOrOptions) {
428
434
  () => manager.getState(),
429
435
  () => manager.getState()
430
436
  );
437
+ const entries = useMemo2(() => manager.getEntries(), [manager, state]);
431
438
  const undo = useCallback3(() => manager.undo(), [manager]);
432
439
  const redo = useCallback3(() => manager.redo(), [manager]);
433
440
  const clear = useCallback3(() => manager.clear(), [manager]);
@@ -443,60 +450,105 @@ function useHistory(managerOrOptions) {
443
450
  (name, fn) => manager.batch(name, fn),
444
451
  [manager]
445
452
  );
453
+ const goTo = useCallback3((index) => manager.goTo(index), [manager]);
454
+ const mark = useCallback3(() => manager.mark(), [manager]);
455
+ const revertTo = useCallback3(
456
+ (m) => manager.revertTo(m),
457
+ [manager]
458
+ );
459
+ const beginGesture = useCallback3(
460
+ (key) => manager.beginGesture(key),
461
+ [manager]
462
+ );
463
+ const endGesture = useCallback3(() => manager.endGesture(), [manager]);
446
464
  return {
447
465
  ...state,
448
466
  manager,
467
+ entries,
449
468
  undo,
450
469
  redo,
451
470
  clear,
452
471
  execute,
453
472
  executeMerged,
454
- batch
473
+ batch,
474
+ goTo,
475
+ mark,
476
+ revertTo,
477
+ beginGesture,
478
+ endGesture
455
479
  };
456
480
  }
457
481
 
482
+ // src/hooks/useHistoryDirty.ts
483
+ import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef5, useState as useState3, useSyncExternalStore as useSyncExternalStore2 } from "react";
484
+ import {
485
+ DirtyTracker,
486
+ bindDirtyToHistory
487
+ } from "@pixerate/editor";
488
+ function useHistoryDirty(options) {
489
+ const getValueRef = useRef5(options.getValue);
490
+ getValueRef.current = options.getValue;
491
+ const [tracker] = useState3(
492
+ () => options.tracker ?? new DirtyTracker(options.getValue(), {
493
+ isEqual: options.isEqual,
494
+ normalize: options.normalize
495
+ })
496
+ );
497
+ const bindingRef = useRef5(null);
498
+ useEffect3(() => {
499
+ const binding = bindDirtyToHistory({
500
+ history: options.history,
501
+ getValue: () => getValueRef.current(),
502
+ tracker
503
+ });
504
+ bindingRef.current = binding;
505
+ return () => {
506
+ binding.destroy();
507
+ if (bindingRef.current === binding) bindingRef.current = null;
508
+ };
509
+ }, [options.history, tracker]);
510
+ const isDirty = useSyncExternalStore2(
511
+ useCallback4((onChange) => tracker.subscribe(onChange), [tracker]),
512
+ () => tracker.dirty,
513
+ () => tracker.dirty
514
+ );
515
+ const markClean = useCallback4(
516
+ () => tracker.markClean(getValueRef.current()),
517
+ [tracker]
518
+ );
519
+ const suppress = useCallback4(
520
+ (fn) => bindingRef.current ? bindingRef.current.suppress(fn) : tracker.suppress(fn),
521
+ [tracker]
522
+ );
523
+ const refresh = useCallback4(
524
+ () => tracker.update(getValueRef.current()),
525
+ [tracker]
526
+ );
527
+ return { isDirty, markClean, suppress, refresh, tracker };
528
+ }
529
+
458
530
  // src/hooks/useHistoryShortcuts.ts
459
- import { useEffect as useEffect3 } from "react";
531
+ import { useEffect as useEffect4 } from "react";
532
+ import { matchHistoryShortcut } from "@pixerate/editor/commands";
460
533
  function useHistoryShortcuts(manager, options = {}) {
461
534
  const {
462
535
  enabled = true,
463
536
  target,
464
537
  ignoreEditableElements = true
465
538
  } = options;
466
- useEffect3(() => {
539
+ useEffect4(() => {
467
540
  if (!enabled) return;
468
541
  const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
469
542
  if (!eventTarget) return;
470
543
  const handleKeyDown = (event) => {
471
544
  const e = event;
472
- const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
473
- const modifier = isMac ? e.metaKey : e.ctrlKey;
474
- if (!modifier) return;
475
- if (ignoreEditableElements && typeof document !== "undefined") {
476
- const targetEl = e.target || document.activeElement;
477
- if (targetEl && targetEl.tagName) {
478
- const tagName = targetEl.tagName.toLowerCase();
479
- const isInput = tagName === "input" || tagName === "textarea";
480
- const isContentEditable = targetEl.isContentEditable || targetEl.getAttribute?.("contenteditable") === "true";
481
- if (isInput || isContentEditable) {
482
- return;
483
- }
484
- }
485
- }
486
- const key = e.key.toLowerCase();
487
- if (key === "z" && !e.shiftKey) {
488
- if (manager.getState().canUndo) {
489
- e.preventDefault();
490
- manager.undo();
491
- }
492
- return;
493
- }
494
- const isRedoKey = key === "z" && e.shiftKey || !isMac && key === "y" && !e.shiftKey;
495
- if (isRedoKey) {
496
- if (manager.getState().canRedo) {
497
- e.preventDefault();
498
- manager.redo();
499
- }
545
+ const action = matchHistoryShortcut(e, { allowInEditable: !ignoreEditableElements });
546
+ if (action === "undo" && manager.getState().canUndo) {
547
+ e.preventDefault();
548
+ manager.undo();
549
+ } else if (action === "redo" && manager.getState().canRedo) {
550
+ e.preventDefault();
551
+ manager.redo();
500
552
  }
501
553
  };
502
554
  eventTarget.addEventListener("keydown", handleKeyDown);
@@ -507,24 +559,24 @@ function useHistoryShortcuts(manager, options = {}) {
507
559
  }
508
560
 
509
561
  // src/hooks/useFileImport.ts
510
- import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useSyncExternalStore as useSyncExternalStore2 } from "react";
562
+ import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef6, useSyncExternalStore as useSyncExternalStore3 } from "react";
511
563
  import {
512
564
  FileImportController
513
565
  } from "@pixerate/editor";
514
566
  function useFileImport(editor, options = {}) {
515
- const editorRef = useRef5(editor);
567
+ const editorRef = useRef6(editor);
516
568
  editorRef.current = editor;
517
- const controller = useMemo2(
569
+ const controller = useMemo3(
518
570
  () => new FileImportController(() => {
519
571
  const current = editorRef.current;
520
572
  return current && !current.isDestroyed ? current : null;
521
573
  }, options),
522
574
  []
523
575
  );
524
- useEffect4(() => {
576
+ useEffect5(() => {
525
577
  controller.setOptions(options);
526
578
  });
527
- const state = useSyncExternalStore2(controller.subscribe, controller.getState, controller.getState);
579
+ const state = useSyncExternalStore3(controller.subscribe, controller.getState, controller.getState);
528
580
  return {
529
581
  ...state,
530
582
  accept: controller.accept,
@@ -534,9 +586,9 @@ function useFileImport(editor, options = {}) {
534
586
  }
535
587
 
536
588
  // src/hooks/useAgentToolProvider.ts
537
- import { useEffect as useEffect5 } from "react";
589
+ import { useEffect as useEffect6 } from "react";
538
590
  function useAgentToolProvider(registry, provider) {
539
- useEffect5(() => {
591
+ useEffect6(() => {
540
592
  if (!registry || !provider) return;
541
593
  return registry.register(provider);
542
594
  }, [registry, provider]);
@@ -556,7 +608,7 @@ var EditorContent = ({
556
608
  };
557
609
 
558
610
  // src/components/BubbleMenu.tsx
559
- import { useEffect as useEffect6, useRef as useRef6, useState as useState3 } from "react";
611
+ import { useEffect as useEffect7, useRef as useRef7, useState as useState4 } from "react";
560
612
  import { createPortal } from "react-dom";
561
613
  import {
562
614
  BubbleMenuPlugin
@@ -570,12 +622,12 @@ var BubbleMenu = ({
570
622
  pluginKey = "bubbleMenu",
571
623
  updateDelay
572
624
  }) => {
573
- const menuEl = useRef6(null);
574
- const [mounted, setMounted] = useState3(false);
625
+ const menuEl = useRef7(null);
626
+ const [mounted, setMounted] = useState4(false);
575
627
  if (!menuEl.current && typeof document !== "undefined") {
576
628
  menuEl.current = document.createElement("div");
577
629
  }
578
- useEffect6(() => {
630
+ useEffect7(() => {
579
631
  if (!editor || !menuEl.current || editor.isDestroyed) {
580
632
  return;
581
633
  }
@@ -606,7 +658,7 @@ var BubbleMenu = ({
606
658
  };
607
659
 
608
660
  // src/components/TemplateRenderer.tsx
609
- import { useMemo as useMemo3 } from "react";
661
+ import { useMemo as useMemo4 } from "react";
610
662
  import {
611
663
  evaluatePromptTokens,
612
664
  resolveTemplates
@@ -624,11 +676,11 @@ var TemplateRenderer = ({
624
676
  onTemplateClick,
625
677
  onVariableClick
626
678
  }) => {
627
- const displayText = useMemo3(() => {
679
+ const displayText = useMemo4(() => {
628
680
  if (!resolve || templates.length === 0) return content;
629
681
  return resolveTemplates(content, templates, { bindings });
630
682
  }, [content, templates, resolve, bindings]);
631
- const tokens = useMemo3(() => {
683
+ const tokens = useMemo4(() => {
632
684
  return evaluatePromptTokens(displayText, values);
633
685
  }, [displayText, values]);
634
686
  return /* @__PURE__ */ jsx2("div", { className, children: tokens.map((token, index) => {
@@ -717,7 +769,7 @@ var TemplateRenderer = ({
717
769
  };
718
770
 
719
771
  // src/components/VariableForm.tsx
720
- import { useId, useMemo as useMemo4, useState as useState4 } from "react";
772
+ import { useId, useMemo as useMemo5, useState as useState5 } from "react";
721
773
  import {
722
774
  compactVariableValues,
723
775
  getVariableFormFields
@@ -733,11 +785,11 @@ var VariableForm = ({
733
785
  className = "flex flex-col gap-3 text-sm"
734
786
  }) => {
735
787
  const idPrefix = useId();
736
- const [internal, setInternal] = useState4(
788
+ const [internal, setInternal] = useState5(
737
789
  () => defaultValues ?? {}
738
790
  );
739
791
  const entered = values ?? internal;
740
- const fields = useMemo4(
792
+ const fields = useMemo5(
741
793
  () => getVariableFormFields(content, { templates, bindings }),
742
794
  [content, templates, bindings]
743
795
  );
@@ -805,11 +857,11 @@ var VariableForm = ({
805
857
  };
806
858
 
807
859
  // src/hooks/useOutline.ts
808
- import { useMemo as useMemo5, useSyncExternalStore as useSyncExternalStore3 } from "react";
860
+ import { useMemo as useMemo6, useSyncExternalStore as useSyncExternalStore4 } from "react";
809
861
  import { createOutlineStore } from "@pixerate/editor";
810
862
  function useOutline(editor) {
811
- const store = useMemo5(() => createOutlineStore(editor), [editor]);
812
- return useSyncExternalStore3(store.subscribe, store.getSnapshot, store.getSnapshot);
863
+ const store = useMemo6(() => createOutlineStore(editor), [editor]);
864
+ return useSyncExternalStore4(store.subscribe, store.getSnapshot, store.getSnapshot);
813
865
  }
814
866
 
815
867
  // src/components/OutlinePanel.tsx
@@ -855,15 +907,17 @@ var OutlinePanel = ({
855
907
 
856
908
  // src/index.ts
857
909
  import {
858
- DirtyTracker,
910
+ DirtyTracker as DirtyTracker2,
859
911
  createDirtyTracker,
860
912
  defaultIsEqual,
861
913
  HistoryManager as HistoryManager2,
862
- createHistoryManager as createHistoryManager2
914
+ createHistoryManager as createHistoryManager2,
915
+ bindDirtyToHistory as bindDirtyToHistory2,
916
+ withoutDirty
863
917
  } from "@pixerate/editor";
864
918
 
865
919
  // src/slots.tsx
866
- import { createContext, useContext, useMemo as useMemo6 } from "react";
920
+ import { createContext, useContext, useMemo as useMemo7 } from "react";
867
921
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
868
922
  var defaultSlots = {
869
923
  Button: (props) => /* @__PURE__ */ jsx5(
@@ -887,7 +941,7 @@ var defaultSlots = {
887
941
  };
888
942
  var EditorSlotsContext = createContext(defaultSlots);
889
943
  function EditorSlotsProvider({ slots, children }) {
890
- const mergedSlots = useMemo6(() => ({ ...defaultSlots, ...slots }), [slots]);
944
+ const mergedSlots = useMemo7(() => ({ ...defaultSlots, ...slots }), [slots]);
891
945
  return /* @__PURE__ */ jsx5(EditorSlotsContext.Provider, { value: mergedSlots, children });
892
946
  }
893
947
  function useEditorSlots() {
@@ -895,7 +949,7 @@ function useEditorSlots() {
895
949
  }
896
950
 
897
951
  // src/review/useReview.ts
898
- import { useCallback as useCallback4, useMemo as useMemo7, useSyncExternalStore as useSyncExternalStore4 } from "react";
952
+ import { useCallback as useCallback5, useMemo as useMemo8, useSyncExternalStore as useSyncExternalStore5 } from "react";
899
953
  import {
900
954
  getCommentStore,
901
955
  getTrackChangesStore
@@ -917,23 +971,23 @@ function isCommentStore(value) {
917
971
  }
918
972
  function useComments(source) {
919
973
  const store = isCommentStore(source) ? source : getCommentStore(source);
920
- const subscribe = useCallback4(
974
+ const subscribe = useCallback5(
921
975
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
922
976
  [store]
923
977
  );
924
978
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
925
- const snapshot = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
926
- return useMemo7(() => ({ ...snapshot, store }), [snapshot, store]);
979
+ const snapshot = useSyncExternalStore5(subscribe, getSnapshot, getSnapshot);
980
+ return useMemo8(() => ({ ...snapshot, store }), [snapshot, store]);
927
981
  }
928
982
  function useTrackChanges(editor) {
929
983
  const store = getTrackChangesStore(editor ?? null);
930
- const subscribe = useCallback4(
984
+ const subscribe = useCallback5(
931
985
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
932
986
  [store]
933
987
  );
934
988
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
935
- const snapshot = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
936
- return useMemo7(() => {
989
+ const snapshot = useSyncExternalStore5(subscribe, getSnapshot, getSnapshot);
990
+ return useMemo8(() => {
937
991
  const run = (fn) => () => {
938
992
  if (editor && !editor.isDestroyed) fn(editor);
939
993
  };
@@ -953,7 +1007,7 @@ function useTrackChanges(editor) {
953
1007
  }
954
1008
 
955
1009
  // src/review/CommentsPanel.tsx
956
- import { useState as useState5 } from "react";
1010
+ import { useState as useState6 } from "react";
957
1011
  import {
958
1012
  formatReviewTime,
959
1013
  getCommentAuthor
@@ -972,7 +1026,7 @@ var CommentsPanel = ({
972
1026
  emptyMessage = "No comments yet. Select text and add a comment."
973
1027
  }) => {
974
1028
  const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
975
- const [showResolved, setShowResolved] = useState5(showResolvedInitial);
1029
+ const [showResolved, setShowResolved] = useState6(showResolvedInitial);
976
1030
  const author = authorProp ?? getCommentAuthor(editor) ?? { id: "anonymous", name: "Anonymous" };
977
1031
  const visible = threads.filter((t) => showResolved || !t.resolved);
978
1032
  const resolvedCount = threads.filter((t) => t.resolved).length;
@@ -1016,7 +1070,7 @@ var CommentThreadCard = ({
1016
1070
  author,
1017
1071
  formatTime
1018
1072
  }) => {
1019
- const [draft, setDraft] = useState5("");
1073
+ const [draft, setDraft] = useState6("");
1020
1074
  const isDraft = thread.body === "";
1021
1075
  const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
1022
1076
  const live = editor && !editor.isDestroyed ? editor : null;
@@ -1126,10 +1180,13 @@ var CommentThreadCard = ({
1126
1180
 
1127
1181
  // src/review/ReviewToolbar.tsx
1128
1182
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1183
+ var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
1129
1184
  var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
1130
1185
  var ReviewToolbar = ({
1131
1186
  editor,
1132
- className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm"
1187
+ className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm",
1188
+ showDescription = true,
1189
+ descriptionClassName = defaultDescriptionClass
1133
1190
  }) => {
1134
1191
  const review = useTrackChanges(editor);
1135
1192
  const count = review.changes.length;
@@ -1171,6 +1228,18 @@ var ReviewToolbar = ({
1171
1228
  children: "\u203A"
1172
1229
  }
1173
1230
  ),
1231
+ showDescription && current && /* @__PURE__ */ jsxs6(
1232
+ "span",
1233
+ {
1234
+ className: descriptionClassName,
1235
+ "data-change-type": current.type,
1236
+ title: `${current.author.name}: ${current.description}`,
1237
+ children: [
1238
+ current.author.name ? `${current.author.name}: ` : "",
1239
+ current.description
1240
+ ]
1241
+ }
1242
+ ),
1174
1243
  /* @__PURE__ */ jsx7("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1175
1244
  /* @__PURE__ */ jsx7(
1176
1245
  "button",
@@ -1215,9 +1284,10 @@ var CommentButton = ({
1215
1284
  );
1216
1285
  export {
1217
1286
  BubbleMenu,
1287
+ CommandPalette,
1218
1288
  CommentButton,
1219
1289
  CommentsPanel,
1220
- DirtyTracker,
1290
+ DirtyTracker2 as DirtyTracker,
1221
1291
  EditorContent,
1222
1292
  EditorSlotsProvider,
1223
1293
  FormulaHints,
@@ -1230,10 +1300,13 @@ export {
1230
1300
  SpreadsheetGroupRow,
1231
1301
  TemplateRenderer,
1232
1302
  VariableForm,
1303
+ bindDirtyToHistory2 as bindDirtyToHistory,
1233
1304
  createDirtyTracker,
1234
1305
  createHistoryManager2 as createHistoryManager,
1235
1306
  defaultIsEqual,
1236
1307
  useAgentToolProvider,
1308
+ useCommandPalette,
1309
+ useCommandShortcuts,
1237
1310
  useComments,
1238
1311
  useDismissGuard,
1239
1312
  useEditor2 as useEditor,
@@ -1241,13 +1314,16 @@ export {
1241
1314
  useFileImport,
1242
1315
  useFillHandle,
1243
1316
  useHistory,
1317
+ useHistoryDirty,
1244
1318
  useHistoryShortcuts,
1245
1319
  useImageEditor,
1246
1320
  useNavigationGuard,
1247
1321
  useOutline,
1248
1322
  usePromptEditor,
1323
+ useRegisterCommands,
1249
1324
  useSpreadsheetClipboard,
1250
1325
  useSpreadsheetEditor,
1251
- useTrackChanges
1326
+ useTrackChanges,
1327
+ withoutDirty
1252
1328
  };
1253
1329
  //# sourceMappingURL=index.js.map