@pixerate/editor-react 0.11.0 → 0.12.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.
package/README.md CHANGED
@@ -58,11 +58,35 @@ export function Document() {
58
58
 
59
59
  `useEditor` is TipTap's `useEditor` preloaded with `createRichTextPreset()`: headings, lists, task lists, links, text alignment, colors and highlights, and images. Mentions and markdown are opt-in via `richTextOptions: { mention: true, markdown: true }`.
60
60
 
61
+ ## Comments and tracked changes
62
+
63
+ ```tsx
64
+ import { Comments, TrackChanges } from "@pixerate/editor";
65
+ import { CommentsPanel, ReviewToolbar, CommentButton, BubbleMenu } from "@pixerate/editor-react";
66
+
67
+ const editor = useEditor({ extensions: [Comments.configure({ author: me }), TrackChanges.configure({ author: me })] });
68
+
69
+ <ReviewToolbar editor={editor} /> {/* toggle tracking, prev/next, accept/reject (all) */}
70
+ <BubbleMenu editor={editor}><CommentButton editor={editor} /></BubbleMenu>
71
+ <CommentsPanel editor={editor} /> {/* threads: reply, resolve, delete, click to select */}
72
+ ```
73
+
74
+ `useComments(editorOrStore)` and `useTrackChanges(editor)` expose the same state for custom UIs.
75
+
76
+ `useEditor({ fileImport: true, richClipboard: true })` imports dropped/pasted `.md`, `.html`, `.txt` and `.rtf` files at the drop position and copies lossless slices between editors. `useFileImport(editor)` returns `{ openFilePicker, importFiles, accept, isImporting, error }` for an import button. `usePromptEditor({ fileImport: true })` imports dropped files as plain text.
77
+
78
+ `useAgentToolProvider(registry, provider)` registers an agent tool provider (see `ToolRegistry` in `@pixerate/editor/agent`) while the component is mounted. Memoize the provider:
79
+
80
+ ```tsx
81
+ const provider = useMemo(() => editor && createRichTextToolProvider(editor, { id: "doc" }), [editor]);
82
+ useAgentToolProvider(registry, provider);
83
+ ```
84
+
61
85
  ## More exports
62
86
 
63
87
  | Import | Exports |
64
88
  | :--- | :--- |
65
- | `@pixerate/editor-react` | `usePromptEditor`, `useEditor`, `EditorContent`, `BubbleMenu`, `TemplateRenderer`, `useHistory`, `useHistoryShortcuts`, `useNavigationGuard`, `useDismissGuard`, `EditorSlotsProvider` / `useEditorSlots`, `SpreadsheetEditor` / `useSpreadsheetEditor`, `DirtyTracker` |
89
+ | `@pixerate/editor-react` | `usePromptEditor`, `useEditor`, `EditorContent`, `BubbleMenu`, `TemplateRenderer`, `useHistory`, `useHistoryShortcuts`, `useNavigationGuard`, `useDismissGuard`, `EditorSlotsProvider` / `useEditorSlots`, `SpreadsheetEditor` / `useSpreadsheetEditor`, `FormulaHints`, `DirtyTracker` |
66
90
  | `@pixerate/editor-react/image-editor` | `ImageEditor`, `useImageEditor`: crop, rotate/flip, adjustments, annotations, depth masks, and `onInpaint` / `onRemoveBackground` AI hooks |
67
91
  | `@pixerate/editor-react/canvas` | `useCanvasGraph`, `useCanvasLayout`, `useCanvasClipboard`, `useCanvasDocking`, `useCanvasShortcuts`, `useCanvasExplosion`, `useStreamingCanvas`, `AgentPresenceLayer`, plus the pure layout helpers from `@pixerate/editor/canvas` |
68
92
 
@@ -21,46 +21,83 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var canvas_exports = {};
22
22
  __export(canvas_exports, {
23
23
  AgentPresenceLayer: () => AgentPresenceLayer,
24
- ManualClock: () => import_canvas6.ManualClock,
25
- blendLayout: () => import_canvas6.blendLayout,
26
- boundsOf: () => import_canvas6.boundsOf,
27
- calculateDagreReflowDisplacement: () => import_canvas6.calculateDagreReflowDisplacement,
28
- calculateDirectionalDisplacement: () => import_canvas6.calculateDirectionalDisplacement,
29
- canvasHeight: () => import_canvas6.canvasHeight,
30
- centerNodes: () => import_canvas6.centerNodes,
31
- centre: () => import_canvas6.centre,
32
- computeNodesBoundingBox: () => import_canvas6.computeNodesBoundingBox,
33
- connectedOnly: () => import_canvas6.connectedOnly,
34
- createBezierTrajectory: () => import_canvas6.createBezierTrajectory,
35
- createFanOutTrajectory: () => import_canvas6.createFanOutTrajectory,
36
- defaultEasing: () => import_canvas6.defaultEasing,
37
- ensureLayout: () => import_canvas6.ensureLayout,
38
- estimateCanvasHeight: () => import_canvas6.estimateCanvasHeight,
39
- estimateNodeSize: () => import_canvas6.estimateNodeSize,
40
- flowSide: () => import_canvas6.flowSide,
41
- getCenteredNodePosition: () => import_canvas6.getCenteredNodePosition,
42
- getLayoutedNodes: () => import_canvas6.getLayoutedNodes,
43
- isEventFromTextInput: () => import_canvas6.isEventFromTextInput,
44
- isValidCanvasNode: () => import_canvas6.isValidCanvasNode,
45
- isValidUrl: () => import_canvas6.isValidUrl,
46
- layoutBounds: () => import_canvas6.layoutBounds,
47
- linearPositionTrajectory: () => import_canvas6.linearPositionTrajectory,
48
- linearTrajectory: () => import_canvas6.linearTrajectory,
49
- overlaps: () => import_canvas6.overlaps,
50
- parseCanvasClipboardData: () => import_canvas6.parseCanvasClipboardData,
51
- placeBeside: () => import_canvas6.placeBeside,
52
- realClock: () => import_canvas6.realClock,
53
- remapPastedNodes: () => import_canvas6.remapPastedNodes,
54
- repositionNodesTo: () => import_canvas6.repositionNodesTo,
55
- runMultiNodeTransition: () => import_canvas6.runMultiNodeTransition,
56
- serializeCanvasNodes: () => import_canvas6.serializeCanvasNodes,
24
+ CONNECTION_LINE_TYPES: () => import_canvas8.CONNECTION_LINE_TYPES,
25
+ CanvasGuidesLayer: () => CanvasGuidesLayer,
26
+ CanvasScenesStore: () => import_canvas8.CanvasScenesStore,
27
+ DEFAULT_ARRANGE_SHORTCUTS: () => import_canvas8.DEFAULT_ARRANGE_SHORTCUTS,
28
+ ManualClock: () => import_canvas9.ManualClock,
29
+ alignNodes: () => import_canvas8.alignNodes,
30
+ applyNodeUpdates: () => import_canvas8.applyNodeUpdates,
31
+ blendLayout: () => import_canvas9.blendLayout,
32
+ boundsOf: () => import_canvas9.boundsOf,
33
+ calculateDagreReflowDisplacement: () => import_canvas9.calculateDagreReflowDisplacement,
34
+ calculateDirectionalDisplacement: () => import_canvas9.calculateDirectionalDisplacement,
35
+ canvasHeight: () => import_canvas9.canvasHeight,
36
+ centerNodes: () => import_canvas9.centerNodes,
37
+ centre: () => import_canvas9.centre,
38
+ computeNodesBoundingBox: () => import_canvas9.computeNodesBoundingBox,
39
+ computeSnapGuides: () => import_canvas8.computeSnapGuides,
40
+ connectedOnly: () => import_canvas9.connectedOnly,
41
+ createBezierTrajectory: () => import_canvas9.createBezierTrajectory,
42
+ createCanvasScenesStore: () => import_canvas8.createCanvasScenesStore,
43
+ createFanOutTrajectory: () => import_canvas9.createFanOutTrajectory,
44
+ createGuideIndex: () => import_canvas8.createGuideIndex,
45
+ createNodeGuideIndex: () => import_canvas8.createNodeGuideIndex,
46
+ defaultEasing: () => import_canvas9.defaultEasing,
47
+ distributeNodes: () => import_canvas8.distributeNodes,
48
+ ensureLayout: () => import_canvas9.ensureLayout,
49
+ estimateCanvasHeight: () => import_canvas9.estimateCanvasHeight,
50
+ estimateNodeSize: () => import_canvas9.estimateNodeSize,
51
+ findConnectorSnap: () => import_canvas8.findConnectorSnap,
52
+ fitViewportToNodes: () => import_canvas8.fitViewportToNodes,
53
+ fitViewportToRect: () => import_canvas8.fitViewportToRect,
54
+ flowSide: () => import_canvas9.flowSide,
55
+ getAbsolutePosition: () => import_canvas8.getAbsolutePosition,
56
+ getCenteredNodePosition: () => import_canvas9.getCenteredNodePosition,
57
+ getLayoutedNodes: () => import_canvas9.getLayoutedNodes,
58
+ getNodeRect: () => import_canvas8.getNodeRect,
59
+ getNodeSize: () => import_canvas8.getNodeSize,
60
+ handlePoint: () => import_canvas8.handlePoint,
61
+ invertNodeUpdates: () => import_canvas8.invertNodeUpdates,
62
+ isEventFromTextInput: () => import_canvas9.isEventFromTextInput,
63
+ isLocked: () => import_canvas8.isLocked,
64
+ isValidCanvasNode: () => import_canvas9.isValidCanvasNode,
65
+ isValidUrl: () => import_canvas9.isValidUrl,
66
+ layoutBounds: () => import_canvas9.layoutBounds,
67
+ linearPositionTrajectory: () => import_canvas9.linearPositionTrajectory,
68
+ linearTrajectory: () => import_canvas9.linearTrajectory,
69
+ lockNodes: () => import_canvas8.lockNodes,
70
+ matchArrangeShortcut: () => import_canvas8.matchArrangeShortcut,
71
+ matchSize: () => import_canvas8.matchSize,
72
+ nearestHandlePositions: () => import_canvas8.nearestHandlePositions,
73
+ oppositePosition: () => import_canvas8.oppositePosition,
74
+ overlaps: () => import_canvas9.overlaps,
75
+ parseCanvasClipboardData: () => import_canvas9.parseCanvasClipboardData,
76
+ parseCanvasScenes: () => import_canvas8.parseCanvasScenes,
77
+ placeBeside: () => import_canvas9.placeBeside,
78
+ positionToSide: () => import_canvas8.positionToSide,
79
+ readStyleSize: () => import_canvas8.readStyleSize,
80
+ realClock: () => import_canvas9.realClock,
81
+ remapPastedNodes: () => import_canvas9.remapPastedNodes,
82
+ reorderNodes: () => import_canvas8.reorderNodes,
83
+ repositionNodesTo: () => import_canvas9.repositionNodesTo,
84
+ runMultiNodeTransition: () => import_canvas9.runMultiNodeTransition,
85
+ serializeCanvasNodes: () => import_canvas9.serializeCanvasNodes,
86
+ sideToPosition: () => import_canvas8.sideToPosition,
87
+ snapNodeDrag: () => import_canvas8.snapNodeDrag,
88
+ toggleLockNodes: () => import_canvas8.toggleLockNodes,
89
+ unlockNodes: () => import_canvas8.unlockNodes,
90
+ useCanvasArrange: () => useCanvasArrange,
91
+ useCanvasArrangeShortcuts: () => useCanvasArrangeShortcuts,
57
92
  useCanvasClipboard: () => useCanvasClipboard,
58
93
  useCanvasDocking: () => useCanvasDocking,
59
94
  useCanvasExplosion: () => useCanvasExplosion,
60
95
  useCanvasGraph: () => useCanvasGraph,
61
96
  useCanvasLayout: () => useCanvasLayout,
97
+ useCanvasScenes: () => useCanvasScenes,
62
98
  useCanvasShortcuts: () => useCanvasShortcuts,
63
- useStreamingCanvas: () => useStreamingCanvas
99
+ useStreamingCanvas: () => useStreamingCanvas,
100
+ withStyleSize: () => import_canvas8.withStyleSize
64
101
  });
65
102
  module.exports = __toCommonJS(canvas_exports);
66
103
 
@@ -805,12 +842,191 @@ function useCanvasExplosion(target) {
805
842
  };
806
843
  }
807
844
 
808
- // src/canvas/index.ts
845
+ // src/canvas/useCanvasArrange.ts
846
+ var import_react8 = require("react");
809
847
  var import_canvas6 = require("@pixerate/editor/canvas");
848
+ function useCanvasArrange(options) {
849
+ const [guides, setGuides] = (0, import_react8.useState)([]);
850
+ const nodesRef = (0, import_react8.useRef)(options.nodes);
851
+ nodesRef.current = options.nodes;
852
+ const optionsRef = (0, import_react8.useRef)(options);
853
+ optionsRef.current = options;
854
+ const controller = (0, import_react8.useMemo)(
855
+ () => (0, import_canvas6.createCanvasArrangeController)({
856
+ getNodes: () => nodesRef.current,
857
+ setNodes: (next) => {
858
+ nodesRef.current = next;
859
+ optionsRef.current.setNodes(next);
860
+ },
861
+ getSelectedIds: () => optionsRef.current.getSelectedIds?.() ?? nodesRef.current.filter((n) => n.selected).map((n) => n.id),
862
+ get history() {
863
+ return optionsRef.current.history;
864
+ },
865
+ getZoom: () => optionsRef.current.zoom ?? 1,
866
+ get guides() {
867
+ return optionsRef.current.guides;
868
+ },
869
+ get snapping() {
870
+ return optionsRef.current.snapping;
871
+ },
872
+ get rectOptions() {
873
+ return optionsRef.current.rectOptions;
874
+ },
875
+ onGuidesChange: setGuides
876
+ }),
877
+ []
878
+ );
879
+ return (0, import_react8.useMemo)(() => ({ ...controller, guides }), [controller, guides]);
880
+ }
881
+ function useCanvasArrangeShortcuts(options) {
882
+ const optionsRef = (0, import_react8.useRef)(options);
883
+ optionsRef.current = options;
884
+ const { targetElement, enabled = true } = options;
885
+ (0, import_react8.useEffect)(() => {
886
+ if (!enabled) return;
887
+ const target = targetElement ?? (typeof window !== "undefined" ? window : null);
888
+ if (!target) return;
889
+ function handleKeyDown(event) {
890
+ const { arrange, scenes, bindings } = optionsRef.current;
891
+ const action = (0, import_canvas6.matchArrangeShortcut)(event, bindings);
892
+ if (!action) return;
893
+ let handled = false;
894
+ if (action === "nextScene" || action === "previousScene") {
895
+ if (scenes) {
896
+ scenes[action === "nextScene" ? "next" : "previous"]();
897
+ handled = true;
898
+ }
899
+ } else {
900
+ handled = arrange.runAction(action);
901
+ }
902
+ if (handled) event.preventDefault();
903
+ }
904
+ target.addEventListener("keydown", handleKeyDown);
905
+ return () => target.removeEventListener("keydown", handleKeyDown);
906
+ }, [targetElement, enabled]);
907
+ }
908
+
909
+ // src/canvas/useCanvasScenes.ts
910
+ var import_react9 = require("react");
911
+ var import_canvas7 = require("@pixerate/editor/canvas");
912
+ function useCanvasScenes(options = {}) {
913
+ const optionsRef = (0, import_react9.useRef)(options);
914
+ optionsRef.current = options;
915
+ const storeRef = (0, import_react9.useRef)(null);
916
+ if (!storeRef.current) storeRef.current = options.store ?? new import_canvas7.CanvasScenesStore(options);
917
+ const store = storeRef.current;
918
+ const state = (0, import_react9.useSyncExternalStore)(
919
+ (listener) => store.subscribe(listener),
920
+ () => store.getState(),
921
+ () => store.getState()
922
+ );
923
+ const actions = (0, import_react9.useMemo)(() => {
924
+ const show = (scene) => {
925
+ if (scene) {
926
+ optionsRef.current.setViewport?.(scene.viewport, { duration: optionsRef.current.duration ?? 300 });
927
+ }
928
+ return scene;
929
+ };
930
+ return {
931
+ store,
932
+ /** Saves the current viewport (or `viewport`) as a new scene. */
933
+ capture(name, extra = {}) {
934
+ const viewport = extra.viewport ?? optionsRef.current.getViewport?.();
935
+ if (!viewport) throw new Error("[useCanvasScenes] capture() needs a viewport or a getViewport option.");
936
+ return store.add({ viewport, name, nodeIds: extra.nodeIds }, extra.index);
937
+ },
938
+ /** Saves a scene framing the given nodes in a container of `size`. */
939
+ captureNodes(nodes, ids, size, name, fit) {
940
+ const viewport = (0, import_canvas7.fitViewportToNodes)(nodes, size, ids, fit);
941
+ return viewport ? store.add({ viewport, name, nodeIds: ids }) : void 0;
942
+ },
943
+ /** Replaces a scene's viewport with the live one. */
944
+ recapture(id) {
945
+ const viewport = optionsRef.current.getViewport?.();
946
+ return viewport ? store.update(id, { viewport }) : void 0;
947
+ },
948
+ update: (id, patch) => store.update(id, patch),
949
+ rename: (id, name) => store.rename(id, name),
950
+ move: (id, toIndex) => store.move(id, toIndex),
951
+ reorder: (ids) => store.reorder(ids),
952
+ remove: (id) => store.remove(id),
953
+ clear: () => store.clear(),
954
+ goTo: (id) => show(store.goTo(id)),
955
+ next: () => show(store.next()),
956
+ previous: () => show(store.previous()),
957
+ toJSON: () => store.toJSON(),
958
+ serialize: () => store.serialize(),
959
+ load: (input) => store.load(input)
960
+ };
961
+ }, [store]);
962
+ return {
963
+ ...actions,
964
+ scenes: state.scenes,
965
+ currentId: state.currentId,
966
+ currentIndex: state.currentIndex,
967
+ current: state.currentIndex === -1 ? null : state.scenes[state.currentIndex]
968
+ };
969
+ }
970
+
971
+ // src/canvas/CanvasGuidesLayer.tsx
972
+ var import_jsx_runtime2 = require("react/jsx-runtime");
973
+ var DEFAULT_VIEWPORT = { x: 0, y: 0, zoom: 1 };
974
+ function CanvasGuidesLayer({
975
+ guides,
976
+ viewport = DEFAULT_VIEWPORT,
977
+ color = "#f59e0b",
978
+ spacingColor = "#ec4899",
979
+ className = ""
980
+ }) {
981
+ if (!guides || guides.length === 0) return null;
982
+ const sx = (x) => x * viewport.zoom + viewport.x;
983
+ const sy = (y) => y * viewport.zoom + viewport.y;
984
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
985
+ "svg",
986
+ {
987
+ className: `pixerate-canvas-guides ${className}`,
988
+ style: {
989
+ position: "absolute",
990
+ inset: 0,
991
+ width: "100%",
992
+ height: "100%",
993
+ pointerEvents: "none",
994
+ overflow: "visible",
995
+ zIndex: 10
996
+ },
997
+ "aria-hidden": "true",
998
+ children: guides.map((guide, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
999
+ "line",
1000
+ {
1001
+ "data-kind": guide.kind,
1002
+ x1: sx(guide.x1),
1003
+ y1: sy(guide.y1),
1004
+ x2: sx(guide.x2),
1005
+ y2: sy(guide.y2),
1006
+ stroke: guide.kind === "spacing" ? spacingColor : color,
1007
+ strokeWidth: 1,
1008
+ strokeDasharray: guide.kind === "centre" ? "4 3" : void 0,
1009
+ shapeRendering: "crispEdges"
1010
+ },
1011
+ i
1012
+ ))
1013
+ }
1014
+ );
1015
+ }
1016
+
1017
+ // src/canvas/index.ts
1018
+ var import_canvas8 = require("@pixerate/editor/canvas");
1019
+ var import_canvas9 = require("@pixerate/editor/canvas");
810
1020
  // Annotate the CommonJS export names for ESM import in node:
811
1021
  0 && (module.exports = {
812
1022
  AgentPresenceLayer,
1023
+ CONNECTION_LINE_TYPES,
1024
+ CanvasGuidesLayer,
1025
+ CanvasScenesStore,
1026
+ DEFAULT_ARRANGE_SHORTCUTS,
813
1027
  ManualClock,
1028
+ alignNodes,
1029
+ applyNodeUpdates,
814
1030
  blendLayout,
815
1031
  boundsOf,
816
1032
  calculateDagreReflowDisplacement,
@@ -819,36 +1035,67 @@ var import_canvas6 = require("@pixerate/editor/canvas");
819
1035
  centerNodes,
820
1036
  centre,
821
1037
  computeNodesBoundingBox,
1038
+ computeSnapGuides,
822
1039
  connectedOnly,
823
1040
  createBezierTrajectory,
1041
+ createCanvasScenesStore,
824
1042
  createFanOutTrajectory,
1043
+ createGuideIndex,
1044
+ createNodeGuideIndex,
825
1045
  defaultEasing,
1046
+ distributeNodes,
826
1047
  ensureLayout,
827
1048
  estimateCanvasHeight,
828
1049
  estimateNodeSize,
1050
+ findConnectorSnap,
1051
+ fitViewportToNodes,
1052
+ fitViewportToRect,
829
1053
  flowSide,
1054
+ getAbsolutePosition,
830
1055
  getCenteredNodePosition,
831
1056
  getLayoutedNodes,
1057
+ getNodeRect,
1058
+ getNodeSize,
1059
+ handlePoint,
1060
+ invertNodeUpdates,
832
1061
  isEventFromTextInput,
1062
+ isLocked,
833
1063
  isValidCanvasNode,
834
1064
  isValidUrl,
835
1065
  layoutBounds,
836
1066
  linearPositionTrajectory,
837
1067
  linearTrajectory,
1068
+ lockNodes,
1069
+ matchArrangeShortcut,
1070
+ matchSize,
1071
+ nearestHandlePositions,
1072
+ oppositePosition,
838
1073
  overlaps,
839
1074
  parseCanvasClipboardData,
1075
+ parseCanvasScenes,
840
1076
  placeBeside,
1077
+ positionToSide,
1078
+ readStyleSize,
841
1079
  realClock,
842
1080
  remapPastedNodes,
1081
+ reorderNodes,
843
1082
  repositionNodesTo,
844
1083
  runMultiNodeTransition,
845
1084
  serializeCanvasNodes,
1085
+ sideToPosition,
1086
+ snapNodeDrag,
1087
+ toggleLockNodes,
1088
+ unlockNodes,
1089
+ useCanvasArrange,
1090
+ useCanvasArrangeShortcuts,
846
1091
  useCanvasClipboard,
847
1092
  useCanvasDocking,
848
1093
  useCanvasExplosion,
849
1094
  useCanvasGraph,
850
1095
  useCanvasLayout,
1096
+ useCanvasScenes,
851
1097
  useCanvasShortcuts,
852
- useStreamingCanvas
1098
+ useStreamingCanvas,
1099
+ withStyleSize
853
1100
  });
854
1101
  //# sourceMappingURL=index.cjs.map