@lupinum/board-core 0.1.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-A7SXOVDB.js +350 -0
- package/dist/dom-attributes.d.ts +13 -0
- package/dist/engine/json-canvas-extras.d.ts +12 -0
- package/dist/engine/persistence.d.ts +3 -1
- package/dist/engine/public-engine.d.ts +8 -0
- package/dist/helpers/json.d.ts +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +384 -191
- package/dist/internal.d.ts +4 -1
- package/dist/internal.js +29 -3
- package/dist/state/types.d.ts +7 -1
- package/dist/types.d.ts +38 -21
- package/package.json +1 -1
- package/dist/chunk-5JZXHZWR.js +0 -68
package/dist/index.js
CHANGED
|
@@ -5,8 +5,15 @@ import {
|
|
|
5
5
|
BoardInputError,
|
|
6
6
|
BoardNotFoundError,
|
|
7
7
|
assertInternalBoardPlugin,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
collectJsonObjectExtras,
|
|
9
|
+
createJsonValueBudget,
|
|
10
|
+
freezeClone,
|
|
11
|
+
freezeJsonObject,
|
|
12
|
+
readonlyMapView,
|
|
13
|
+
readonlySetView,
|
|
14
|
+
registerBoardInteractionAdapter,
|
|
15
|
+
sameArray
|
|
16
|
+
} from "./chunk-A7SXOVDB.js";
|
|
10
17
|
|
|
11
18
|
// src/math.ts
|
|
12
19
|
function clamp(value, min, max) {
|
|
@@ -528,112 +535,6 @@ function snapPositionToEdges(bounds, otherEdges, threshold, excludeIds) {
|
|
|
528
535
|
return { dx, dy, guides };
|
|
529
536
|
}
|
|
530
537
|
|
|
531
|
-
// src/helpers/clone.ts
|
|
532
|
-
function freezeClone(value) {
|
|
533
|
-
if (Array.isArray(value)) {
|
|
534
|
-
for (const entry of value) {
|
|
535
|
-
freezeClone(entry);
|
|
536
|
-
}
|
|
537
|
-
return Object.freeze(value);
|
|
538
|
-
}
|
|
539
|
-
if (value && typeof value === "object") {
|
|
540
|
-
for (const child of Object.values(value)) {
|
|
541
|
-
freezeClone(child);
|
|
542
|
-
}
|
|
543
|
-
return Object.freeze(value);
|
|
544
|
-
}
|
|
545
|
-
return value;
|
|
546
|
-
}
|
|
547
|
-
function sameArray(a, b) {
|
|
548
|
-
if (a.length !== b.length) {
|
|
549
|
-
return false;
|
|
550
|
-
}
|
|
551
|
-
return a.every((value, index) => value === b[index]);
|
|
552
|
-
}
|
|
553
|
-
function readonlyMapView(source) {
|
|
554
|
-
let view;
|
|
555
|
-
view = Object.freeze({
|
|
556
|
-
get size() {
|
|
557
|
-
return source.size;
|
|
558
|
-
},
|
|
559
|
-
get: (key) => source.get(key),
|
|
560
|
-
has: (key) => source.has(key),
|
|
561
|
-
entries: () => source.entries(),
|
|
562
|
-
keys: () => source.keys(),
|
|
563
|
-
values: () => source.values(),
|
|
564
|
-
forEach: (callback, thisArg) => {
|
|
565
|
-
source.forEach((value, key) => callback.call(thisArg, value, key, view));
|
|
566
|
-
},
|
|
567
|
-
[Symbol.iterator]: () => source[Symbol.iterator]()
|
|
568
|
-
});
|
|
569
|
-
return view;
|
|
570
|
-
}
|
|
571
|
-
function readonlySetView(source) {
|
|
572
|
-
let view;
|
|
573
|
-
view = Object.freeze({
|
|
574
|
-
get size() {
|
|
575
|
-
return source.size;
|
|
576
|
-
},
|
|
577
|
-
has: (value) => source.has(value),
|
|
578
|
-
entries: () => source.entries(),
|
|
579
|
-
keys: () => source.keys(),
|
|
580
|
-
values: () => source.values(),
|
|
581
|
-
forEach: (callback, thisArg) => {
|
|
582
|
-
source.forEach((value) => callback.call(thisArg, value, value, view));
|
|
583
|
-
},
|
|
584
|
-
[Symbol.iterator]: () => source[Symbol.iterator](),
|
|
585
|
-
union: (other) => {
|
|
586
|
-
const result = new Set(source);
|
|
587
|
-
for (const value of iteratorValues(other.keys())) result.add(value);
|
|
588
|
-
return result;
|
|
589
|
-
},
|
|
590
|
-
intersection: (other) => {
|
|
591
|
-
const result = /* @__PURE__ */ new Set();
|
|
592
|
-
for (const value of source) {
|
|
593
|
-
if (other.has(value)) result.add(value);
|
|
594
|
-
}
|
|
595
|
-
return result;
|
|
596
|
-
},
|
|
597
|
-
difference: (other) => {
|
|
598
|
-
const result = /* @__PURE__ */ new Set();
|
|
599
|
-
for (const value of source) {
|
|
600
|
-
if (!other.has(value)) result.add(value);
|
|
601
|
-
}
|
|
602
|
-
return result;
|
|
603
|
-
},
|
|
604
|
-
symmetricDifference: (other) => {
|
|
605
|
-
const result = new Set(source);
|
|
606
|
-
for (const value of iteratorValues(other.keys())) {
|
|
607
|
-
if (source.has(value)) result.delete(value);
|
|
608
|
-
else result.add(value);
|
|
609
|
-
}
|
|
610
|
-
return result;
|
|
611
|
-
},
|
|
612
|
-
isSubsetOf: (other) => {
|
|
613
|
-
for (const value of source) if (!other.has(value)) return false;
|
|
614
|
-
return true;
|
|
615
|
-
},
|
|
616
|
-
isSupersetOf: (other) => {
|
|
617
|
-
for (const value of iteratorValues(other.keys())) {
|
|
618
|
-
if (!source.has(value)) return false;
|
|
619
|
-
}
|
|
620
|
-
return true;
|
|
621
|
-
},
|
|
622
|
-
isDisjointFrom: (other) => {
|
|
623
|
-
for (const value of source) if (other.has(value)) return false;
|
|
624
|
-
return true;
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
return view;
|
|
628
|
-
}
|
|
629
|
-
function* iteratorValues(iterator) {
|
|
630
|
-
while (true) {
|
|
631
|
-
const result = iterator.next();
|
|
632
|
-
if (result.done) return;
|
|
633
|
-
yield result.value;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
538
|
// src/helpers/ids.ts
|
|
638
539
|
function createNodeId() {
|
|
639
540
|
return crypto.randomUUID();
|
|
@@ -799,6 +700,7 @@ function validateState(state, grid, context) {
|
|
|
799
700
|
}
|
|
800
701
|
zIndexes.add(node.zIndex);
|
|
801
702
|
}
|
|
703
|
+
validateParentCycles(state, push);
|
|
802
704
|
for (const id of state.selection.values()) {
|
|
803
705
|
if (!state.nodes.has(id)) {
|
|
804
706
|
push("selection.exists", `Selected node ${id} does not exist.`);
|
|
@@ -855,28 +757,26 @@ function validateNodeParent(node, state, push) {
|
|
|
855
757
|
`Node ${node.id} parent must be type "group", got "${parent.type}".`
|
|
856
758
|
);
|
|
857
759
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
);
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
if (!walk.parentId) {
|
|
877
|
-
break;
|
|
760
|
+
}
|
|
761
|
+
function validateParentCycles(state, push) {
|
|
762
|
+
const complete = /* @__PURE__ */ new Set();
|
|
763
|
+
for (const start of state.nodes.keys()) {
|
|
764
|
+
if (complete.has(start)) continue;
|
|
765
|
+
const path = [];
|
|
766
|
+
const pathIndexes = /* @__PURE__ */ new Map();
|
|
767
|
+
let current = start;
|
|
768
|
+
while (current !== void 0 && !complete.has(current)) {
|
|
769
|
+
const cycleStart = pathIndexes.get(current);
|
|
770
|
+
if (cycleStart !== void 0) {
|
|
771
|
+
const cycle = [...path.slice(cycleStart), current].join(" -> ");
|
|
772
|
+
push("node.parentId", `Cycle detected in parent chain: ${cycle}.`);
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
pathIndexes.set(current, path.length);
|
|
776
|
+
path.push(current);
|
|
777
|
+
current = state.nodes.get(current)?.parentId;
|
|
878
778
|
}
|
|
879
|
-
|
|
779
|
+
for (const id of path) complete.add(id);
|
|
880
780
|
}
|
|
881
781
|
}
|
|
882
782
|
|
|
@@ -1593,7 +1493,11 @@ function stagePersistentRoots(roots) {
|
|
|
1593
1493
|
selection: new Set(roots.state.selection),
|
|
1594
1494
|
interaction: cloneInteraction(roots.state.interaction),
|
|
1595
1495
|
snapGuides: roots.state.snapGuides.map((guide) => ({ ...guide })),
|
|
1596
|
-
nextZIndex: roots.state.nextZIndex
|
|
1496
|
+
nextZIndex: roots.state.nextZIndex,
|
|
1497
|
+
jsonCanvas: {
|
|
1498
|
+
document: roots.state.jsonCanvas.document,
|
|
1499
|
+
nodes: new Map(roots.state.jsonCanvas.nodes)
|
|
1500
|
+
}
|
|
1597
1501
|
},
|
|
1598
1502
|
grid: { ...roots.grid },
|
|
1599
1503
|
pluginStates: new Map(
|
|
@@ -1639,6 +1543,32 @@ var JSON_CANVAS_SIDES = /* @__PURE__ */ new Set([
|
|
|
1639
1543
|
]);
|
|
1640
1544
|
var JSON_CANVAS_EDGE_ENDS = /* @__PURE__ */ new Set(["none", "arrow"]);
|
|
1641
1545
|
var JSON_CANVAS_BACKGROUND_STYLES = /* @__PURE__ */ new Set(["cover", "ratio", "repeat"]);
|
|
1546
|
+
var MAX_DOCUMENT_NODES = 1e4;
|
|
1547
|
+
var MAX_DOCUMENT_EDGES = 2e4;
|
|
1548
|
+
var MAX_DOCUMENT_SELECTION = 1e4;
|
|
1549
|
+
var LEGACY_BOARD_METADATA_KEY = "x-vue-board";
|
|
1550
|
+
var DOCUMENT_FIELDS = /* @__PURE__ */ new Set([
|
|
1551
|
+
"nodes",
|
|
1552
|
+
"edges",
|
|
1553
|
+
"x-lupinum-board",
|
|
1554
|
+
LEGACY_BOARD_METADATA_KEY
|
|
1555
|
+
]);
|
|
1556
|
+
var NODE_FIELDS = /* @__PURE__ */ new Set([
|
|
1557
|
+
"id",
|
|
1558
|
+
"type",
|
|
1559
|
+
"x",
|
|
1560
|
+
"y",
|
|
1561
|
+
"width",
|
|
1562
|
+
"height",
|
|
1563
|
+
"color",
|
|
1564
|
+
"text",
|
|
1565
|
+
"file",
|
|
1566
|
+
"subpath",
|
|
1567
|
+
"url",
|
|
1568
|
+
"label",
|
|
1569
|
+
"background",
|
|
1570
|
+
"backgroundStyle"
|
|
1571
|
+
]);
|
|
1642
1572
|
function isJsonCanvasNodeType(value) {
|
|
1643
1573
|
return typeof value === "string" && JSON_CANVAS_NODE_TYPES.has(value);
|
|
1644
1574
|
}
|
|
@@ -1716,8 +1646,9 @@ function jsonNodeToBoardNode(node, meta, index) {
|
|
|
1716
1646
|
};
|
|
1717
1647
|
return withNodeFields(base, node);
|
|
1718
1648
|
}
|
|
1719
|
-
function boardNodeToJsonNode(node) {
|
|
1649
|
+
function boardNodeToJsonNode(node, extras) {
|
|
1720
1650
|
const base = {
|
|
1651
|
+
...extras ?? {},
|
|
1721
1652
|
id: node.id,
|
|
1722
1653
|
type: node.type,
|
|
1723
1654
|
x: node.x,
|
|
@@ -1759,9 +1690,10 @@ function mergeMetadata(base, patch) {
|
|
|
1759
1690
|
};
|
|
1760
1691
|
}
|
|
1761
1692
|
function getDocumentMetadata(document) {
|
|
1762
|
-
|
|
1693
|
+
const record = document;
|
|
1694
|
+
return document["x-lupinum-board"] ?? record[LEGACY_BOARD_METADATA_KEY];
|
|
1763
1695
|
}
|
|
1764
|
-
function toPersistedDocument(snapshot, featureDocuments) {
|
|
1696
|
+
function toPersistedDocument(snapshot, featureDocuments, passthrough) {
|
|
1765
1697
|
let metadata = {
|
|
1766
1698
|
camera: snapshot.camera,
|
|
1767
1699
|
grid: snapshot.grid,
|
|
@@ -1787,13 +1719,18 @@ function toPersistedDocument(snapshot, featureDocuments) {
|
|
|
1787
1719
|
metadata = mergeMetadata(metadata, getDocumentMetadata(featureDocument));
|
|
1788
1720
|
}
|
|
1789
1721
|
return {
|
|
1790
|
-
|
|
1722
|
+
...passthrough.document,
|
|
1723
|
+
nodes: snapshot.nodes.map(
|
|
1724
|
+
(node) => boardNodeToJsonNode(node, passthrough.nodes.get(node.id))
|
|
1725
|
+
),
|
|
1791
1726
|
...edges !== void 0 ? { edges } : {},
|
|
1792
|
-
"x-
|
|
1727
|
+
"x-lupinum-board": metadata
|
|
1793
1728
|
};
|
|
1794
1729
|
}
|
|
1795
1730
|
function isRecord(value) {
|
|
1796
|
-
|
|
1731
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1732
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1733
|
+
return prototype === Object.prototype || prototype === null;
|
|
1797
1734
|
}
|
|
1798
1735
|
function assertRecord(value, message) {
|
|
1799
1736
|
if (!isRecord(value)) {
|
|
@@ -1925,13 +1862,27 @@ function validateDocumentMetadata(metadata) {
|
|
|
1925
1862
|
}
|
|
1926
1863
|
}
|
|
1927
1864
|
function normalizeDocumentForImport(raw) {
|
|
1928
|
-
const
|
|
1865
|
+
const parsedRecord = assertRecord(
|
|
1929
1866
|
raw,
|
|
1930
1867
|
"Invalid board document: document must be an object."
|
|
1931
1868
|
);
|
|
1869
|
+
const parsed = parsedRecord;
|
|
1932
1870
|
if (!Array.isArray(parsed.nodes)) {
|
|
1933
1871
|
throw new BoardInputError("Invalid board document: missing nodes array.");
|
|
1934
1872
|
}
|
|
1873
|
+
if (parsed.nodes.length > MAX_DOCUMENT_NODES) {
|
|
1874
|
+
throw new BoardInputError(
|
|
1875
|
+
`Invalid board document: nodes exceed the supported limit of ${MAX_DOCUMENT_NODES}.`
|
|
1876
|
+
);
|
|
1877
|
+
}
|
|
1878
|
+
if (parsed.edges !== void 0 && !Array.isArray(parsed.edges)) {
|
|
1879
|
+
throw new BoardInputError("Invalid board document: edges must be an array.");
|
|
1880
|
+
}
|
|
1881
|
+
if (parsed.edges && parsed.edges.length > MAX_DOCUMENT_EDGES) {
|
|
1882
|
+
throw new BoardInputError(
|
|
1883
|
+
`Invalid board document: edges exceed the supported limit of ${MAX_DOCUMENT_EDGES}.`
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1935
1886
|
for (const key of [
|
|
1936
1887
|
"camera",
|
|
1937
1888
|
"grid",
|
|
@@ -1942,11 +1893,35 @@ function normalizeDocumentForImport(raw) {
|
|
|
1942
1893
|
]) {
|
|
1943
1894
|
if (key in parsed) {
|
|
1944
1895
|
throw new BoardInputError(
|
|
1945
|
-
`Invalid board document: runtime field "${key}" belongs under x-
|
|
1896
|
+
`Invalid board document: runtime field "${key}" belongs under x-lupinum-board.`
|
|
1946
1897
|
);
|
|
1947
1898
|
}
|
|
1948
1899
|
}
|
|
1949
|
-
|
|
1900
|
+
const rawMetadata = getDocumentMetadata(parsed);
|
|
1901
|
+
if (isRecord(rawMetadata)) {
|
|
1902
|
+
if (Array.isArray(rawMetadata.selection) && rawMetadata.selection.length > MAX_DOCUMENT_SELECTION) {
|
|
1903
|
+
throw new BoardInputError(
|
|
1904
|
+
`Invalid board document: selection exceeds the supported limit of ${MAX_DOCUMENT_SELECTION}.`
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
if (isRecord(rawMetadata.nodes) && Reflect.ownKeys(rawMetadata.nodes).length > MAX_DOCUMENT_NODES) {
|
|
1908
|
+
throw new BoardInputError(
|
|
1909
|
+
`Invalid board document: node metadata exceeds the supported limit of ${MAX_DOCUMENT_NODES}.`
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
if (isRecord(rawMetadata.edges) && Reflect.ownKeys(rawMetadata.edges).length > MAX_DOCUMENT_EDGES) {
|
|
1913
|
+
throw new BoardInputError(
|
|
1914
|
+
`Invalid board document: edge metadata exceeds the supported limit of ${MAX_DOCUMENT_EDGES}.`
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
validateDocumentMetadata(rawMetadata);
|
|
1919
|
+
const jsonBudget = createJsonValueBudget();
|
|
1920
|
+
const metadata = rawMetadata === void 0 ? void 0 : freezeJsonObject(
|
|
1921
|
+
rawMetadata,
|
|
1922
|
+
"Invalid board document: board metadata",
|
|
1923
|
+
jsonBudget
|
|
1924
|
+
);
|
|
1950
1925
|
const seenNodes = /* @__PURE__ */ new Set();
|
|
1951
1926
|
const nodes = parsed.nodes.map((node) => {
|
|
1952
1927
|
if (!isRecord(node)) {
|
|
@@ -1969,7 +1944,11 @@ function normalizeDocumentForImport(raw) {
|
|
|
1969
1944
|
`Invalid board document: node "${String(node.id)}" has unsupported backgroundStyle "${String(node.backgroundStyle)}".`
|
|
1970
1945
|
);
|
|
1971
1946
|
}
|
|
1972
|
-
const normalized =
|
|
1947
|
+
const normalized = freezeJsonObject(
|
|
1948
|
+
node,
|
|
1949
|
+
`Invalid board document: node "${String(node.id ?? "?")}"`,
|
|
1950
|
+
jsonBudget
|
|
1951
|
+
);
|
|
1973
1952
|
validateJsonCanvasNodeFields(normalized);
|
|
1974
1953
|
if (seenNodes.has(normalized.id)) {
|
|
1975
1954
|
throw new BoardInputError(
|
|
@@ -1981,11 +1960,6 @@ function normalizeDocumentForImport(raw) {
|
|
|
1981
1960
|
});
|
|
1982
1961
|
let edges;
|
|
1983
1962
|
if (parsed.edges !== void 0) {
|
|
1984
|
-
if (!Array.isArray(parsed.edges)) {
|
|
1985
|
-
throw new BoardInputError(
|
|
1986
|
-
"Invalid board document: edges must be an array."
|
|
1987
|
-
);
|
|
1988
|
-
}
|
|
1989
1963
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
1990
1964
|
edges = parsed.edges.map((edge) => {
|
|
1991
1965
|
if (!isRecord(edge)) {
|
|
@@ -2037,13 +2011,43 @@ function normalizeDocumentForImport(raw) {
|
|
|
2037
2011
|
`Invalid board document: edge "${id}" has invalid label.`
|
|
2038
2012
|
);
|
|
2039
2013
|
}
|
|
2040
|
-
return
|
|
2014
|
+
return freezeJsonObject(
|
|
2015
|
+
{ ...edge, id, fromNode, toNode },
|
|
2016
|
+
`Invalid board document: edge "${id}"`,
|
|
2017
|
+
jsonBudget
|
|
2018
|
+
);
|
|
2041
2019
|
});
|
|
2042
2020
|
}
|
|
2043
2021
|
return {
|
|
2022
|
+
...collectJsonObjectExtras(
|
|
2023
|
+
parsedRecord,
|
|
2024
|
+
DOCUMENT_FIELDS,
|
|
2025
|
+
"Invalid board document",
|
|
2026
|
+
jsonBudget
|
|
2027
|
+
),
|
|
2044
2028
|
nodes,
|
|
2045
2029
|
...edges !== void 0 ? { edges } : {},
|
|
2046
|
-
...
|
|
2030
|
+
...metadata !== void 0 ? { "x-lupinum-board": metadata } : {}
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
function extractJsonCanvasPassthrough(document) {
|
|
2034
|
+
const record = document;
|
|
2035
|
+
return {
|
|
2036
|
+
document: collectJsonObjectExtras(
|
|
2037
|
+
record,
|
|
2038
|
+
DOCUMENT_FIELDS,
|
|
2039
|
+
"Invalid board document"
|
|
2040
|
+
),
|
|
2041
|
+
nodes: new Map(
|
|
2042
|
+
document.nodes.map((node) => [
|
|
2043
|
+
node.id,
|
|
2044
|
+
collectJsonObjectExtras(
|
|
2045
|
+
node,
|
|
2046
|
+
NODE_FIELDS,
|
|
2047
|
+
`Invalid board document: node "${node.id}"`
|
|
2048
|
+
)
|
|
2049
|
+
])
|
|
2050
|
+
)
|
|
2047
2051
|
};
|
|
2048
2052
|
}
|
|
2049
2053
|
function materializeSnapshotNodes(snapshot) {
|
|
@@ -2056,12 +2060,13 @@ function documentToSnapshot(document) {
|
|
|
2056
2060
|
jsonNodeToBoardNode(node, metadata?.nodes?.[node.id], index)
|
|
2057
2061
|
)
|
|
2058
2062
|
);
|
|
2063
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
2059
2064
|
const gridSettings = {
|
|
2060
2065
|
...DEFAULT_GRID,
|
|
2061
2066
|
...metadata?.grid ?? {}
|
|
2062
2067
|
};
|
|
2063
2068
|
const selection = Array.isArray(metadata?.selection) ? metadata.selection.filter(
|
|
2064
|
-
(id) => typeof id === "string" &&
|
|
2069
|
+
(id) => typeof id === "string" && nodeIds.has(id)
|
|
2065
2070
|
).map((id) => id) : [];
|
|
2066
2071
|
const nextZIndex = metadata?.nextZIndex ?? nodes.reduce((max, node) => Math.max(max, node.zIndex), 0) + 1;
|
|
2067
2072
|
const camera = { ...DEFAULT_CAMERA, ...metadata?.camera ?? {} };
|
|
@@ -2163,6 +2168,110 @@ function createCameraSession(deps) {
|
|
|
2163
2168
|
return { animateTo, cancelAnimations, computeFit };
|
|
2164
2169
|
}
|
|
2165
2170
|
|
|
2171
|
+
// src/engine/public-engine.ts
|
|
2172
|
+
function createPublicEngine(engine) {
|
|
2173
|
+
return {
|
|
2174
|
+
plugins: engine.plugins,
|
|
2175
|
+
$camera: engine.$camera,
|
|
2176
|
+
$grid: engine.$grid,
|
|
2177
|
+
$nodes: engine.$nodes,
|
|
2178
|
+
$selection: engine.$selection,
|
|
2179
|
+
$interaction: engine.$interaction,
|
|
2180
|
+
$snapGuides: engine.$snapGuides,
|
|
2181
|
+
destroy: engine.destroy,
|
|
2182
|
+
batch: engine.batch,
|
|
2183
|
+
getState: engine.getState,
|
|
2184
|
+
getGridSettings: engine.getGridSettings,
|
|
2185
|
+
getViewportSize: engine.getViewportSize,
|
|
2186
|
+
updateGridSettings: engine.updateGridSettings,
|
|
2187
|
+
setViewportSize: engine.setViewportSize,
|
|
2188
|
+
on: engine.on,
|
|
2189
|
+
once: engine.once,
|
|
2190
|
+
off: engine.off,
|
|
2191
|
+
exportTrace: engine.exportTrace,
|
|
2192
|
+
addCommandGuard: engine.addCommandGuard,
|
|
2193
|
+
screenToWorld: engine.screenToWorld,
|
|
2194
|
+
worldToScreen: engine.worldToScreen,
|
|
2195
|
+
getVisibleBounds: engine.getVisibleBounds,
|
|
2196
|
+
getNode: engine.getNode,
|
|
2197
|
+
findNode: engine.findNode,
|
|
2198
|
+
hasNode: engine.hasNode,
|
|
2199
|
+
getNodeAt: engine.getNodeAt,
|
|
2200
|
+
getNodesInBounds: engine.getNodesInBounds,
|
|
2201
|
+
panBy: engine.panBy,
|
|
2202
|
+
panTo: engine.panTo,
|
|
2203
|
+
zoomAt: engine.zoomAt,
|
|
2204
|
+
zoomTo: engine.zoomTo,
|
|
2205
|
+
zoomToFit: engine.zoomToFit,
|
|
2206
|
+
zoomToNodes: engine.zoomToNodes,
|
|
2207
|
+
createNode: engine.createNode,
|
|
2208
|
+
updateNode: engine.updateNode,
|
|
2209
|
+
deleteNode: engine.deleteNode,
|
|
2210
|
+
moveNode: engine.moveNode,
|
|
2211
|
+
translateSelectedNodes: engine.translateSelectedNodes,
|
|
2212
|
+
resizeNode: engine.resizeNode,
|
|
2213
|
+
bringToFront: engine.bringToFront,
|
|
2214
|
+
sendToBack: engine.sendToBack,
|
|
2215
|
+
lockNode: engine.lockNode,
|
|
2216
|
+
unlockNode: engine.unlockNode,
|
|
2217
|
+
duplicateNodes: engine.duplicateNodes,
|
|
2218
|
+
copySelected: engine.copySelected,
|
|
2219
|
+
pasteClipboard: engine.pasteClipboard,
|
|
2220
|
+
pasteData: engine.pasteData,
|
|
2221
|
+
select: engine.select,
|
|
2222
|
+
selectAll: engine.selectAll,
|
|
2223
|
+
clearSelection: engine.clearSelection,
|
|
2224
|
+
deleteSelected: engine.deleteSelected,
|
|
2225
|
+
getSelection: engine.getSelection,
|
|
2226
|
+
beginTextEdit: engine.beginTextEdit,
|
|
2227
|
+
commitTextEdit: engine.commitTextEdit,
|
|
2228
|
+
cancelTextEdit: engine.cancelTextEdit,
|
|
2229
|
+
exportDocument: engine.exportDocument,
|
|
2230
|
+
loadDocument: engine.loadDocument
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
// src/engine/json-canvas-extras.ts
|
|
2235
|
+
function removeNodeExtras(current, id) {
|
|
2236
|
+
if (!current.nodes.has(id)) return current;
|
|
2237
|
+
const nodes = new Map(current.nodes);
|
|
2238
|
+
nodes.delete(id);
|
|
2239
|
+
return { ...current, nodes };
|
|
2240
|
+
}
|
|
2241
|
+
function copyNodeExtras(current, idMap, source = current.nodes) {
|
|
2242
|
+
const nodes = new Map(current.nodes);
|
|
2243
|
+
for (const [sourceId, targetId] of idMap) {
|
|
2244
|
+
const extras = source.get(sourceId);
|
|
2245
|
+
if (extras) nodes.set(targetId, extras);
|
|
2246
|
+
}
|
|
2247
|
+
return { ...current, nodes };
|
|
2248
|
+
}
|
|
2249
|
+
function replaceJsonCanvasExtras(incoming) {
|
|
2250
|
+
return {
|
|
2251
|
+
document: incoming.document,
|
|
2252
|
+
nodes: new Map(incoming.nodes)
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
function mergeJsonCanvasExtras(current, incoming, idMap) {
|
|
2256
|
+
const document = Object.freeze({
|
|
2257
|
+
...current.document,
|
|
2258
|
+
...incoming.document
|
|
2259
|
+
});
|
|
2260
|
+
const nodes = new Map(current.nodes);
|
|
2261
|
+
for (const [id, extras] of incoming.nodes) {
|
|
2262
|
+
nodes.set(idMap.get(id) ?? id, extras);
|
|
2263
|
+
}
|
|
2264
|
+
return { document, nodes };
|
|
2265
|
+
}
|
|
2266
|
+
function selectNodeExtras(source, ids) {
|
|
2267
|
+
const selected = /* @__PURE__ */ new Map();
|
|
2268
|
+
for (const id of ids) {
|
|
2269
|
+
const extras = source.get(id);
|
|
2270
|
+
if (extras) selected.set(id, extras);
|
|
2271
|
+
}
|
|
2272
|
+
return selected;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2166
2275
|
// src/engine/node-shape.ts
|
|
2167
2276
|
function assertValidNodeGeometry(id, geometry) {
|
|
2168
2277
|
if (!Number.isFinite(geometry.x) || !Number.isFinite(geometry.y) || !Number.isFinite(geometry.width) || !Number.isFinite(geometry.height) || geometry.width <= 0 || geometry.height <= 0) {
|
|
@@ -2368,6 +2477,7 @@ function createBoardEngine(options = {}) {
|
|
|
2368
2477
|
const { emit, emitImmediate, on, once, off, reportUnhandledError } = eventBus;
|
|
2369
2478
|
const commandGuards = createCommandGuardRegistry();
|
|
2370
2479
|
const commitProjectors = /* @__PURE__ */ new Set();
|
|
2480
|
+
const projectedSubscribables = /* @__PURE__ */ new Set();
|
|
2371
2481
|
const nodeDeletedHooks = /* @__PURE__ */ new Set();
|
|
2372
2482
|
const pluginCleanups = /* @__PURE__ */ new Map();
|
|
2373
2483
|
let pluginStates = /* @__PURE__ */ new Map();
|
|
@@ -2376,6 +2486,7 @@ function createBoardEngine(options = {}) {
|
|
|
2376
2486
|
for (const hook of nodeDeletedHooks) hook(nodeId);
|
|
2377
2487
|
}
|
|
2378
2488
|
const clipboard = [];
|
|
2489
|
+
const clipboardPassthrough = /* @__PURE__ */ new Map();
|
|
2379
2490
|
const plugins = {};
|
|
2380
2491
|
let viewportSize = { ...DEFAULT_VIEWPORT_SIZE };
|
|
2381
2492
|
let destroyed = false;
|
|
@@ -2414,11 +2525,13 @@ function createBoardEngine(options = {}) {
|
|
|
2414
2525
|
selection: /* @__PURE__ */ new Set(),
|
|
2415
2526
|
interaction: { mode: "idle" },
|
|
2416
2527
|
snapGuides: [],
|
|
2417
|
-
nextZIndex: 1
|
|
2528
|
+
nextZIndex: 1,
|
|
2529
|
+
jsonCanvas: { document: Object.freeze({}), nodes: /* @__PURE__ */ new Map() }
|
|
2418
2530
|
};
|
|
2419
2531
|
const initialDocument = options.initialDocument ? normalizeDocumentForImport(options.initialDocument) : null;
|
|
2420
2532
|
if (initialDocument) {
|
|
2421
2533
|
const initial = documentToSnapshot(initialDocument);
|
|
2534
|
+
state.jsonCanvas = extractJsonCanvasPassthrough(initialDocument);
|
|
2422
2535
|
state.camera = { ...initial.camera };
|
|
2423
2536
|
state.selection = /* @__PURE__ */ new Set();
|
|
2424
2537
|
state.nextZIndex = initial.nextZIndex;
|
|
@@ -2518,6 +2631,7 @@ function createBoardEngine(options = {}) {
|
|
|
2518
2631
|
const checkpoint = {
|
|
2519
2632
|
roots,
|
|
2520
2633
|
clipboard: [...clipboard],
|
|
2634
|
+
clipboardPassthrough: new Map(clipboardPassthrough),
|
|
2521
2635
|
nodeOverrides: new Map(nodeOverrides),
|
|
2522
2636
|
activeGestureHistoryRoot,
|
|
2523
2637
|
activeBoxSelectionBefore
|
|
@@ -2533,6 +2647,10 @@ function createBoardEngine(options = {}) {
|
|
|
2533
2647
|
grid = checkpoint.roots.grid;
|
|
2534
2648
|
pluginStates = checkpoint.roots.pluginStates;
|
|
2535
2649
|
clipboard.splice(0, clipboard.length, ...checkpoint.clipboard);
|
|
2650
|
+
clipboardPassthrough.clear();
|
|
2651
|
+
for (const [id, extras] of checkpoint.clipboardPassthrough) {
|
|
2652
|
+
clipboardPassthrough.set(id, extras);
|
|
2653
|
+
}
|
|
2536
2654
|
nodeOverrides.clear();
|
|
2537
2655
|
for (const [id, node] of checkpoint.nodeOverrides) {
|
|
2538
2656
|
nodeOverrides.set(id, node);
|
|
@@ -2547,6 +2665,7 @@ function createBoardEngine(options = {}) {
|
|
|
2547
2665
|
grid: freezeClone({ ...grid }),
|
|
2548
2666
|
selection: state.selection,
|
|
2549
2667
|
nextZIndex: state.nextZIndex,
|
|
2668
|
+
jsonCanvas: state.jsonCanvas,
|
|
2550
2669
|
pluginSlices: new Map(
|
|
2551
2670
|
Array.from(pluginStates, ([name, pluginState]) => [
|
|
2552
2671
|
name,
|
|
@@ -2557,6 +2676,11 @@ function createBoardEngine(options = {}) {
|
|
|
2557
2676
|
}
|
|
2558
2677
|
function sameHistoryRoot(left, right) {
|
|
2559
2678
|
if (left.nextZIndex !== right.nextZIndex) return false;
|
|
2679
|
+
if (left.jsonCanvas.document !== right.jsonCanvas.document) return false;
|
|
2680
|
+
if (left.jsonCanvas.nodes.size !== right.jsonCanvas.nodes.size) return false;
|
|
2681
|
+
for (const [id, extras] of left.jsonCanvas.nodes) {
|
|
2682
|
+
if (right.jsonCanvas.nodes.get(id) !== extras) return false;
|
|
2683
|
+
}
|
|
2560
2684
|
if (left.grid.size !== right.grid.size || left.grid.majorEvery !== right.grid.majorEvery || left.grid.snap !== right.grid.snap || left.grid.edgeSnap !== right.grid.edgeSnap || left.grid.edgeSnapThreshold !== right.grid.edgeSnapThreshold || left.grid.pattern !== right.grid.pattern) {
|
|
2561
2685
|
return false;
|
|
2562
2686
|
}
|
|
@@ -2863,7 +2987,13 @@ function createBoardEngine(options = {}) {
|
|
|
2863
2987
|
Array.from(state.selection.values()).filter((id) => state.nodes.has(id))
|
|
2864
2988
|
);
|
|
2865
2989
|
}
|
|
2866
|
-
function
|
|
2990
|
+
function deleteNodePassthrough(id) {
|
|
2991
|
+
state.jsonCanvas = removeNodeExtras(state.jsonCanvas, id);
|
|
2992
|
+
}
|
|
2993
|
+
function copyNodePassthrough(idMap, source = state.jsonCanvas.nodes) {
|
|
2994
|
+
state.jsonCanvas = copyNodeExtras(state.jsonCanvas, idMap, source);
|
|
2995
|
+
}
|
|
2996
|
+
function restoreSnapshot(snapshot, mode, passthrough) {
|
|
2867
2997
|
const snapshotNodes = materializeSnapshotNodes(snapshot);
|
|
2868
2998
|
const idMap = /* @__PURE__ */ new Map();
|
|
2869
2999
|
if (mode === "replace") {
|
|
@@ -2874,6 +3004,7 @@ function createBoardEngine(options = {}) {
|
|
|
2874
3004
|
continue;
|
|
2875
3005
|
}
|
|
2876
3006
|
state.nodes.delete(id);
|
|
3007
|
+
deleteNodePassthrough(id);
|
|
2877
3008
|
notifyNodeDeletedPlugins(prevNode.id);
|
|
2878
3009
|
emit("node:deleted", id, materializeNode2(prevNode));
|
|
2879
3010
|
}
|
|
@@ -2884,6 +3015,7 @@ function createBoardEngine(options = {}) {
|
|
|
2884
3015
|
idMap.set(rawNode.id, node.id);
|
|
2885
3016
|
emit("node:created", materializeNode2(node));
|
|
2886
3017
|
}
|
|
3018
|
+
state.jsonCanvas = replaceJsonCanvasExtras(passthrough);
|
|
2887
3019
|
state.selection = new Set(
|
|
2888
3020
|
snapshot.selection.filter((id) => state.nodes.has(id))
|
|
2889
3021
|
);
|
|
@@ -2898,12 +3030,29 @@ function createBoardEngine(options = {}) {
|
|
|
2898
3030
|
notifySnapGuidesChanged();
|
|
2899
3031
|
return idMap;
|
|
2900
3032
|
}
|
|
3033
|
+
const reservedIds = new Set(state.nodes.keys());
|
|
3034
|
+
for (const rawNode of snapshotNodes) {
|
|
3035
|
+
let id = rawNode.id;
|
|
3036
|
+
while (reservedIds.has(id)) id = createNodeId();
|
|
3037
|
+
reservedIds.add(id);
|
|
3038
|
+
idMap.set(rawNode.id, id);
|
|
3039
|
+
}
|
|
2901
3040
|
for (const rawNode of snapshotNodes) {
|
|
2902
3041
|
const node = normalizeExistingNode(rawNode);
|
|
2903
|
-
const id =
|
|
2904
|
-
|
|
2905
|
-
|
|
3042
|
+
const id = idMap.get(node.id);
|
|
3043
|
+
const parentId = node.parentId ? idMap.get(node.parentId) ?? node.parentId : void 0;
|
|
3044
|
+
state.nodes.set(id, {
|
|
3045
|
+
...node,
|
|
3046
|
+
id,
|
|
3047
|
+
parentId,
|
|
3048
|
+
zIndex: state.nextZIndex++
|
|
3049
|
+
});
|
|
2906
3050
|
}
|
|
3051
|
+
state.jsonCanvas = mergeJsonCanvasExtras(
|
|
3052
|
+
state.jsonCanvas,
|
|
3053
|
+
passthrough,
|
|
3054
|
+
idMap
|
|
3055
|
+
);
|
|
2907
3056
|
notifyNodesChanged();
|
|
2908
3057
|
return idMap;
|
|
2909
3058
|
}
|
|
@@ -2998,6 +3147,8 @@ function createBoardEngine(options = {}) {
|
|
|
2998
3147
|
pluginStates.clear();
|
|
2999
3148
|
pluginPersistence.clear();
|
|
3000
3149
|
commitProjectors.clear();
|
|
3150
|
+
for (const projection of projectedSubscribables) projection.destroy();
|
|
3151
|
+
projectedSubscribables.clear();
|
|
3001
3152
|
nodeDeletedHooks.clear();
|
|
3002
3153
|
commandGuards.clear();
|
|
3003
3154
|
eventBus.clear();
|
|
@@ -3011,7 +3162,7 @@ function createBoardEngine(options = {}) {
|
|
|
3011
3162
|
},
|
|
3012
3163
|
extend(key, value) {
|
|
3013
3164
|
assertMutationAllowed();
|
|
3014
|
-
plugins
|
|
3165
|
+
Reflect.set(plugins, key, value);
|
|
3015
3166
|
},
|
|
3016
3167
|
batch(fn) {
|
|
3017
3168
|
assertCommandReady();
|
|
@@ -3120,6 +3271,27 @@ function createBoardEngine(options = {}) {
|
|
|
3120
3271
|
commitProjectors.add(projector);
|
|
3121
3272
|
return () => commitProjectors.delete(projector);
|
|
3122
3273
|
},
|
|
3274
|
+
createCommitSubscribable(select, channel) {
|
|
3275
|
+
assertAlive();
|
|
3276
|
+
assertMutationAllowed();
|
|
3277
|
+
let selected = select();
|
|
3278
|
+
const projection = createSubscribable(
|
|
3279
|
+
selected,
|
|
3280
|
+
createBatchController(),
|
|
3281
|
+
(error) => reportUnhandledError(error, { source: "subscriber", channel })
|
|
3282
|
+
);
|
|
3283
|
+
const projector = () => {
|
|
3284
|
+
const next = select();
|
|
3285
|
+
if (Object.is(next, selected)) return () => void 0;
|
|
3286
|
+
return () => {
|
|
3287
|
+
selected = next;
|
|
3288
|
+
projection.set(next);
|
|
3289
|
+
};
|
|
3290
|
+
};
|
|
3291
|
+
commitProjectors.add(projector);
|
|
3292
|
+
projectedSubscribables.add(projection);
|
|
3293
|
+
return projection;
|
|
3294
|
+
},
|
|
3123
3295
|
restoreHistoryRoot(root) {
|
|
3124
3296
|
runCommand(
|
|
3125
3297
|
"history:restore",
|
|
@@ -3132,6 +3304,10 @@ function createBoardEngine(options = {}) {
|
|
|
3132
3304
|
Array.from(root.selection).filter((id) => state.nodes.has(id))
|
|
3133
3305
|
);
|
|
3134
3306
|
state.nextZIndex = root.nextZIndex;
|
|
3307
|
+
state.jsonCanvas = {
|
|
3308
|
+
document: root.jsonCanvas.document,
|
|
3309
|
+
nodes: new Map(root.jsonCanvas.nodes)
|
|
3310
|
+
};
|
|
3135
3311
|
Object.assign(grid, root.grid);
|
|
3136
3312
|
for (const [name, pluginState] of pluginStates) {
|
|
3137
3313
|
if (root.pluginSlices.has(name)) {
|
|
@@ -3147,16 +3323,6 @@ function createBoardEngine(options = {}) {
|
|
|
3147
3323
|
IGNORE_COMMAND
|
|
3148
3324
|
);
|
|
3149
3325
|
},
|
|
3150
|
-
getPluginState() {
|
|
3151
|
-
throw new Error(
|
|
3152
|
-
"getPluginState is only available inside an internal plugin install() context."
|
|
3153
|
-
);
|
|
3154
|
-
},
|
|
3155
|
-
updatePluginState(_update) {
|
|
3156
|
-
throw new Error(
|
|
3157
|
-
"updatePluginState is only available inside an internal plugin install() context."
|
|
3158
|
-
);
|
|
3159
|
-
},
|
|
3160
3326
|
screenToWorld(point) {
|
|
3161
3327
|
assertAlive();
|
|
3162
3328
|
return screenToWorld(point, state.camera);
|
|
@@ -3344,6 +3510,7 @@ function createBoardEngine(options = {}) {
|
|
|
3344
3510
|
continue;
|
|
3345
3511
|
}
|
|
3346
3512
|
state.nodes.delete(deleteId);
|
|
3513
|
+
deleteNodePassthrough(deleteId);
|
|
3347
3514
|
notifyNodeDeletedPlugins(prevNode.id);
|
|
3348
3515
|
emit("node:deleted", deleteId, materializeNode2(prevNode));
|
|
3349
3516
|
}
|
|
@@ -3501,6 +3668,7 @@ function createBoardEngine(options = {}) {
|
|
|
3501
3668
|
const forest = forestIdsFromSeeds(ids);
|
|
3502
3669
|
const source = Array.from(forest).map((id) => state.nodes.get(id)).filter((node) => Boolean(node)).sort((a, b) => a.zIndex - b.zIndex);
|
|
3503
3670
|
const duplicated = duplicateForest2(source, offset);
|
|
3671
|
+
copyNodePassthrough(duplicated.idMap);
|
|
3504
3672
|
const created = duplicated.nodes;
|
|
3505
3673
|
for (const node of created) {
|
|
3506
3674
|
state.nodes.set(node.id, node);
|
|
@@ -3517,15 +3685,59 @@ function createBoardEngine(options = {}) {
|
|
|
3517
3685
|
copySelected() {
|
|
3518
3686
|
return runCommand("copySelected", [], () => {
|
|
3519
3687
|
clipboard.length = 0;
|
|
3520
|
-
|
|
3688
|
+
clipboardPassthrough.clear();
|
|
3689
|
+
const copied = getCopyClosureNodes2();
|
|
3690
|
+
for (const node of copied) {
|
|
3521
3691
|
clipboard.push({ ...node });
|
|
3522
3692
|
}
|
|
3693
|
+
for (const [id, extras] of selectNodeExtras(
|
|
3694
|
+
state.jsonCanvas.nodes,
|
|
3695
|
+
copied.map((node) => node.id)
|
|
3696
|
+
)) {
|
|
3697
|
+
clipboardPassthrough.set(id, extras);
|
|
3698
|
+
}
|
|
3523
3699
|
return clipboard.map((node) => materializeNode2(node));
|
|
3524
3700
|
});
|
|
3525
3701
|
},
|
|
3526
3702
|
pasteClipboard(offset = { x: grid.size, y: grid.size }) {
|
|
3527
3703
|
return runCommand("pasteClipboard", [offset], () => {
|
|
3528
|
-
const
|
|
3704
|
+
const duplicated = duplicateForest2(clipboard, offset);
|
|
3705
|
+
copyNodePassthrough(duplicated.idMap, clipboardPassthrough);
|
|
3706
|
+
const created = duplicated.nodes;
|
|
3707
|
+
for (const node of created) {
|
|
3708
|
+
state.nodes.set(node.id, node);
|
|
3709
|
+
emit("node:created", materializeNode2(node));
|
|
3710
|
+
}
|
|
3711
|
+
notifyNodesChanged();
|
|
3712
|
+
setSelection(created.map((node) => node.id));
|
|
3713
|
+
return created.map((node) => materializeNode2(node));
|
|
3714
|
+
});
|
|
3715
|
+
},
|
|
3716
|
+
pasteData(payload, offset = { x: grid.size, y: grid.size }) {
|
|
3717
|
+
return runCommand("pasteData", [payload, offset], () => {
|
|
3718
|
+
const inputs = options.clipboard?.deserialize(payload) ?? null;
|
|
3719
|
+
if (!inputs || inputs.length === 0) return null;
|
|
3720
|
+
const stagedNodes = new Map(state.nodes);
|
|
3721
|
+
let nextZIndex = state.nextZIndex;
|
|
3722
|
+
const created = inputs.map((input) => {
|
|
3723
|
+
const normalized = normalizeNodeInput(
|
|
3724
|
+
{
|
|
3725
|
+
...input,
|
|
3726
|
+
x: input.x ?? offset.x,
|
|
3727
|
+
y: input.y ?? offset.y
|
|
3728
|
+
},
|
|
3729
|
+
{
|
|
3730
|
+
nodes: stagedNodes,
|
|
3731
|
+
grid,
|
|
3732
|
+
constraints: nodeConstraints,
|
|
3733
|
+
nextZIndex
|
|
3734
|
+
}
|
|
3735
|
+
);
|
|
3736
|
+
nextZIndex = normalized.nextZIndex;
|
|
3737
|
+
stagedNodes.set(normalized.node.id, normalized.node);
|
|
3738
|
+
return normalized.node;
|
|
3739
|
+
});
|
|
3740
|
+
state.nextZIndex = nextZIndex;
|
|
3529
3741
|
for (const node of created) {
|
|
3530
3742
|
state.nodes.set(node.id, node);
|
|
3531
3743
|
emit("node:created", materializeNode2(node));
|
|
@@ -3600,6 +3812,7 @@ function createBoardEngine(options = {}) {
|
|
|
3600
3812
|
continue;
|
|
3601
3813
|
}
|
|
3602
3814
|
state.nodes.delete(deleteId);
|
|
3815
|
+
deleteNodePassthrough(deleteId);
|
|
3603
3816
|
notifyNodeDeletedPlugins(prevNode.id);
|
|
3604
3817
|
emit("node:deleted", deleteId, materializeNode2(prevNode));
|
|
3605
3818
|
}
|
|
@@ -4038,7 +4251,8 @@ function createBoardEngine(options = {}) {
|
|
|
4038
4251
|
);
|
|
4039
4252
|
return toPersistedDocument(
|
|
4040
4253
|
buildSnapshot(state, grid, buildPublicNodeMap(state)),
|
|
4041
|
-
pluginDocuments
|
|
4254
|
+
pluginDocuments,
|
|
4255
|
+
state.jsonCanvas
|
|
4042
4256
|
);
|
|
4043
4257
|
},
|
|
4044
4258
|
loadDocument(document, options2 = {}) {
|
|
@@ -4050,10 +4264,11 @@ function createBoardEngine(options = {}) {
|
|
|
4050
4264
|
const normalized = normalizeDocumentForImport(document);
|
|
4051
4265
|
assertCanRestoreDocument(normalized);
|
|
4052
4266
|
const snapshot = documentToSnapshot(normalized);
|
|
4267
|
+
const passthrough = extractJsonCanvasPassthrough(normalized);
|
|
4053
4268
|
nodeOverrides.clear();
|
|
4054
4269
|
activeGestureHistoryRoot = null;
|
|
4055
4270
|
activeBoxSelectionBefore = null;
|
|
4056
|
-
const idMap = restoreSnapshot(snapshot, mode);
|
|
4271
|
+
const idMap = restoreSnapshot(snapshot, mode, passthrough);
|
|
4057
4272
|
restorePluginDocuments(normalized, mode, idMap);
|
|
4058
4273
|
},
|
|
4059
4274
|
IGNORE_COMMAND
|
|
@@ -4075,29 +4290,7 @@ function createBoardEngine(options = {}) {
|
|
|
4075
4290
|
);
|
|
4076
4291
|
}
|
|
4077
4292
|
validate("createBoardEngine");
|
|
4078
|
-
const
|
|
4079
|
-
"emit",
|
|
4080
|
-
"assertActive",
|
|
4081
|
-
"isBatching",
|
|
4082
|
-
"extend",
|
|
4083
|
-
"runCommand",
|
|
4084
|
-
"projectCommit",
|
|
4085
|
-
"restoreHistoryRoot",
|
|
4086
|
-
"getPluginState",
|
|
4087
|
-
"updatePluginState",
|
|
4088
|
-
"beginPan",
|
|
4089
|
-
"beginNodeDrag",
|
|
4090
|
-
"beginResize",
|
|
4091
|
-
"beginBoxSelect",
|
|
4092
|
-
"updatePointer",
|
|
4093
|
-
"endInteraction",
|
|
4094
|
-
"cancelInteraction",
|
|
4095
|
-
"getUniformTranslationTargets",
|
|
4096
|
-
"syncGroupZOrder"
|
|
4097
|
-
]);
|
|
4098
|
-
const publicEngine = Object.fromEntries(
|
|
4099
|
-
Object.entries(engine).filter(([key]) => !internalKeys.has(key))
|
|
4100
|
-
);
|
|
4293
|
+
const publicEngine = createPublicEngine(engine);
|
|
4101
4294
|
registerBoardInteractionAdapter(publicEngine, engine);
|
|
4102
4295
|
return publicEngine;
|
|
4103
4296
|
}
|