@pixerate/editor-svelte 0.207.1 → 0.208.1

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.
@@ -1,5 +1,7 @@
1
1
  import { type XYPosition } from '@xyflow/svelte';
2
+ import { applyGraphDifferences, sanitizeCanvasDocument, type CanvasDocumentOmitOptions } from '@pixerate/editor/canvas';
2
3
  import type { CanvasNode, CanvasEdge, GraphDifferences } from './types.js';
4
+ export { applyGraphDifferences, sanitizeCanvasDocument };
3
5
  /**
4
6
  * Pure calculation of graph differences between a current state and a baseline state.
5
7
  */
@@ -9,7 +11,7 @@ export declare function calculateGraphDifferences<TNode extends CanvasNode = Can
9
11
  }, baselineState: {
10
12
  nodes?: TNode[];
11
13
  edges?: TEdge[];
12
- }): GraphDifferences<TNode, TEdge>;
14
+ }, options?: CanvasDocumentOmitOptions): GraphDifferences<TNode, TEdge>;
13
15
  /**
14
16
  * Pure function to replace a node in a graph, optionally preserving its connected edges.
15
17
  */
@@ -1,32 +1,11 @@
1
1
  import { cubicOut } from 'svelte/easing';
2
+ import { applyGraphDifferences, calculateGraphDifferences as calculateCoreGraphDifferences, sanitizeCanvasDocument } from '@pixerate/editor/canvas';
3
+ export { applyGraphDifferences, sanitizeCanvasDocument };
2
4
  /**
3
5
  * Pure calculation of graph differences between a current state and a baseline state.
4
6
  */
5
- export function calculateGraphDifferences(currentState, baselineState) {
6
- const currentNodes = currentState.nodes ?? [];
7
- const currentEdges = currentState.edges ?? [];
8
- const baselineNodes = baselineState.nodes ?? [];
9
- const baselineEdges = baselineState.edges ?? [];
10
- // Map baseline for fast O(1) lookup
11
- const baselineNodeMap = new Map(baselineNodes.map((n) => [n.id, n]));
12
- const baselineEdgeMap = new Map(baselineEdges.map((e) => [e.id, e]));
13
- const currentNodeMap = new Map(currentNodes.map((n) => [n.id, n]));
14
- const currentEdgeMap = new Map(currentEdges.map((e) => [e.id, e]));
15
- const removedNodes = baselineNodes.filter((oldNode) => !currentNodeMap.has(oldNode.id));
16
- const removedEdges = baselineEdges.filter((oldEdge) => !currentEdgeMap.has(oldEdge.id));
17
- const changedNodes = currentNodes.filter((newNode) => {
18
- const oldNode = baselineNodeMap.get(newNode.id);
19
- if (!oldNode)
20
- return true;
21
- return JSON.stringify(newNode) !== JSON.stringify(oldNode);
22
- });
23
- const changedEdges = currentEdges.filter((newEdge) => {
24
- const oldEdge = baselineEdgeMap.get(newEdge.id);
25
- if (!oldEdge)
26
- return true;
27
- return JSON.stringify(newEdge) !== JSON.stringify(oldEdge);
28
- });
29
- return { changedNodes, changedEdges, removedNodes, removedEdges };
7
+ export function calculateGraphDifferences(currentState, baselineState, options) {
8
+ return calculateCoreGraphDifferences(currentState, baselineState, options);
30
9
  }
31
10
  /**
32
11
  * Pure function to replace a node in a graph, optionally preserving its connected edges.
@@ -1,5 +1,5 @@
1
1
  export * from './types.js';
2
- export { createCanvasGraph, calculateGraphDifferences, replaceNodeInGraph, type CreateCanvasGraphOptions } from './createCanvasGraph.svelte.js';
2
+ export { createCanvasGraph, calculateGraphDifferences, applyGraphDifferences, sanitizeCanvasDocument, replaceNodeInGraph, type CreateCanvasGraphOptions } from './createCanvasGraph.svelte.js';
3
3
  export { getLayoutedNodes, getCenteredNodePosition, centerNodes, ensureLayout, placeBeside, estimateNodeSize, flowSide } from './createCanvasLayout.js';
4
4
  export { createCanvasClipboard, isEventFromTextInput, isValidCanvasNode, isValidUrl, type CreateCanvasClipboardOptions } from './createCanvasClipboard.svelte.js';
5
5
  export { createCanvasDocking, type CreateCanvasDockingOptions, type DockStrategy } from './createCanvasDocking.svelte.js';
@@ -1,7 +1,7 @@
1
1
  // Types
2
2
  export * from './types.js';
3
3
  // Graph management rune & pure functions
4
- export { createCanvasGraph, calculateGraphDifferences, replaceNodeInGraph } from './createCanvasGraph.svelte.js';
4
+ export { createCanvasGraph, calculateGraphDifferences, applyGraphDifferences, sanitizeCanvasDocument, replaceNodeInGraph } from './createCanvasGraph.svelte.js';
5
5
  // Layout calculations
6
6
  export { getLayoutedNodes, getCenteredNodePosition, centerNodes, ensureLayout, placeBeside, estimateNodeSize, flowSide } from './createCanvasLayout.js';
7
7
  // Clipboard rune & helpers
@@ -27,6 +27,12 @@ export interface CreateAutosaveOptions<T> {
27
27
  recover?: boolean;
28
28
  /** Write pending changes on `pagehide` and when the tab is hidden (default `true`). */
29
29
  flushOnHide?: boolean;
30
+ /**
31
+ * The server / host version of the loaded document, stored with each snapshot. The recovery
32
+ * check retires snapshots made on an older version instead of offering them, so they never
33
+ * overwrite newer server state.
34
+ */
35
+ baseVersion?: number | (() => number | undefined);
30
36
  }
31
37
  export interface SvelteAutosave<T> {
32
38
  /** Reactive status. */
@@ -20,6 +20,7 @@ export function createAutosave(options) {
20
20
  debounceMs: options.debounceMs,
21
21
  retryBaseMs: options.retryBaseMs,
22
22
  retryMaxMs: options.retryMaxMs,
23
+ baseVersion: options.baseVersion,
23
24
  });
24
25
  const recover = options.recover !== false;
25
26
  let status = $state.raw(controller.getStatus());
@@ -3,4 +3,5 @@ import type RecoveryPromptComponent from "./RecoveryPrompt.svelte";
3
3
  export { default as RecoveryPrompt } from "./RecoveryPrompt.svelte";
4
4
  export * from "./autosave.svelte.js";
5
5
  export * from "./editorAutosave.svelte.js";
6
+ export * from "./remoteSync.svelte.js";
6
7
  export type RecoveryPromptProps = ComponentProps<typeof RecoveryPromptComponent>;
@@ -1,3 +1,4 @@
1
1
  export { default as RecoveryPrompt } from "./RecoveryPrompt.svelte";
2
2
  export * from "./autosave.svelte.js";
3
3
  export * from "./editorAutosave.svelte.js";
4
+ export * from "./remoteSync.svelte.js";
@@ -0,0 +1,27 @@
1
+ import { type EditorDocument, type EditorRemoteSync, type EditorRemoteSyncOptions, type EditorRemoteSyncStatus } from "@pixerate/editor/persistence";
2
+ export interface CreateEditorSyncOptions<TDocument = unknown, TDiff = unknown> extends EditorRemoteSyncOptions<TDocument, TDiff> {
3
+ /**
4
+ * Flush pending debounced changes when the tab hides or unloads (`visibilitychange` to
5
+ * `"hidden"`, `pagehide`; default `true`).
6
+ */
7
+ flushOnHide?: boolean;
8
+ }
9
+ export interface SvelteEditorSync<TDocument = unknown, TDiff = unknown> {
10
+ readonly status: EditorRemoteSyncStatus;
11
+ readonly version: number;
12
+ readonly savedVersion: number;
13
+ readonly dirty: boolean;
14
+ readonly controller: EditorRemoteSync<TDocument, TDiff>;
15
+ baseline(): TDocument;
16
+ notify(): boolean;
17
+ applyRemote<TResult>(fn: () => TResult, remoteVersion?: number): TResult;
18
+ resetBaseline(remoteVersion?: number): void;
19
+ flush(): Promise<boolean>;
20
+ whenIdle(): Promise<void>;
21
+ destroy(): void;
22
+ }
23
+ /**
24
+ * Svelte 5 rune wrapping `createEditorRemoteSync`: subscribes to `doc` and exposes reactive
25
+ * `status`, `version`, `savedVersion`, and `dirty` fields, plus optional `flushOnHide` listener.
26
+ */
27
+ export declare function createEditorSync<TDocument = unknown, TDiff = unknown>(doc: EditorDocument<TDocument>, options: CreateEditorSyncOptions<TDocument, TDiff>): SvelteEditorSync<TDocument, TDiff>;
@@ -0,0 +1,56 @@
1
+ import { createEditorRemoteSync, } from "@pixerate/editor/persistence";
2
+ /**
3
+ * Svelte 5 rune wrapping `createEditorRemoteSync`: subscribes to `doc` and exposes reactive
4
+ * `status`, `version`, `savedVersion`, and `dirty` fields, plus optional `flushOnHide` listener.
5
+ */
6
+ export function createEditorSync(doc, options) {
7
+ const { flushOnHide = true, ...syncOptions } = options;
8
+ const controller = createEditorRemoteSync(doc, syncOptions);
9
+ let status = $state(controller.status());
10
+ const unsubscribe = controller.subscribe((next) => {
11
+ status = next;
12
+ });
13
+ const unsubscribers = [unsubscribe];
14
+ if (flushOnHide && typeof window !== "undefined") {
15
+ const onVisibilityChange = () => {
16
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
17
+ void controller.flush();
18
+ }
19
+ };
20
+ const onPageHide = () => {
21
+ void controller.flush();
22
+ };
23
+ if (typeof document !== "undefined") {
24
+ document.addEventListener("visibilitychange", onVisibilityChange);
25
+ unsubscribers.push(() => document.removeEventListener("visibilitychange", onVisibilityChange));
26
+ }
27
+ window.addEventListener("pagehide", onPageHide);
28
+ unsubscribers.push(() => window.removeEventListener("pagehide", onPageHide));
29
+ }
30
+ return {
31
+ get status() {
32
+ return status;
33
+ },
34
+ get version() {
35
+ return status.version;
36
+ },
37
+ get savedVersion() {
38
+ return status.savedVersion;
39
+ },
40
+ get dirty() {
41
+ return status.dirty;
42
+ },
43
+ controller,
44
+ baseline: () => controller.baseline(),
45
+ notify: () => controller.notify(),
46
+ applyRemote: (fn, remoteVersion) => controller.applyRemote(fn, remoteVersion),
47
+ resetBaseline: (remoteVersion) => controller.resetBaseline(remoteVersion),
48
+ flush: () => controller.flush(),
49
+ whenIdle: () => controller.whenIdle(),
50
+ destroy() {
51
+ unsubscribers.forEach((fn) => fn());
52
+ unsubscribers.length = 0;
53
+ controller.destroy();
54
+ },
55
+ };
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.207.1",
3
+ "version": "0.208.1",
4
4
  "description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -184,7 +184,7 @@
184
184
  "**/*.css"
185
185
  ],
186
186
  "dependencies": {
187
- "@pixerate/editor": "0.278.0"
187
+ "@pixerate/editor": "0.279.1"
188
188
  },
189
189
  "peerDependencies": {
190
190
  "@dagrejs/dagre": "^1.1.4",