@yuzhouu/canvas-kit 0.1.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/README.md +145 -0
- package/_vendor/canvas-core/bindings/BindingUtil.ts +92 -0
- package/_vendor/canvas-core/bindings/BindingUtilRegistry.ts +22 -0
- package/_vendor/canvas-core/bindings/arrow/arrowBindingHelpers.ts +52 -0
- package/_vendor/canvas-core/contextMenu.ts +85 -0
- package/_vendor/canvas-core/editor/Editor.ts +2614 -0
- package/_vendor/canvas-core/editor/HistoryManager.ts +83 -0
- package/_vendor/canvas-core/editor/StateNode.ts +162 -0
- package/_vendor/canvas-core/editor/camera.ts +6 -0
- package/_vendor/canvas-core/editor/clipboard.ts +478 -0
- package/_vendor/canvas-core/editor/commands.ts +833 -0
- package/_vendor/canvas-core/editor/edgeScroll.ts +37 -0
- package/_vendor/canvas-core/editor/editorComputedHelpers.ts +257 -0
- package/_vendor/canvas-core/editor/editorSelectionTransforms.ts +116 -0
- package/_vendor/canvas-core/editor/events.ts +93 -0
- package/_vendor/canvas-core/editor/index.ts +12 -0
- package/_vendor/canvas-core/editor/interactions.ts +101 -0
- package/_vendor/canvas-core/editor/internal.ts +22 -0
- package/_vendor/canvas-core/editor/managers/BindingManager.ts +262 -0
- package/_vendor/canvas-core/editor/managers/CameraManager.ts +147 -0
- package/_vendor/canvas-core/editor/managers/ClipboardManager.ts +32 -0
- package/_vendor/canvas-core/editor/managers/DocumentSnapshotManager.ts +256 -0
- package/_vendor/canvas-core/editor/managers/InputsManager.ts +319 -0
- package/_vendor/canvas-core/editor/managers/InteractionTransientManager.ts +218 -0
- package/_vendor/canvas-core/editor/managers/QueryManager.ts +837 -0
- package/_vendor/canvas-core/editor/managers/RenderWindowManager.ts +347 -0
- package/_vendor/canvas-core/editor/managers/SelectionLayoutManager.ts +196 -0
- package/_vendor/canvas-core/editor/managers/SelectionManager.ts +98 -0
- package/_vendor/canvas-core/editor/managers/SelectionOverlayManager.ts +308 -0
- package/_vendor/canvas-core/editor/managers/ShapeEditingManager.ts +226 -0
- package/_vendor/canvas-core/editor/managers/ShapeGeometryManager.ts +162 -0
- package/_vendor/canvas-core/editor/managers/ShapeHierarchyManager.ts +797 -0
- package/_vendor/canvas-core/editor/managers/SideEffectManager.ts +83 -0
- package/_vendor/canvas-core/editor/managers/SnapManager.ts +433 -0
- package/_vendor/canvas-core/editor/managers/SnapSnapshotBuilder.ts +430 -0
- package/_vendor/canvas-core/editor/managers/SpatialIndexManager.ts +354 -0
- package/_vendor/canvas-core/editor/managers/ToolManager.ts +81 -0
- package/_vendor/canvas-core/editor/managers/queryState.ts +583 -0
- package/_vendor/canvas-core/editor/managers/selectionGeometry.ts +287 -0
- package/_vendor/canvas-core/editor/managers/shapeOrder.ts +350 -0
- package/_vendor/canvas-core/editor/managers/shapeReparenting.ts +160 -0
- package/_vendor/canvas-core/editor/managers/snapCalculations.ts +412 -0
- package/_vendor/canvas-core/editor/managers/snapGeometry.ts +202 -0
- package/_vendor/canvas-core/editor/managers/snapIndicators.ts +403 -0
- package/_vendor/canvas-core/editor/managers/snapTypes.ts +122 -0
- package/_vendor/canvas-core/editor/queries/shapeTransforms.ts +171 -0
- package/_vendor/canvas-core/editor/renderWindow.ts +439 -0
- package/_vendor/canvas-core/editor/resizeGeometry.ts +654 -0
- package/_vendor/canvas-core/editor/selection.ts +489 -0
- package/_vendor/canvas-core/editor/selectionHandleInteractions.ts +43 -0
- package/_vendor/canvas-core/editor/selectionOverlay.ts +567 -0
- package/_vendor/canvas-core/exports/integrity.ts +174 -0
- package/_vendor/canvas-core/exports/svg.ts +494 -0
- package/_vendor/canvas-core/geometry/Box.ts +80 -0
- package/_vendor/canvas-core/geometry/Ellipse2d.ts +67 -0
- package/_vendor/canvas-core/geometry/Geometry2d.ts +52 -0
- package/_vendor/canvas-core/geometry/Mat.ts +92 -0
- package/_vendor/canvas-core/geometry/Polyline2d.ts +63 -0
- package/_vendor/canvas-core/geometry/Rectangle2d.ts +64 -0
- package/_vendor/canvas-core/geometry/Vec.ts +123 -0
- package/_vendor/canvas-core/geometry/index.ts +13 -0
- package/_vendor/canvas-core/geometry/segments.ts +65 -0
- package/_vendor/canvas-core/index.ts +391 -0
- package/_vendor/canvas-core/plugins/index.ts +5 -0
- package/_vendor/canvas-core/plugins/types.ts +53 -0
- package/_vendor/canvas-core/reactivity.ts +16 -0
- package/_vendor/canvas-core/schema/records.ts +221 -0
- package/_vendor/canvas-core/schema/snapshots.ts +527 -0
- package/_vendor/canvas-core/shapes/ShapeUtil.ts +516 -0
- package/_vendor/canvas-core/shapes/ShapeUtilRegistry.ts +26 -0
- package/_vendor/canvas-core/shapes/capabilities.ts +67 -0
- package/_vendor/canvas-core/shapes/types.ts +20 -0
- package/_vendor/canvas-core/toolbar.ts +148 -0
- package/_vendor/canvas-core/tools/hand/HandTool.ts +75 -0
- package/_vendor/canvas-core/tools/index.ts +2 -0
- package/_vendor/canvas-core/tools/select/SelectTool.ts +33 -0
- package/_vendor/canvas-core/tools/select/selectContainment.ts +104 -0
- package/_vendor/canvas-core/tools/select/selectToolHelpers.ts +801 -0
- package/_vendor/canvas-core/tools/select/states/BrushingState.ts +145 -0
- package/_vendor/canvas-core/tools/select/states/DraggingSelectionHandleInteractionState.ts +116 -0
- package/_vendor/canvas-core/tools/select/states/IdleState.ts +159 -0
- package/_vendor/canvas-core/tools/select/states/PointingCanvasState.ts +41 -0
- package/_vendor/canvas-core/tools/select/states/PointingResizeHandleState.ts +78 -0
- package/_vendor/canvas-core/tools/select/states/PointingRotateHandleState.ts +64 -0
- package/_vendor/canvas-core/tools/select/states/PointingSelectionHandleInteractionState.ts +34 -0
- package/_vendor/canvas-core/tools/select/states/PointingShapeState.ts +140 -0
- package/_vendor/canvas-core/tools/select/states/ResizingState.ts +171 -0
- package/_vendor/canvas-core/tools/select/states/RotatingState.ts +124 -0
- package/_vendor/canvas-core/tools/select/states/SelectStateNode.ts +128 -0
- package/_vendor/canvas-core/tools/select/states/TranslatingState.ts +139 -0
- package/_vendor/canvas-core/tools/select/states/index.ts +11 -0
- package/_vendor/canvas-plugin-api/feature.ts +204 -0
- package/_vendor/canvas-plugin-api/index.ts +142 -0
- package/_vendor/canvas-product-react/api/apiRegistry.ts +35 -0
- package/_vendor/canvas-product-react/api/canvasProductApi.ts +422 -0
- package/_vendor/canvas-product-react/api/index.ts +31 -0
- package/_vendor/canvas-product-react/capabilities/clipboard/clipboardService.ts +36 -0
- package/_vendor/canvas-product-react/capabilities/clipboard/systemClipboard.ts +245 -0
- package/_vendor/canvas-product-react/capabilities/context-menu/systemContextMenu.ts +396 -0
- package/_vendor/canvas-product-react/capabilities/context-menu/useContextMenuSurface.ts +59 -0
- package/_vendor/canvas-product-react/capabilities/export/useSvgExport.ts +156 -0
- package/_vendor/canvas-product-react/capabilities/history/useHistoryControls.ts +23 -0
- package/_vendor/canvas-product-react/capabilities/pages/usePageControls.ts +41 -0
- package/_vendor/canvas-product-react/capabilities/toolbar/systemToolbar.ts +490 -0
- package/_vendor/canvas-product-react/capabilities/toolbar/useToolbarSurfaces.ts +92 -0
- package/_vendor/canvas-product-react/capabilities/zoom/useZoomControls.ts +27 -0
- package/_vendor/canvas-product-react/index.ts +84 -0
- package/_vendor/canvas-product-react/persistence/index.ts +18 -0
- package/_vendor/canvas-product-react/persistence/localSnapshotPersistence.ts +294 -0
- package/_vendor/canvas-product-react/runtime/CanvasProductRoot.tsx +250 -0
- package/_vendor/canvas-product-react/runtime/productDefinition.ts +72 -0
- package/_vendor/canvas-product-react/runtime/productServices.ts +96 -0
- package/_vendor/canvas-react/components/CanvasView.tsx +796 -0
- package/_vendor/canvas-react/components/OverlayLayer.tsx +879 -0
- package/_vendor/canvas-react/components/PageSpace.tsx +34 -0
- package/_vendor/canvas-react/components/ShapeLayer.tsx +428 -0
- package/_vendor/canvas-react/components/index.ts +4 -0
- package/_vendor/canvas-react/hooks/useEditor.ts +11 -0
- package/_vendor/canvas-react/hooks/useValue.ts +14 -0
- package/_vendor/canvas-react/host/CanvasRendererRegistryContext.tsx +14 -0
- package/_vendor/canvas-react/host/contextMenuRegistry.ts +61 -0
- package/_vendor/canvas-react/host/domEventGuards.ts +61 -0
- package/_vendor/canvas-react/host/index.ts +38 -0
- package/_vendor/canvas-react/host/pluginHost.ts +635 -0
- package/_vendor/canvas-react/host/rendererRegistry.tsx +58 -0
- package/_vendor/canvas-react/host/toolbarRegistry.ts +100 -0
- package/_vendor/canvas-react/index.ts +3 -0
- package/_vendor/plugin-arrow/ArrowBindingUtil.ts +239 -0
- package/_vendor/plugin-arrow/ArrowShapeUtil.ts +311 -0
- package/_vendor/plugin-arrow/ArrowTool.ts +208 -0
- package/_vendor/plugin-arrow/arrowGeometry.ts +441 -0
- package/_vendor/plugin-arrow/arrowTerminalBindingResolver.ts +253 -0
- package/_vendor/plugin-arrow/arrowTerminalCommands.ts +230 -0
- package/_vendor/plugin-arrow/canvas.tsx +92 -0
- package/_vendor/plugin-arrow/core.ts +409 -0
- package/_vendor/plugin-arrow/feature.ts +20 -0
- package/_vendor/plugin-arrow/index.ts +28 -0
- package/_vendor/plugin-arrow/selectionHandleInteractions.ts +154 -0
- package/_vendor/plugin-arrow/types.ts +63 -0
- package/_vendor/plugin-draw/DrawShapeRenderer.tsx +60 -0
- package/_vendor/plugin-draw/DrawShapeUtil.ts +372 -0
- package/_vendor/plugin-draw/DrawTool.ts +444 -0
- package/_vendor/plugin-draw/canvas.tsx +24 -0
- package/_vendor/plugin-draw/core.ts +515 -0
- package/_vendor/plugin-draw/drawGeometry.ts +491 -0
- package/_vendor/plugin-draw/drawHitTest.ts +271 -0
- package/_vendor/plugin-draw/drawInterpolation.ts +250 -0
- package/_vendor/plugin-draw/drawPath.ts +550 -0
- package/_vendor/plugin-draw/drawSegments.ts +106 -0
- package/_vendor/plugin-draw/drawSelectionTransform.ts +67 -0
- package/_vendor/plugin-draw/drawShapeGeometry.ts +307 -0
- package/_vendor/plugin-draw/drawStrokeGeometry.ts +454 -0
- package/_vendor/plugin-draw/feature.ts +20 -0
- package/_vendor/plugin-draw/index.ts +31 -0
- package/_vendor/plugin-draw/types.ts +36 -0
- package/_vendor/plugin-eraser/EraserTool.ts +297 -0
- package/_vendor/plugin-eraser/canvas.tsx +616 -0
- package/_vendor/plugin-eraser/core.ts +49 -0
- package/_vendor/plugin-eraser/feature.ts +20 -0
- package/_vendor/plugin-eraser/index.ts +4 -0
- package/_vendor/plugin-frame/FrameShapeUtil.ts +173 -0
- package/_vendor/plugin-frame/FrameTool.ts +225 -0
- package/_vendor/plugin-frame/canvas.tsx +75 -0
- package/_vendor/plugin-frame/core.ts +534 -0
- package/_vendor/plugin-frame/feature.ts +20 -0
- package/_vendor/plugin-frame/index.ts +6 -0
- package/_vendor/plugin-geo/GeoShapeUtil.ts +463 -0
- package/_vendor/plugin-geo/GeoTools.ts +247 -0
- package/_vendor/plugin-geo/canvas.tsx +90 -0
- package/_vendor/plugin-geo/core.ts +495 -0
- package/_vendor/plugin-geo/feature.ts +20 -0
- package/_vendor/plugin-geo/index.ts +7 -0
- package/_vendor/plugin-geo/types.ts +27 -0
- package/_vendor/plugin-group/GroupShapeUtil.ts +59 -0
- package/_vendor/plugin-group/canvas.tsx +59 -0
- package/_vendor/plugin-group/core.ts +40 -0
- package/_vendor/plugin-group/feature.ts +20 -0
- package/_vendor/plugin-group/index.ts +5 -0
- package/_vendor/plugin-image/ImageShapeUtil.ts +95 -0
- package/_vendor/plugin-image/canvas.tsx +212 -0
- package/_vendor/plugin-image/core.ts +542 -0
- package/_vendor/plugin-image/crop.ts +770 -0
- package/_vendor/plugin-image/cropInteractions.ts +350 -0
- package/_vendor/plugin-image/cropSession.ts +38 -0
- package/_vendor/plugin-image/feature.ts +20 -0
- package/_vendor/plugin-image/index.ts +29 -0
- package/_vendor/plugin-image/types.ts +55 -0
- package/_vendor/plugin-image-product-react/ImageImportController.tsx +143 -0
- package/_vendor/plugin-image-product-react/imageAssetService.ts +52 -0
- package/_vendor/plugin-image-product-react/imageBlobStore.ts +315 -0
- package/_vendor/plugin-image-product-react/imageHostActions.ts +438 -0
- package/_vendor/plugin-image-product-react/imageProductApi.ts +88 -0
- package/_vendor/plugin-image-product-react/imageToolbarHostBridge.ts +48 -0
- package/_vendor/plugin-image-product-react/imageToolbarRuntime.ts +21 -0
- package/_vendor/plugin-image-product-react/index.ts +60 -0
- package/_vendor/plugin-image-product-react/snapshotGuards.ts +33 -0
- package/_vendor/plugin-image-product-react/svgImageAssetInliner.ts +54 -0
- package/_vendor/plugin-image-product-react/useImageImport.ts +64 -0
- package/_vendor/plugin-laser/LaserTool.ts +154 -0
- package/_vendor/plugin-laser/canvas.tsx +645 -0
- package/_vendor/plugin-laser/core.ts +49 -0
- package/_vendor/plugin-laser/feature.ts +20 -0
- package/_vendor/plugin-laser/index.ts +4 -0
- package/_vendor/plugin-text/ArrowLabelBindingUtil.ts +336 -0
- package/_vendor/plugin-text/GeoLabelBindingUtil.ts +391 -0
- package/_vendor/plugin-text/TextShapeSvgExporter.ts +101 -0
- package/_vendor/plugin-text/TextShapeUtil.ts +284 -0
- package/_vendor/plugin-text/TextTool.ts +228 -0
- package/_vendor/plugin-text/canvas.tsx +115 -0
- package/_vendor/plugin-text/core.ts +422 -0
- package/_vendor/plugin-text/feature.ts +20 -0
- package/_vendor/plugin-text/host/RichTextEditingBox.tsx +144 -0
- package/_vendor/plugin-text/host/RichTextToolbar.tsx +266 -0
- package/_vendor/plugin-text/host/TiptapTextEditor.tsx +191 -0
- package/_vendor/plugin-text/host/richTextRendering.tsx +49 -0
- package/_vendor/plugin-text/host/textDomSelection.ts +93 -0
- package/_vendor/plugin-text/host/textEditingBoxStyle.ts +36 -0
- package/_vendor/plugin-text/host/textEntryFocus.ts +307 -0
- package/_vendor/plugin-text/host/textFontLoader.ts +149 -0
- package/_vendor/plugin-text/host/textMeasurement.ts +258 -0
- package/_vendor/plugin-text/host/textMeasurementSync.ts +77 -0
- package/_vendor/plugin-text/host/textRangeSelection.ts +134 -0
- package/_vendor/plugin-text/host/textShapeRendererRuntime.tsx +89 -0
- package/_vendor/plugin-text/host/textShapeSync.ts +173 -0
- package/_vendor/plugin-text/host/tiptapTextEditorController.ts +148 -0
- package/_vendor/plugin-text/index.ts +18 -0
- package/_vendor/plugin-text/richTextUtils.ts +259 -0
- package/_vendor/plugin-text/textInteractions.ts +366 -0
- package/_vendor/plugin-text/textLabelHelpers.ts +235 -0
- package/_vendor/plugin-text/textShapeHelpers.ts +123 -0
- package/_vendor/plugin-text/textStyle.ts +96 -0
- package/_vendor/plugin-text/textTypes.ts +48 -0
- package/_vendor/signals/atom.ts +74 -0
- package/_vendor/signals/computed.ts +132 -0
- package/_vendor/signals/core.ts +130 -0
- package/_vendor/signals/effect.ts +117 -0
- package/_vendor/signals/index.ts +9 -0
- package/_vendor/signals/types.ts +17 -0
- package/_vendor/store/Store.ts +171 -0
- package/_vendor/store/diff.ts +229 -0
- package/_vendor/store/history.ts +177 -0
- package/_vendor/store/index.ts +32 -0
- package/_vendor/store/types.ts +37 -0
- package/api/canvasKitApi.d.ts +29 -0
- package/index.css +3 -0
- package/index.d.ts +16 -0
- package/index.mjs +1 -0
- package/internal-modules.d.ts +187 -0
- package/package.json +48 -0
- package/product/Shell.d.ts +19 -0
- package/product/chromeContext.d.ts +70 -0
- package/product/createCanvasKitEditor.d.ts +6 -0
- package/product/index.d.ts +7 -0
- package/product/persistence.d.ts +3 -0
- package/product/product.d.ts +22 -0
- package/product/useCanvasClipboardShortcuts.d.ts +6 -0
- package/product-iyBbEPIs.mjs +4 -0
- package/product.mjs +1 -0
- package/standard/extensions.d.ts +1 -0
- package/standard/index.d.ts +3 -0
- package/standard/standardApi.d.ts +91 -0
- package/standard-BmrxwqpJ.mjs +44 -0
- package/standard.mjs +1 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { ChangeSource } from "@land/store";
|
|
2
|
+
import {
|
|
3
|
+
type BindingId,
|
|
4
|
+
type BindingRecord,
|
|
5
|
+
type ShapeId,
|
|
6
|
+
type ShapeRecord,
|
|
7
|
+
} from "../../schema/records";
|
|
8
|
+
import type { Editor } from "../Editor";
|
|
9
|
+
import { getEditorStore } from "../internal";
|
|
10
|
+
import type { BindingUtilEffect } from "../../bindings/BindingUtil";
|
|
11
|
+
|
|
12
|
+
export interface ShapeChangeEntry {
|
|
13
|
+
before: ShapeRecord;
|
|
14
|
+
after: ShapeRecord;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class BindingManager {
|
|
18
|
+
constructor(private readonly editor: Editor) {}
|
|
19
|
+
|
|
20
|
+
getBindingsFromShape(shapeId: ShapeId): BindingRecord[] {
|
|
21
|
+
return this.editor.queries
|
|
22
|
+
.getBindingIdsFromShape(shapeId)
|
|
23
|
+
.map((id) => this.editor.queries.getBinding(id))
|
|
24
|
+
.filter((binding): binding is BindingRecord => !!binding);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getBindingsToShape(shapeId: ShapeId): BindingRecord[] {
|
|
28
|
+
return this.editor.queries
|
|
29
|
+
.getBindingIdsToShape(shapeId)
|
|
30
|
+
.map((id) => this.editor.queries.getBinding(id))
|
|
31
|
+
.filter((binding): binding is BindingRecord => !!binding);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getBindingsForShape(shapeId: ShapeId): BindingRecord[] {
|
|
35
|
+
return this.editor.queries
|
|
36
|
+
.getBindingIdsForShape(shapeId)
|
|
37
|
+
.map((id) => this.editor.queries.getBinding(id))
|
|
38
|
+
.filter((binding): binding is BindingRecord => !!binding);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getConflictingBindings(binding: BindingRecord): BindingRecord[] {
|
|
42
|
+
const exclusiveKey = this.getExclusiveBindingKey(binding);
|
|
43
|
+
if (!exclusiveKey) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.getAllBindings().filter(
|
|
48
|
+
(candidate) =>
|
|
49
|
+
candidate.id !== binding.id &&
|
|
50
|
+
candidate.type === binding.type &&
|
|
51
|
+
this.getExclusiveBindingKey(candidate) === exclusiveKey,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
canBind(binding: BindingRecord): boolean {
|
|
56
|
+
const fromShape = this.editor.getShape(binding.fromId);
|
|
57
|
+
const toShape = this.editor.getShape(binding.toId);
|
|
58
|
+
if (!fromShape || !toShape) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
this.editor.getBindingUtil(binding).canBind?.(
|
|
64
|
+
this.editor,
|
|
65
|
+
binding as never,
|
|
66
|
+
{
|
|
67
|
+
fromShape,
|
|
68
|
+
toShape,
|
|
69
|
+
},
|
|
70
|
+
) ?? true
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
syncCreatedBinding(binding: BindingRecord, source: ChangeSource): void {
|
|
75
|
+
const effect = this.editor
|
|
76
|
+
.getBindingUtil(binding)
|
|
77
|
+
.onBindingCreate?.(this.editor, binding as never);
|
|
78
|
+
this.applyEffect(effect, source, binding.id);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
syncUpdatedBinding(
|
|
82
|
+
before: BindingRecord,
|
|
83
|
+
after: BindingRecord,
|
|
84
|
+
source: ChangeSource,
|
|
85
|
+
): void {
|
|
86
|
+
const effect = this.editor
|
|
87
|
+
.getBindingUtil(after)
|
|
88
|
+
.onBindingUpdate?.(this.editor, before as never, after as never);
|
|
89
|
+
this.applyEffect(effect, source, after.id);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
repairLoadedBindings(source: ChangeSource): void {
|
|
93
|
+
getEditorStore(this.editor).atomic(() => {
|
|
94
|
+
const seenExclusiveBindings = new Set<string>();
|
|
95
|
+
|
|
96
|
+
for (const binding of this.getAllBindings()) {
|
|
97
|
+
const fromShape = this.editor.getShape(binding.fromId);
|
|
98
|
+
const toShape = this.editor.getShape(binding.toId);
|
|
99
|
+
if (!fromShape || !toShape) {
|
|
100
|
+
getEditorStore(this.editor).remove([binding.id], source);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!this.canBind(binding)) {
|
|
105
|
+
getEditorStore(this.editor).remove([binding.id], source);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const exclusiveKey = this.getExclusiveBindingKey(binding);
|
|
110
|
+
if (exclusiveKey) {
|
|
111
|
+
if (seenExclusiveBindings.has(exclusiveKey)) {
|
|
112
|
+
getEditorStore(this.editor).remove([binding.id], source);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
seenExclusiveBindings.add(exclusiveKey);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const effect = this.editor
|
|
119
|
+
.getBindingUtil(binding)
|
|
120
|
+
.onBindingLoad?.(this.editor, binding as never);
|
|
121
|
+
this.applyEffect(effect, source, binding.id);
|
|
122
|
+
}
|
|
123
|
+
}, source);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
handleShapeChanges(
|
|
127
|
+
changes: ShapeChangeEntry[],
|
|
128
|
+
source: ChangeSource,
|
|
129
|
+
options?: { skipBindingIds?: ReadonlySet<BindingId> },
|
|
130
|
+
): void {
|
|
131
|
+
if (changes.length === 0) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const change of changes) {
|
|
136
|
+
for (const binding of this.getBindingsForShape(change.after.id)) {
|
|
137
|
+
if (options?.skipBindingIds?.has(binding.id)) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const role = binding.fromId === change.after.id ? "from" : "to";
|
|
141
|
+
const effect = this.editor
|
|
142
|
+
.getBindingUtil(binding)
|
|
143
|
+
.onAfterShapeChange?.(this.editor, binding as never, {
|
|
144
|
+
before: change.before,
|
|
145
|
+
after: change.after,
|
|
146
|
+
role,
|
|
147
|
+
});
|
|
148
|
+
this.applyEffect(effect, source, binding.id);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
cleanupBindingsForDeletedShapes(ids: ShapeId[], source: ChangeSource): void {
|
|
154
|
+
if (ids.length === 0) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const deletingIds = new Set(ids);
|
|
159
|
+
const seenBindings = new Set<BindingId>();
|
|
160
|
+
|
|
161
|
+
for (const shapeId of ids) {
|
|
162
|
+
const shape = this.editor.getShape(shapeId);
|
|
163
|
+
if (!shape) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const binding of this.getBindingsForShape(shapeId)) {
|
|
168
|
+
if (seenBindings.has(binding.id)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
seenBindings.add(binding.id);
|
|
172
|
+
|
|
173
|
+
const role = binding.fromId === shapeId ? "from" : "to";
|
|
174
|
+
if (role === "to" && deletingIds.has(binding.fromId)) {
|
|
175
|
+
getEditorStore(this.editor).remove([binding.id], source);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const effect = this.editor
|
|
180
|
+
.getBindingUtil(binding)
|
|
181
|
+
.onBeforeDeleteShape?.(this.editor, binding as never, {
|
|
182
|
+
shape,
|
|
183
|
+
role,
|
|
184
|
+
});
|
|
185
|
+
this.applyEffect(effect, source, binding.id);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
cleanupBindings(ids: BindingId[], source: ChangeSource): void {
|
|
191
|
+
for (const id of ids) {
|
|
192
|
+
const binding = this.editor.getBinding(id);
|
|
193
|
+
if (!binding) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const effect = this.editor
|
|
197
|
+
.getBindingUtil(binding)
|
|
198
|
+
.onBeforeDeleteBinding?.(this.editor, binding as never);
|
|
199
|
+
this.applyEffect(effect, source, binding.id);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private applyEffect(
|
|
204
|
+
effect: BindingUtilEffect | void,
|
|
205
|
+
source: ChangeSource,
|
|
206
|
+
originBindingId?: BindingId,
|
|
207
|
+
): void {
|
|
208
|
+
if (!effect) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (effect.bindings && effect.bindings.length > 0) {
|
|
213
|
+
getEditorStore(this.editor).put(effect.bindings, source);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (effect.shapes && effect.shapes.length > 0) {
|
|
217
|
+
const changes = effect.shapes.flatMap((after) => {
|
|
218
|
+
const before = this.editor.getShape(after.id);
|
|
219
|
+
return before ? [{ before, after }] : [];
|
|
220
|
+
});
|
|
221
|
+
getEditorStore(this.editor).put(effect.shapes, source);
|
|
222
|
+
this.handleShapeChanges(
|
|
223
|
+
changes,
|
|
224
|
+
source,
|
|
225
|
+
originBindingId
|
|
226
|
+
? { skipBindingIds: new Set([originBindingId]) }
|
|
227
|
+
: undefined,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (effect.deleteShapeIds && effect.deleteShapeIds.length > 0) {
|
|
232
|
+
const idsWithDescendants = this.editor.getShapeIdsIncludingDescendants(
|
|
233
|
+
effect.deleteShapeIds,
|
|
234
|
+
);
|
|
235
|
+
this.cleanupBindingsForDeletedShapes(idsWithDescendants, source);
|
|
236
|
+
getEditorStore(this.editor).remove(
|
|
237
|
+
Array.from(new Set(idsWithDescendants)),
|
|
238
|
+
source,
|
|
239
|
+
);
|
|
240
|
+
this.editor.removeShapeIdsFromSelection(idsWithDescendants, source);
|
|
241
|
+
this.editor.removeShapeIdsFromEditingSession(idsWithDescendants, source);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (effect.deleteBindingIds && effect.deleteBindingIds.length > 0) {
|
|
245
|
+
getEditorStore(this.editor).remove(
|
|
246
|
+
Array.from(new Set(effect.deleteBindingIds)),
|
|
247
|
+
source,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private getAllBindings(): BindingRecord[] {
|
|
253
|
+
return this.editor.queries.getBindings();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private getExclusiveBindingKey(binding: BindingRecord): string | null {
|
|
257
|
+
const key = this.editor
|
|
258
|
+
.getBindingUtil(binding)
|
|
259
|
+
.getExclusiveBindingKey?.(binding as never);
|
|
260
|
+
return key ? `${binding.type}:${key}` : null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type Atom, type Signal, atom } from "@land/signals";
|
|
2
|
+
import type { ChangeSource } from "@land/store";
|
|
3
|
+
import { Vec } from "../../geometry/Vec";
|
|
4
|
+
import { type Camera, DEFAULT_CAMERA } from "../../schema/records";
|
|
5
|
+
import type { PageId } from "../../schema/records";
|
|
6
|
+
import type { Editor } from "../Editor";
|
|
7
|
+
import { clampCameraZoom } from "../camera";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Camera and viewport-screen-size state for the editor. Owns the viewport size
|
|
11
|
+
* atom (viewport + camera together define the page<->screen transform) and the
|
|
12
|
+
* camera read/write/transform operations carved out of the Editor facade so the
|
|
13
|
+
* editor can stay a thinner delegating surface. The Editor exposes thin wrappers
|
|
14
|
+
* that forward here and re-publishes {@link viewportScreenSizeSignal}.
|
|
15
|
+
*/
|
|
16
|
+
export class CameraManager {
|
|
17
|
+
private readonly _viewportScreenSizeSignal: Atom<{ w: number; h: number }> =
|
|
18
|
+
atom<{ w: number; h: number }>("editor.viewportScreenSize", { w: 0, h: 0 });
|
|
19
|
+
readonly viewportScreenSizeSignal: Signal<{ w: number; h: number }> =
|
|
20
|
+
this._viewportScreenSizeSignal;
|
|
21
|
+
|
|
22
|
+
constructor(private readonly editor: Editor) {}
|
|
23
|
+
|
|
24
|
+
getCurrentCamera(): Camera {
|
|
25
|
+
return this.editor.currentCameraSignal.get();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getCamera(pageId: PageId = this.editor.getCurrentPageId()): Camera {
|
|
29
|
+
return this.editor.sessionSignal.get().pageCameraById[pageId] ?? DEFAULT_CAMERA;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pageToScreen(point: Vec, pageId: PageId = this.editor.getCurrentPageId()): Vec {
|
|
33
|
+
const camera = this.getCamera(pageId);
|
|
34
|
+
return new (point.constructor as typeof Vec)(
|
|
35
|
+
(point.x - camera.x) * camera.z,
|
|
36
|
+
(point.y - camera.y) * camera.z,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
screenToPage(point: Vec, pageId: PageId = this.editor.getCurrentPageId()): Vec {
|
|
41
|
+
const camera = this.getCamera(pageId);
|
|
42
|
+
return new (point.constructor as typeof Vec)(
|
|
43
|
+
point.x / camera.z + camera.x,
|
|
44
|
+
point.y / camera.z + camera.y,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setCamera(
|
|
49
|
+
partial: Partial<Camera>,
|
|
50
|
+
options?: { pageId?: PageId; source?: ChangeSource },
|
|
51
|
+
): void {
|
|
52
|
+
const source = options?.source ?? "system";
|
|
53
|
+
const pageId = options?.pageId ?? this.editor.getCurrentPageId();
|
|
54
|
+
const current = this.getCamera(pageId);
|
|
55
|
+
const nextX = typeof partial.x === "number" ? partial.x : current.x;
|
|
56
|
+
const nextY = typeof partial.y === "number" ? partial.y : current.y;
|
|
57
|
+
const nextZInput = typeof partial.z === "number" ? partial.z : current.z;
|
|
58
|
+
const nextCamera: Camera = {
|
|
59
|
+
x: nextX,
|
|
60
|
+
y: nextY,
|
|
61
|
+
z: clampCameraZoom(nextZInput),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.editor.updateSession(
|
|
65
|
+
{
|
|
66
|
+
pageCameraById: {
|
|
67
|
+
...this.editor.sessionSignal.get().pageCameraById,
|
|
68
|
+
[pageId]: nextCamera,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
source,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
panCamera(delta: Vec, source: ChangeSource = "system"): void {
|
|
76
|
+
const camera = this.getCurrentCamera();
|
|
77
|
+
this.setCamera(
|
|
78
|
+
{
|
|
79
|
+
x: camera.x + delta.x / camera.z,
|
|
80
|
+
y: camera.y + delta.y / camera.z,
|
|
81
|
+
},
|
|
82
|
+
{ source },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
zoomCameraAtScreenPoint(
|
|
87
|
+
screenPoint: Vec,
|
|
88
|
+
deltaY: number,
|
|
89
|
+
source: ChangeSource = "system",
|
|
90
|
+
): void {
|
|
91
|
+
const current = this.getCurrentCamera();
|
|
92
|
+
const nextZoom = clampCameraZoom(current.z * Math.exp(-deltaY * 0.0015));
|
|
93
|
+
this.zoomCameraToScreenPoint(screenPoint, nextZoom, source);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
zoomCameraToScreenPoint(
|
|
97
|
+
screenPoint: Vec,
|
|
98
|
+
zoom: number,
|
|
99
|
+
source: ChangeSource = "system",
|
|
100
|
+
): void {
|
|
101
|
+
const nextZoom = clampCameraZoom(zoom);
|
|
102
|
+
const pagePoint = this.screenToPage(screenPoint);
|
|
103
|
+
|
|
104
|
+
this.setCamera(
|
|
105
|
+
{
|
|
106
|
+
x: pagePoint.x - screenPoint.x / nextZoom,
|
|
107
|
+
y: pagePoint.y - screenPoint.y / nextZoom,
|
|
108
|
+
z: nextZoom,
|
|
109
|
+
},
|
|
110
|
+
{ source },
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
zoomAtViewportCenter(deltaY: number, source: ChangeSource = "system"): void {
|
|
115
|
+
const viewport = this.viewportScreenSizeSignal.get();
|
|
116
|
+
const center = new Vec(viewport.w / 2, viewport.h / 2);
|
|
117
|
+
this.zoomCameraAtScreenPoint(center, deltaY, source);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
resetZoom(source: ChangeSource = "user"): void {
|
|
121
|
+
const viewport = this.viewportScreenSizeSignal.get();
|
|
122
|
+
const centerScreen = new Vec(viewport.w / 2, viewport.h / 2);
|
|
123
|
+
const centerPage = this.screenToPage(centerScreen);
|
|
124
|
+
this.editor.commands.setCamera(
|
|
125
|
+
{
|
|
126
|
+
camera: {
|
|
127
|
+
x: centerPage.x - centerScreen.x,
|
|
128
|
+
y: centerPage.y - centerScreen.y,
|
|
129
|
+
z: 1,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{ source },
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
setViewportScreenSize(size: { w: number; h: number }): void {
|
|
137
|
+
const nextSize = {
|
|
138
|
+
w: Math.max(0, size.w),
|
|
139
|
+
h: Math.max(0, size.h),
|
|
140
|
+
};
|
|
141
|
+
const current = this._viewportScreenSizeSignal.get();
|
|
142
|
+
if (current.w === nextSize.w && current.h === nextSize.h) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this._viewportScreenSizeSignal.set(nextSize);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ShapeId } from "../../schema/records";
|
|
2
|
+
import {
|
|
3
|
+
createEditorClipboardPayloadFromSelection,
|
|
4
|
+
duplicateEditorSelection,
|
|
5
|
+
pasteEditorClipboardPayload,
|
|
6
|
+
type DuplicateSelectionInput,
|
|
7
|
+
type EditorClipboardPayload,
|
|
8
|
+
type PasteClipboardPayloadInput,
|
|
9
|
+
} from "../clipboard";
|
|
10
|
+
import type { Editor } from "../Editor";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Clipboard operations over editor selections. The Editor exposes thin wrappers
|
|
14
|
+
* that forward here.
|
|
15
|
+
*/
|
|
16
|
+
export class ClipboardManager {
|
|
17
|
+
constructor(private readonly editor: Editor) {}
|
|
18
|
+
|
|
19
|
+
createPayloadFromSelection(): EditorClipboardPayload | null {
|
|
20
|
+
return createEditorClipboardPayloadFromSelection(this.editor);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pastePayload(input: PasteClipboardPayloadInput): ShapeId[] {
|
|
24
|
+
this.editor.cancelInteractionForCommand();
|
|
25
|
+
return pasteEditorClipboardPayload(this.editor, input);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
duplicateSelection(input: DuplicateSelectionInput = {}): ShapeId[] {
|
|
29
|
+
this.editor.cancelInteractionForCommand();
|
|
30
|
+
return duplicateEditorSelection(this.editor, input);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type { ChangeSource } from "@land/store";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_CAMERA,
|
|
4
|
+
DEFAULT_PAGE_ID,
|
|
5
|
+
DEFAULT_SESSION_ID,
|
|
6
|
+
createDefaultPage,
|
|
7
|
+
createDefaultSession,
|
|
8
|
+
isAssetRecord,
|
|
9
|
+
isBindingRecord,
|
|
10
|
+
isDocumentRecord,
|
|
11
|
+
isPageRecord,
|
|
12
|
+
isSessionRecord,
|
|
13
|
+
isShapeRecord,
|
|
14
|
+
type EditorRecord,
|
|
15
|
+
type PageId,
|
|
16
|
+
type SessionRecord,
|
|
17
|
+
type ShapeId,
|
|
18
|
+
} from "../../schema/records";
|
|
19
|
+
import {
|
|
20
|
+
SnapshotParseError,
|
|
21
|
+
parseDocumentSnapshot,
|
|
22
|
+
parseSessionSnapshot,
|
|
23
|
+
type DocumentSnapshot,
|
|
24
|
+
type SessionSnapshot,
|
|
25
|
+
} from "../../schema/snapshots";
|
|
26
|
+
import type { Editor } from "../Editor";
|
|
27
|
+
import { getShapePageIdFromParentChain } from "../editorComputedHelpers";
|
|
28
|
+
import { getEditorStore } from "../internal";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Document/session snapshot serialization, restore, and post-restore repair.
|
|
32
|
+
* The Editor exposes thin wrappers that forward here.
|
|
33
|
+
*/
|
|
34
|
+
export class DocumentSnapshotManager {
|
|
35
|
+
constructor(private readonly editor: Editor) {}
|
|
36
|
+
|
|
37
|
+
buildInitialRecords(
|
|
38
|
+
documentSnapshot: unknown,
|
|
39
|
+
sessionSnapshot: unknown,
|
|
40
|
+
): EditorRecord[] {
|
|
41
|
+
const parsedDocumentSnapshot =
|
|
42
|
+
this.parseInitialDocumentSnapshot(documentSnapshot);
|
|
43
|
+
const parsedSessionSnapshot =
|
|
44
|
+
this.parseInitialSessionSnapshot(sessionSnapshot);
|
|
45
|
+
|
|
46
|
+
const documentRecords = parsedDocumentSnapshot.records.length
|
|
47
|
+
? parsedDocumentSnapshot.records
|
|
48
|
+
: [createDefaultPage()];
|
|
49
|
+
const session = parsedSessionSnapshot.records[0] ?? createDefaultSession();
|
|
50
|
+
|
|
51
|
+
return [...documentRecords, session];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getDocumentSnapshot(): DocumentSnapshot {
|
|
55
|
+
const records = this.editor.store.getAll();
|
|
56
|
+
const pages = this.editor.getPages();
|
|
57
|
+
const assets = records
|
|
58
|
+
.filter(isAssetRecord)
|
|
59
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
60
|
+
const shapes = records.filter(isShapeRecord).sort((left, right) => {
|
|
61
|
+
if (left.parentId === right.parentId) {
|
|
62
|
+
return left.index.localeCompare(right.index);
|
|
63
|
+
}
|
|
64
|
+
return left.parentId.localeCompare(right.parentId);
|
|
65
|
+
});
|
|
66
|
+
const bindings = records
|
|
67
|
+
.filter(isBindingRecord)
|
|
68
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
schemaVersion: 1,
|
|
72
|
+
records: [...pages, ...assets, ...shapes, ...bindings],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getSessionSnapshot(): SessionSnapshot {
|
|
77
|
+
return {
|
|
78
|
+
schemaVersion: 1,
|
|
79
|
+
records: [this.editor.sessionSignal.get()],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
loadDocumentSnapshot(
|
|
84
|
+
snapshot: unknown,
|
|
85
|
+
options?: { source?: ChangeSource },
|
|
86
|
+
): void {
|
|
87
|
+
const source = options?.source ?? "system";
|
|
88
|
+
const parsed = parseDocumentSnapshot(
|
|
89
|
+
snapshot,
|
|
90
|
+
this.editor.shapeUtils,
|
|
91
|
+
this.editor.bindingUtils,
|
|
92
|
+
);
|
|
93
|
+
this.editor.cancelInteractionAndClearTransients("restore");
|
|
94
|
+
this.editor.editing.end({ runEditEnd: false, source });
|
|
95
|
+
const session = this.editor.sessionSignal.get();
|
|
96
|
+
getEditorStore(this.editor).replaceAll([...parsed.records, session], source);
|
|
97
|
+
this.syncAfterDocumentSnapshotLoad(source);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
loadSessionSnapshot(
|
|
101
|
+
snapshot: unknown,
|
|
102
|
+
options?: { source?: ChangeSource },
|
|
103
|
+
): void {
|
|
104
|
+
const source = options?.source ?? "system";
|
|
105
|
+
const session =
|
|
106
|
+
parseSessionSnapshot(snapshot).records[0] ?? createDefaultSession();
|
|
107
|
+
const documentRecords = getEditorStore(this.editor)
|
|
108
|
+
.getAll()
|
|
109
|
+
.filter(isDocumentRecord);
|
|
110
|
+
this.editor.cancelInteractionAndClearTransients("restore");
|
|
111
|
+
this.editor.editing.end({ runEditEnd: false, source });
|
|
112
|
+
getEditorStore(this.editor).replaceAll(
|
|
113
|
+
[...documentRecords, session],
|
|
114
|
+
source,
|
|
115
|
+
);
|
|
116
|
+
this.ensureSessionIntegrity();
|
|
117
|
+
this.editor.syncToolStateFromSession(source);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
syncAfterDocumentRestore(): void {
|
|
121
|
+
this.ensureSessionIntegrity();
|
|
122
|
+
this.editor.syncShapeCounter();
|
|
123
|
+
this.editor.syncPageCounter();
|
|
124
|
+
this.editor.syncBindingCounter();
|
|
125
|
+
this.editor.syncAssetCounter();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
ensureSessionIntegrity(): void {
|
|
129
|
+
const store = getEditorStore(this.editor);
|
|
130
|
+
const records = store.getAll();
|
|
131
|
+
const pages = records
|
|
132
|
+
.filter(isPageRecord)
|
|
133
|
+
.sort((left, right) => left.index.localeCompare(right.index));
|
|
134
|
+
const fallbackPageId = pages[0]?.id ?? DEFAULT_PAGE_ID;
|
|
135
|
+
const pageIds = new Set(pages.map((record) => record.id));
|
|
136
|
+
const shapes = records.filter(isShapeRecord);
|
|
137
|
+
const shapeIds = new Set(shapes.map((record) => record.id));
|
|
138
|
+
const shapeById = new Map(shapes.map((record) => [record.id, record]));
|
|
139
|
+
const shapePageById = new Map<ShapeId, PageId>();
|
|
140
|
+
for (const shape of shapes) {
|
|
141
|
+
shapePageById.set(
|
|
142
|
+
shape.id,
|
|
143
|
+
getShapePageIdFromParentChain(shape, shapeById, pageIds) ??
|
|
144
|
+
fallbackPageId,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const currentSession = records.find(
|
|
148
|
+
(record) => record.id === DEFAULT_SESSION_ID,
|
|
149
|
+
);
|
|
150
|
+
const session = isSessionRecord(currentSession)
|
|
151
|
+
? currentSession
|
|
152
|
+
: createDefaultSession(fallbackPageId);
|
|
153
|
+
|
|
154
|
+
const nextSession: SessionRecord = {
|
|
155
|
+
...session,
|
|
156
|
+
currentPageId: pageIds.has(session.currentPageId)
|
|
157
|
+
? session.currentPageId
|
|
158
|
+
: fallbackPageId,
|
|
159
|
+
pageCameraById: this.repairCameraByPage(session.pageCameraById, pageIds),
|
|
160
|
+
pageSelectedShapeIdsById: this.repairSelectionByPage(
|
|
161
|
+
session.pageSelectedShapeIdsById,
|
|
162
|
+
pageIds,
|
|
163
|
+
shapeIds,
|
|
164
|
+
shapePageById,
|
|
165
|
+
),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
store.put([nextSession], "system");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private syncAfterDocumentSnapshotLoad(source: ChangeSource): void {
|
|
172
|
+
this.syncAfterDocumentRestore();
|
|
173
|
+
this.editor.history.clear();
|
|
174
|
+
this.editor.bindings.repairLoadedBindings(source);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private repairCameraByPage(
|
|
178
|
+
cameraByPage: SessionRecord["pageCameraById"],
|
|
179
|
+
pageIds: Set<PageId>,
|
|
180
|
+
): SessionRecord["pageCameraById"] {
|
|
181
|
+
const nextCameraByPage: SessionRecord["pageCameraById"] = {};
|
|
182
|
+
|
|
183
|
+
for (const pageId of pageIds) {
|
|
184
|
+
const camera = cameraByPage[pageId] ?? DEFAULT_CAMERA;
|
|
185
|
+
nextCameraByPage[pageId] = {
|
|
186
|
+
x: Number.isFinite(camera.x) ? camera.x : DEFAULT_CAMERA.x,
|
|
187
|
+
y: Number.isFinite(camera.y) ? camera.y : DEFAULT_CAMERA.y,
|
|
188
|
+
z: Math.min(
|
|
189
|
+
8,
|
|
190
|
+
Math.max(
|
|
191
|
+
0.1,
|
|
192
|
+
Number.isFinite(camera.z) ? camera.z : DEFAULT_CAMERA.z,
|
|
193
|
+
),
|
|
194
|
+
),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return nextCameraByPage;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private repairSelectionByPage(
|
|
202
|
+
selectionByPage: SessionRecord["pageSelectedShapeIdsById"],
|
|
203
|
+
pageIds: Set<PageId>,
|
|
204
|
+
shapeIds: Set<ShapeId>,
|
|
205
|
+
shapePageById: Map<ShapeId, PageId>,
|
|
206
|
+
): SessionRecord["pageSelectedShapeIdsById"] {
|
|
207
|
+
const nextSelectionByPage: SessionRecord["pageSelectedShapeIdsById"] = {};
|
|
208
|
+
|
|
209
|
+
for (const pageId of pageIds) {
|
|
210
|
+
nextSelectionByPage[pageId] = Array.from(
|
|
211
|
+
new Set(
|
|
212
|
+
(selectionByPage[pageId] ?? []).filter(
|
|
213
|
+
(shapeId) =>
|
|
214
|
+
shapeIds.has(shapeId) && shapePageById.get(shapeId) === pageId,
|
|
215
|
+
),
|
|
216
|
+
),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return nextSelectionByPage;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private parseInitialDocumentSnapshot(snapshot: unknown): DocumentSnapshot {
|
|
224
|
+
if (!snapshot) {
|
|
225
|
+
return { schemaVersion: 1, records: [createDefaultPage()] };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
return parseDocumentSnapshot(
|
|
230
|
+
snapshot,
|
|
231
|
+
this.editor.shapeUtils,
|
|
232
|
+
this.editor.bindingUtils,
|
|
233
|
+
);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (!(error instanceof SnapshotParseError)) {
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
return { schemaVersion: 1, records: [createDefaultPage()] };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private parseInitialSessionSnapshot(snapshot: unknown): SessionSnapshot {
|
|
243
|
+
if (!snapshot) {
|
|
244
|
+
return { schemaVersion: 1, records: [createDefaultSession()] };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
return parseSessionSnapshot(snapshot);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (!(error instanceof SnapshotParseError)) {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
return { schemaVersion: 1, records: [createDefaultSession()] };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|