@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.
Files changed (263) hide show
  1. package/README.md +145 -0
  2. package/_vendor/canvas-core/bindings/BindingUtil.ts +92 -0
  3. package/_vendor/canvas-core/bindings/BindingUtilRegistry.ts +22 -0
  4. package/_vendor/canvas-core/bindings/arrow/arrowBindingHelpers.ts +52 -0
  5. package/_vendor/canvas-core/contextMenu.ts +85 -0
  6. package/_vendor/canvas-core/editor/Editor.ts +2614 -0
  7. package/_vendor/canvas-core/editor/HistoryManager.ts +83 -0
  8. package/_vendor/canvas-core/editor/StateNode.ts +162 -0
  9. package/_vendor/canvas-core/editor/camera.ts +6 -0
  10. package/_vendor/canvas-core/editor/clipboard.ts +478 -0
  11. package/_vendor/canvas-core/editor/commands.ts +833 -0
  12. package/_vendor/canvas-core/editor/edgeScroll.ts +37 -0
  13. package/_vendor/canvas-core/editor/editorComputedHelpers.ts +257 -0
  14. package/_vendor/canvas-core/editor/editorSelectionTransforms.ts +116 -0
  15. package/_vendor/canvas-core/editor/events.ts +93 -0
  16. package/_vendor/canvas-core/editor/index.ts +12 -0
  17. package/_vendor/canvas-core/editor/interactions.ts +101 -0
  18. package/_vendor/canvas-core/editor/internal.ts +22 -0
  19. package/_vendor/canvas-core/editor/managers/BindingManager.ts +262 -0
  20. package/_vendor/canvas-core/editor/managers/CameraManager.ts +147 -0
  21. package/_vendor/canvas-core/editor/managers/ClipboardManager.ts +32 -0
  22. package/_vendor/canvas-core/editor/managers/DocumentSnapshotManager.ts +256 -0
  23. package/_vendor/canvas-core/editor/managers/InputsManager.ts +319 -0
  24. package/_vendor/canvas-core/editor/managers/InteractionTransientManager.ts +218 -0
  25. package/_vendor/canvas-core/editor/managers/QueryManager.ts +837 -0
  26. package/_vendor/canvas-core/editor/managers/RenderWindowManager.ts +347 -0
  27. package/_vendor/canvas-core/editor/managers/SelectionLayoutManager.ts +196 -0
  28. package/_vendor/canvas-core/editor/managers/SelectionManager.ts +98 -0
  29. package/_vendor/canvas-core/editor/managers/SelectionOverlayManager.ts +308 -0
  30. package/_vendor/canvas-core/editor/managers/ShapeEditingManager.ts +226 -0
  31. package/_vendor/canvas-core/editor/managers/ShapeGeometryManager.ts +162 -0
  32. package/_vendor/canvas-core/editor/managers/ShapeHierarchyManager.ts +797 -0
  33. package/_vendor/canvas-core/editor/managers/SideEffectManager.ts +83 -0
  34. package/_vendor/canvas-core/editor/managers/SnapManager.ts +433 -0
  35. package/_vendor/canvas-core/editor/managers/SnapSnapshotBuilder.ts +430 -0
  36. package/_vendor/canvas-core/editor/managers/SpatialIndexManager.ts +354 -0
  37. package/_vendor/canvas-core/editor/managers/ToolManager.ts +81 -0
  38. package/_vendor/canvas-core/editor/managers/queryState.ts +583 -0
  39. package/_vendor/canvas-core/editor/managers/selectionGeometry.ts +287 -0
  40. package/_vendor/canvas-core/editor/managers/shapeOrder.ts +350 -0
  41. package/_vendor/canvas-core/editor/managers/shapeReparenting.ts +160 -0
  42. package/_vendor/canvas-core/editor/managers/snapCalculations.ts +412 -0
  43. package/_vendor/canvas-core/editor/managers/snapGeometry.ts +202 -0
  44. package/_vendor/canvas-core/editor/managers/snapIndicators.ts +403 -0
  45. package/_vendor/canvas-core/editor/managers/snapTypes.ts +122 -0
  46. package/_vendor/canvas-core/editor/queries/shapeTransforms.ts +171 -0
  47. package/_vendor/canvas-core/editor/renderWindow.ts +439 -0
  48. package/_vendor/canvas-core/editor/resizeGeometry.ts +654 -0
  49. package/_vendor/canvas-core/editor/selection.ts +489 -0
  50. package/_vendor/canvas-core/editor/selectionHandleInteractions.ts +43 -0
  51. package/_vendor/canvas-core/editor/selectionOverlay.ts +567 -0
  52. package/_vendor/canvas-core/exports/integrity.ts +174 -0
  53. package/_vendor/canvas-core/exports/svg.ts +494 -0
  54. package/_vendor/canvas-core/geometry/Box.ts +80 -0
  55. package/_vendor/canvas-core/geometry/Ellipse2d.ts +67 -0
  56. package/_vendor/canvas-core/geometry/Geometry2d.ts +52 -0
  57. package/_vendor/canvas-core/geometry/Mat.ts +92 -0
  58. package/_vendor/canvas-core/geometry/Polyline2d.ts +63 -0
  59. package/_vendor/canvas-core/geometry/Rectangle2d.ts +64 -0
  60. package/_vendor/canvas-core/geometry/Vec.ts +123 -0
  61. package/_vendor/canvas-core/geometry/index.ts +13 -0
  62. package/_vendor/canvas-core/geometry/segments.ts +65 -0
  63. package/_vendor/canvas-core/index.ts +391 -0
  64. package/_vendor/canvas-core/plugins/index.ts +5 -0
  65. package/_vendor/canvas-core/plugins/types.ts +53 -0
  66. package/_vendor/canvas-core/reactivity.ts +16 -0
  67. package/_vendor/canvas-core/schema/records.ts +221 -0
  68. package/_vendor/canvas-core/schema/snapshots.ts +527 -0
  69. package/_vendor/canvas-core/shapes/ShapeUtil.ts +516 -0
  70. package/_vendor/canvas-core/shapes/ShapeUtilRegistry.ts +26 -0
  71. package/_vendor/canvas-core/shapes/capabilities.ts +67 -0
  72. package/_vendor/canvas-core/shapes/types.ts +20 -0
  73. package/_vendor/canvas-core/toolbar.ts +148 -0
  74. package/_vendor/canvas-core/tools/hand/HandTool.ts +75 -0
  75. package/_vendor/canvas-core/tools/index.ts +2 -0
  76. package/_vendor/canvas-core/tools/select/SelectTool.ts +33 -0
  77. package/_vendor/canvas-core/tools/select/selectContainment.ts +104 -0
  78. package/_vendor/canvas-core/tools/select/selectToolHelpers.ts +801 -0
  79. package/_vendor/canvas-core/tools/select/states/BrushingState.ts +145 -0
  80. package/_vendor/canvas-core/tools/select/states/DraggingSelectionHandleInteractionState.ts +116 -0
  81. package/_vendor/canvas-core/tools/select/states/IdleState.ts +159 -0
  82. package/_vendor/canvas-core/tools/select/states/PointingCanvasState.ts +41 -0
  83. package/_vendor/canvas-core/tools/select/states/PointingResizeHandleState.ts +78 -0
  84. package/_vendor/canvas-core/tools/select/states/PointingRotateHandleState.ts +64 -0
  85. package/_vendor/canvas-core/tools/select/states/PointingSelectionHandleInteractionState.ts +34 -0
  86. package/_vendor/canvas-core/tools/select/states/PointingShapeState.ts +140 -0
  87. package/_vendor/canvas-core/tools/select/states/ResizingState.ts +171 -0
  88. package/_vendor/canvas-core/tools/select/states/RotatingState.ts +124 -0
  89. package/_vendor/canvas-core/tools/select/states/SelectStateNode.ts +128 -0
  90. package/_vendor/canvas-core/tools/select/states/TranslatingState.ts +139 -0
  91. package/_vendor/canvas-core/tools/select/states/index.ts +11 -0
  92. package/_vendor/canvas-plugin-api/feature.ts +204 -0
  93. package/_vendor/canvas-plugin-api/index.ts +142 -0
  94. package/_vendor/canvas-product-react/api/apiRegistry.ts +35 -0
  95. package/_vendor/canvas-product-react/api/canvasProductApi.ts +422 -0
  96. package/_vendor/canvas-product-react/api/index.ts +31 -0
  97. package/_vendor/canvas-product-react/capabilities/clipboard/clipboardService.ts +36 -0
  98. package/_vendor/canvas-product-react/capabilities/clipboard/systemClipboard.ts +245 -0
  99. package/_vendor/canvas-product-react/capabilities/context-menu/systemContextMenu.ts +396 -0
  100. package/_vendor/canvas-product-react/capabilities/context-menu/useContextMenuSurface.ts +59 -0
  101. package/_vendor/canvas-product-react/capabilities/export/useSvgExport.ts +156 -0
  102. package/_vendor/canvas-product-react/capabilities/history/useHistoryControls.ts +23 -0
  103. package/_vendor/canvas-product-react/capabilities/pages/usePageControls.ts +41 -0
  104. package/_vendor/canvas-product-react/capabilities/toolbar/systemToolbar.ts +490 -0
  105. package/_vendor/canvas-product-react/capabilities/toolbar/useToolbarSurfaces.ts +92 -0
  106. package/_vendor/canvas-product-react/capabilities/zoom/useZoomControls.ts +27 -0
  107. package/_vendor/canvas-product-react/index.ts +84 -0
  108. package/_vendor/canvas-product-react/persistence/index.ts +18 -0
  109. package/_vendor/canvas-product-react/persistence/localSnapshotPersistence.ts +294 -0
  110. package/_vendor/canvas-product-react/runtime/CanvasProductRoot.tsx +250 -0
  111. package/_vendor/canvas-product-react/runtime/productDefinition.ts +72 -0
  112. package/_vendor/canvas-product-react/runtime/productServices.ts +96 -0
  113. package/_vendor/canvas-react/components/CanvasView.tsx +796 -0
  114. package/_vendor/canvas-react/components/OverlayLayer.tsx +879 -0
  115. package/_vendor/canvas-react/components/PageSpace.tsx +34 -0
  116. package/_vendor/canvas-react/components/ShapeLayer.tsx +428 -0
  117. package/_vendor/canvas-react/components/index.ts +4 -0
  118. package/_vendor/canvas-react/hooks/useEditor.ts +11 -0
  119. package/_vendor/canvas-react/hooks/useValue.ts +14 -0
  120. package/_vendor/canvas-react/host/CanvasRendererRegistryContext.tsx +14 -0
  121. package/_vendor/canvas-react/host/contextMenuRegistry.ts +61 -0
  122. package/_vendor/canvas-react/host/domEventGuards.ts +61 -0
  123. package/_vendor/canvas-react/host/index.ts +38 -0
  124. package/_vendor/canvas-react/host/pluginHost.ts +635 -0
  125. package/_vendor/canvas-react/host/rendererRegistry.tsx +58 -0
  126. package/_vendor/canvas-react/host/toolbarRegistry.ts +100 -0
  127. package/_vendor/canvas-react/index.ts +3 -0
  128. package/_vendor/plugin-arrow/ArrowBindingUtil.ts +239 -0
  129. package/_vendor/plugin-arrow/ArrowShapeUtil.ts +311 -0
  130. package/_vendor/plugin-arrow/ArrowTool.ts +208 -0
  131. package/_vendor/plugin-arrow/arrowGeometry.ts +441 -0
  132. package/_vendor/plugin-arrow/arrowTerminalBindingResolver.ts +253 -0
  133. package/_vendor/plugin-arrow/arrowTerminalCommands.ts +230 -0
  134. package/_vendor/plugin-arrow/canvas.tsx +92 -0
  135. package/_vendor/plugin-arrow/core.ts +409 -0
  136. package/_vendor/plugin-arrow/feature.ts +20 -0
  137. package/_vendor/plugin-arrow/index.ts +28 -0
  138. package/_vendor/plugin-arrow/selectionHandleInteractions.ts +154 -0
  139. package/_vendor/plugin-arrow/types.ts +63 -0
  140. package/_vendor/plugin-draw/DrawShapeRenderer.tsx +60 -0
  141. package/_vendor/plugin-draw/DrawShapeUtil.ts +372 -0
  142. package/_vendor/plugin-draw/DrawTool.ts +444 -0
  143. package/_vendor/plugin-draw/canvas.tsx +24 -0
  144. package/_vendor/plugin-draw/core.ts +515 -0
  145. package/_vendor/plugin-draw/drawGeometry.ts +491 -0
  146. package/_vendor/plugin-draw/drawHitTest.ts +271 -0
  147. package/_vendor/plugin-draw/drawInterpolation.ts +250 -0
  148. package/_vendor/plugin-draw/drawPath.ts +550 -0
  149. package/_vendor/plugin-draw/drawSegments.ts +106 -0
  150. package/_vendor/plugin-draw/drawSelectionTransform.ts +67 -0
  151. package/_vendor/plugin-draw/drawShapeGeometry.ts +307 -0
  152. package/_vendor/plugin-draw/drawStrokeGeometry.ts +454 -0
  153. package/_vendor/plugin-draw/feature.ts +20 -0
  154. package/_vendor/plugin-draw/index.ts +31 -0
  155. package/_vendor/plugin-draw/types.ts +36 -0
  156. package/_vendor/plugin-eraser/EraserTool.ts +297 -0
  157. package/_vendor/plugin-eraser/canvas.tsx +616 -0
  158. package/_vendor/plugin-eraser/core.ts +49 -0
  159. package/_vendor/plugin-eraser/feature.ts +20 -0
  160. package/_vendor/plugin-eraser/index.ts +4 -0
  161. package/_vendor/plugin-frame/FrameShapeUtil.ts +173 -0
  162. package/_vendor/plugin-frame/FrameTool.ts +225 -0
  163. package/_vendor/plugin-frame/canvas.tsx +75 -0
  164. package/_vendor/plugin-frame/core.ts +534 -0
  165. package/_vendor/plugin-frame/feature.ts +20 -0
  166. package/_vendor/plugin-frame/index.ts +6 -0
  167. package/_vendor/plugin-geo/GeoShapeUtil.ts +463 -0
  168. package/_vendor/plugin-geo/GeoTools.ts +247 -0
  169. package/_vendor/plugin-geo/canvas.tsx +90 -0
  170. package/_vendor/plugin-geo/core.ts +495 -0
  171. package/_vendor/plugin-geo/feature.ts +20 -0
  172. package/_vendor/plugin-geo/index.ts +7 -0
  173. package/_vendor/plugin-geo/types.ts +27 -0
  174. package/_vendor/plugin-group/GroupShapeUtil.ts +59 -0
  175. package/_vendor/plugin-group/canvas.tsx +59 -0
  176. package/_vendor/plugin-group/core.ts +40 -0
  177. package/_vendor/plugin-group/feature.ts +20 -0
  178. package/_vendor/plugin-group/index.ts +5 -0
  179. package/_vendor/plugin-image/ImageShapeUtil.ts +95 -0
  180. package/_vendor/plugin-image/canvas.tsx +212 -0
  181. package/_vendor/plugin-image/core.ts +542 -0
  182. package/_vendor/plugin-image/crop.ts +770 -0
  183. package/_vendor/plugin-image/cropInteractions.ts +350 -0
  184. package/_vendor/plugin-image/cropSession.ts +38 -0
  185. package/_vendor/plugin-image/feature.ts +20 -0
  186. package/_vendor/plugin-image/index.ts +29 -0
  187. package/_vendor/plugin-image/types.ts +55 -0
  188. package/_vendor/plugin-image-product-react/ImageImportController.tsx +143 -0
  189. package/_vendor/plugin-image-product-react/imageAssetService.ts +52 -0
  190. package/_vendor/plugin-image-product-react/imageBlobStore.ts +315 -0
  191. package/_vendor/plugin-image-product-react/imageHostActions.ts +438 -0
  192. package/_vendor/plugin-image-product-react/imageProductApi.ts +88 -0
  193. package/_vendor/plugin-image-product-react/imageToolbarHostBridge.ts +48 -0
  194. package/_vendor/plugin-image-product-react/imageToolbarRuntime.ts +21 -0
  195. package/_vendor/plugin-image-product-react/index.ts +60 -0
  196. package/_vendor/plugin-image-product-react/snapshotGuards.ts +33 -0
  197. package/_vendor/plugin-image-product-react/svgImageAssetInliner.ts +54 -0
  198. package/_vendor/plugin-image-product-react/useImageImport.ts +64 -0
  199. package/_vendor/plugin-laser/LaserTool.ts +154 -0
  200. package/_vendor/plugin-laser/canvas.tsx +645 -0
  201. package/_vendor/plugin-laser/core.ts +49 -0
  202. package/_vendor/plugin-laser/feature.ts +20 -0
  203. package/_vendor/plugin-laser/index.ts +4 -0
  204. package/_vendor/plugin-text/ArrowLabelBindingUtil.ts +336 -0
  205. package/_vendor/plugin-text/GeoLabelBindingUtil.ts +391 -0
  206. package/_vendor/plugin-text/TextShapeSvgExporter.ts +101 -0
  207. package/_vendor/plugin-text/TextShapeUtil.ts +284 -0
  208. package/_vendor/plugin-text/TextTool.ts +228 -0
  209. package/_vendor/plugin-text/canvas.tsx +115 -0
  210. package/_vendor/plugin-text/core.ts +422 -0
  211. package/_vendor/plugin-text/feature.ts +20 -0
  212. package/_vendor/plugin-text/host/RichTextEditingBox.tsx +144 -0
  213. package/_vendor/plugin-text/host/RichTextToolbar.tsx +266 -0
  214. package/_vendor/plugin-text/host/TiptapTextEditor.tsx +191 -0
  215. package/_vendor/plugin-text/host/richTextRendering.tsx +49 -0
  216. package/_vendor/plugin-text/host/textDomSelection.ts +93 -0
  217. package/_vendor/plugin-text/host/textEditingBoxStyle.ts +36 -0
  218. package/_vendor/plugin-text/host/textEntryFocus.ts +307 -0
  219. package/_vendor/plugin-text/host/textFontLoader.ts +149 -0
  220. package/_vendor/plugin-text/host/textMeasurement.ts +258 -0
  221. package/_vendor/plugin-text/host/textMeasurementSync.ts +77 -0
  222. package/_vendor/plugin-text/host/textRangeSelection.ts +134 -0
  223. package/_vendor/plugin-text/host/textShapeRendererRuntime.tsx +89 -0
  224. package/_vendor/plugin-text/host/textShapeSync.ts +173 -0
  225. package/_vendor/plugin-text/host/tiptapTextEditorController.ts +148 -0
  226. package/_vendor/plugin-text/index.ts +18 -0
  227. package/_vendor/plugin-text/richTextUtils.ts +259 -0
  228. package/_vendor/plugin-text/textInteractions.ts +366 -0
  229. package/_vendor/plugin-text/textLabelHelpers.ts +235 -0
  230. package/_vendor/plugin-text/textShapeHelpers.ts +123 -0
  231. package/_vendor/plugin-text/textStyle.ts +96 -0
  232. package/_vendor/plugin-text/textTypes.ts +48 -0
  233. package/_vendor/signals/atom.ts +74 -0
  234. package/_vendor/signals/computed.ts +132 -0
  235. package/_vendor/signals/core.ts +130 -0
  236. package/_vendor/signals/effect.ts +117 -0
  237. package/_vendor/signals/index.ts +9 -0
  238. package/_vendor/signals/types.ts +17 -0
  239. package/_vendor/store/Store.ts +171 -0
  240. package/_vendor/store/diff.ts +229 -0
  241. package/_vendor/store/history.ts +177 -0
  242. package/_vendor/store/index.ts +32 -0
  243. package/_vendor/store/types.ts +37 -0
  244. package/api/canvasKitApi.d.ts +29 -0
  245. package/index.css +3 -0
  246. package/index.d.ts +16 -0
  247. package/index.mjs +1 -0
  248. package/internal-modules.d.ts +187 -0
  249. package/package.json +48 -0
  250. package/product/Shell.d.ts +19 -0
  251. package/product/chromeContext.d.ts +70 -0
  252. package/product/createCanvasKitEditor.d.ts +6 -0
  253. package/product/index.d.ts +7 -0
  254. package/product/persistence.d.ts +3 -0
  255. package/product/product.d.ts +22 -0
  256. package/product/useCanvasClipboardShortcuts.d.ts +6 -0
  257. package/product-iyBbEPIs.mjs +4 -0
  258. package/product.mjs +1 -0
  259. package/standard/extensions.d.ts +1 -0
  260. package/standard/index.d.ts +3 -0
  261. package/standard/standardApi.d.ts +91 -0
  262. package/standard-BmrxwqpJ.mjs +44 -0
  263. package/standard.mjs +1 -0
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from "react";
2
+ import { useEditor } from "../hooks/useEditor";
3
+ import { useValue } from "../hooks/useValue";
4
+
5
+ export function PageSpace({
6
+ children,
7
+ className,
8
+ dataUi,
9
+ }: {
10
+ children: ReactNode;
11
+ className?: string;
12
+ dataUi: string;
13
+ }) {
14
+ const editor = useEditor();
15
+ const pageTransform = useValue(editor.pageTransformSignal);
16
+
17
+ return (
18
+ <div
19
+ data-ui={dataUi}
20
+ className={className}
21
+ style={{
22
+ position: "absolute",
23
+ left: 0,
24
+ top: 0,
25
+ width: "100%",
26
+ height: "100%",
27
+ transform: pageTransform.toCssString(),
28
+ transformOrigin: "0 0",
29
+ }}
30
+ >
31
+ {children}
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,428 @@
1
+ import { memo, type CSSProperties, type ReactNode } from "react";
2
+ import {
3
+ Box,
4
+ Mat,
5
+ Vec,
6
+ type Editor,
7
+ type ShapeId,
8
+ type ShapeRecord,
9
+ } from "@land/canvas-core";
10
+ import { useEditor } from "../hooks/useEditor";
11
+ import { useValue } from "../hooks/useValue";
12
+ import { PageSpace } from "./PageSpace";
13
+ import { useCanvasRendererRegistry } from "../host/CanvasRendererRegistryContext";
14
+
15
+ export function ShapeLayer() {
16
+ const showDebugSummary = shouldShowShapeLayerDebugSummary();
17
+
18
+ return (
19
+ <div
20
+ data-ui="shape-layer"
21
+ className="absolute inset-0"
22
+ style={{
23
+ position: "absolute",
24
+ left: 0,
25
+ top: 0,
26
+ width: "100%",
27
+ height: "100%",
28
+ }}
29
+ >
30
+ <PageSpace dataUi="shape-page-space" className="absolute inset-0">
31
+ <ShapeRenderList />
32
+ </PageSpace>
33
+ {showDebugSummary ? <ShapeLayerDebugSummary /> : null}
34
+ </div>
35
+ );
36
+ }
37
+
38
+ interface ShapeFrame {
39
+ left: number;
40
+ top: number;
41
+ width: number;
42
+ height: number;
43
+ transform: string;
44
+ transformOrigin: string;
45
+ elementToPage: Mat;
46
+ }
47
+
48
+ const ShapeRenderList = memo(function ShapeRenderList() {
49
+ const editor = useEditor();
50
+ const retainedShapeIds = useValue(editor.retainedShapeIdsSignal);
51
+
52
+ return retainedShapeIds.map((shapeId: ShapeId) => (
53
+ <ShapeItem key={shapeId} shapeId={shapeId} />
54
+ ));
55
+ });
56
+
57
+ const ShapeLayerDebugSummary = memo(function ShapeLayerDebugSummary() {
58
+ const editor = useEditor();
59
+ const pageShapeIds = useValue(editor.currentPageShapeIdsSignal);
60
+ const retainedShapeIds = useValue(editor.retainedShapeIdsSignal);
61
+ const visibleShapeIds = useValue(editor.renderVisibleShapeIdsSignal);
62
+ const renderOrderById = useValue(editor.shapeRenderOrderByIdSignal);
63
+ const visibleShapeIdSet = new Set(visibleShapeIds);
64
+ const renderOrders = retainedShapeIds.map(
65
+ (shapeId) => renderOrderById.get(shapeId) ?? -1,
66
+ );
67
+ const visibleRenderOrders = retainedShapeIds
68
+ .filter((shapeId) => visibleShapeIdSet.has(shapeId))
69
+ .map((shapeId) => renderOrderById.get(shapeId) ?? -1);
70
+ const hiddenRetainedIds = retainedShapeIds.filter(
71
+ (shapeId) => !visibleShapeIdSet.has(shapeId),
72
+ );
73
+ const summary = {
74
+ pageShapeCount: pageShapeIds.length,
75
+ mountedCount: retainedShapeIds.length,
76
+ visibleCount: visibleShapeIds.length,
77
+ hiddenRetainedCount: hiddenRetainedIds.length,
78
+ minRenderOrder:
79
+ renderOrders.length > 0 ? Math.min(...renderOrders) : null,
80
+ maxRenderOrder:
81
+ renderOrders.length > 0 ? Math.max(...renderOrders) : null,
82
+ mountedRenderOrderSeq: renderOrders.join(","),
83
+ visibleRenderOrderSeq: visibleRenderOrders.join(","),
84
+ firstMountedShapeId: retainedShapeIds[0] ?? null,
85
+ lastMountedShapeId: retainedShapeIds[retainedShapeIds.length - 1] ?? null,
86
+ hiddenRetainedShapeIds: hiddenRetainedIds,
87
+ };
88
+
89
+ return (
90
+ <pre
91
+ data-ui="shape-layer-debug-summary"
92
+ data-mounted-count={summary.mountedCount}
93
+ data-visible-count={summary.visibleCount}
94
+ data-hidden-retained-count={summary.hiddenRetainedCount}
95
+ data-min-render-order={summary.minRenderOrder ?? undefined}
96
+ data-max-render-order={summary.maxRenderOrder ?? undefined}
97
+ hidden
98
+ >
99
+ {JSON.stringify(summary)}
100
+ </pre>
101
+ );
102
+ });
103
+
104
+ function shouldShowShapeLayerDebugSummary(): boolean {
105
+ if (typeof window === "undefined") {
106
+ return false;
107
+ }
108
+
109
+ const params = new URLSearchParams(window.location.search);
110
+ return params.get("renderHostDebug") === "1";
111
+ }
112
+
113
+ const ShapeItem = memo(function ShapeItem({ shapeId }: { shapeId: ShapeId }) {
114
+ const editor = useEditor();
115
+ const stableShapeId = shapeId as ShapeId;
116
+ const shape = useValue(editor.getShapeSignal(stableShapeId));
117
+ const isVisible = useValue(editor.getShapeRenderVisibleSignal(stableShapeId));
118
+ const activeToolId = useValue(editor.toolStateSignal).activeToolId;
119
+ const isSelectedByState = useValue(
120
+ editor.getShapeSelectedSignal(stableShapeId),
121
+ );
122
+ const isSelected = activeToolId === "select" && isSelectedByState;
123
+ const shapeEditingSession = useValue(
124
+ editor.getShapeEditingSessionForShapeSignal(stableShapeId),
125
+ );
126
+ const renderOrder = useValue(editor.getShapeRenderOrderSignal(stableShapeId));
127
+ const isDropTarget = useValue(editor.getShapeDropTargetSignal(stableShapeId));
128
+ const isErasing = useValue(editor.getShapeErasingSignal(stableShapeId));
129
+ const isSelectedDescendantTransformAncestor = useValue(
130
+ editor.getShapeSelectedDescendantTransformAncestorSignal(stableShapeId),
131
+ );
132
+ const clipAncestorIds = useValue(
133
+ editor.getShapeClipAncestorIdsSignal(stableShapeId),
134
+ );
135
+ const rendererRegistry = useCanvasRendererRegistry();
136
+
137
+ if (!shape) {
138
+ return null;
139
+ }
140
+
141
+ const renderer = rendererRegistry.getShapeRenderer(shape.type);
142
+ if (!renderer) return null;
143
+ const Component = renderer.component;
144
+ const frame = getShapeFrame(editor, shape);
145
+ const effectiveOpacity = isErasing ? shape.opacity * 0.32 : shape.opacity;
146
+ const siblingOrderState =
147
+ isSelected ? editor.getSelectionSiblingOrderState() : null;
148
+ const parentShape =
149
+ siblingOrderState?.parentId.startsWith("shape:")
150
+ ? editor.getShape(siblingOrderState.parentId as ShapeId)
151
+ : null;
152
+ const shouldShowOrderBadge = parentShape?.type === "frame";
153
+
154
+ return (
155
+ <div
156
+ data-ui="shape-instance"
157
+ data-shape-id={shape.id}
158
+ data-shape-type={shape.type}
159
+ data-selected={isSelected ? "true" : "false"}
160
+ data-drop-target={isDropTarget ? "true" : "false"}
161
+ data-visible={isVisible ? "true" : "false"}
162
+ data-render-order={renderOrder >= 0 ? String(renderOrder) : undefined}
163
+ data-clip-ancestor-count={clipAncestorIds.length}
164
+ style={{
165
+ position: "absolute",
166
+ display: isVisible ? undefined : "none",
167
+ left: frame.left,
168
+ top: frame.top,
169
+ width: frame.width,
170
+ height: frame.height,
171
+ opacity: effectiveOpacity === 1 ? undefined : effectiveOpacity,
172
+ transform: frame.transform,
173
+ transformOrigin: frame.transformOrigin,
174
+ pointerEvents: "none",
175
+ outline: isDropTarget ? "2px solid #2563eb" : undefined,
176
+ outlineOffset: isDropTarget ? 4 : undefined,
177
+ boxShadow: isDropTarget
178
+ ? "0 0 0 6px rgba(37, 99, 235, 0.16)"
179
+ : undefined,
180
+ zIndex: renderOrder >= 0 ? renderOrder + 1 : undefined,
181
+ }}
182
+ >
183
+ {isVisible ? (
184
+ <ShapeClipStack
185
+ clipAncestorIds={clipAncestorIds}
186
+ shapeElementToPage={frame.elementToPage}
187
+ >
188
+ <Component
189
+ shape={shape as never}
190
+ editor={editor}
191
+ isSelected={isSelected}
192
+ shapeEditingSession={shapeEditingSession}
193
+ />
194
+ </ShapeClipStack>
195
+ ) : null}
196
+ {isVisible && isDropTarget ? (
197
+ <ShapeDropTargetLabel shapeType={shape.type} />
198
+ ) : null}
199
+ {isVisible && isSelectedDescendantTransformAncestor ? (
200
+ <TransformDescendantAncestorBoundary />
201
+ ) : null}
202
+ {isVisible && shouldShowOrderBadge && siblingOrderState ? (
203
+ <ShapeOrderBadge state={siblingOrderState} />
204
+ ) : null}
205
+ </div>
206
+ );
207
+ });
208
+
209
+ function TransformDescendantAncestorBoundary() {
210
+ return (
211
+ <div
212
+ data-ui="transform-descendant-ancestor-boundary"
213
+ style={{
214
+ position: "absolute",
215
+ inset: 0,
216
+ border: "1px dashed #64748b",
217
+ pointerEvents: "none",
218
+ }}
219
+ />
220
+ );
221
+ }
222
+
223
+ function ShapeOrderBadge({
224
+ state,
225
+ }: {
226
+ state: { index: number; count: number };
227
+ }) {
228
+ return (
229
+ <div
230
+ data-ui="shape-order-badge"
231
+ style={{
232
+ position: "absolute",
233
+ right: 8,
234
+ top: 8,
235
+ display: "flex",
236
+ alignItems: "center",
237
+ height: 22,
238
+ padding: "0 8px",
239
+ borderRadius: 999,
240
+ border: "1px solid rgba(15, 23, 42, 0.12)",
241
+ background: "rgba(255, 255, 255, 0.94)",
242
+ color: "#334155",
243
+ fontFamily:
244
+ "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
245
+ fontSize: 11,
246
+ fontWeight: 700,
247
+ lineHeight: "22px",
248
+ boxShadow: "0 8px 18px rgba(15, 23, 42, 0.12)",
249
+ whiteSpace: "nowrap",
250
+ pointerEvents: "none",
251
+ }}
252
+ >
253
+ {state.index + 1} / {state.count}
254
+ </div>
255
+ );
256
+ }
257
+
258
+ function ShapeDropTargetLabel({ shapeType }: { shapeType: string }) {
259
+ return (
260
+ <div
261
+ data-ui="shape-drop-target-label"
262
+ style={{
263
+ position: "absolute",
264
+ left: 10,
265
+ top: 10,
266
+ display: "flex",
267
+ alignItems: "center",
268
+ height: 24,
269
+ padding: "0 10px",
270
+ borderRadius: 999,
271
+ background: "#2563eb",
272
+ color: "white",
273
+ fontFamily:
274
+ "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
275
+ fontSize: 12,
276
+ fontWeight: 700,
277
+ lineHeight: "24px",
278
+ boxShadow: "0 10px 24px rgba(37, 99, 235, 0.24)",
279
+ whiteSpace: "nowrap",
280
+ pointerEvents: "none",
281
+ }}
282
+ >
283
+ {shapeType === "frame" ? "Drop into frame" : "Drop into group"}
284
+ </div>
285
+ );
286
+ }
287
+
288
+ function ShapeClipStack({
289
+ children,
290
+ clipAncestorIds,
291
+ shapeElementToPage,
292
+ }: {
293
+ children: ReactNode;
294
+ clipAncestorIds: readonly ShapeId[];
295
+ shapeElementToPage: Mat;
296
+ }) {
297
+ if (clipAncestorIds.length === 0) {
298
+ return <>{children}</>;
299
+ }
300
+
301
+ return clipAncestorIds.reduceRight<ReactNode>(
302
+ (content, ancestorId) => (
303
+ <ShapeClipBoundary
304
+ key={ancestorId}
305
+ ancestorId={ancestorId}
306
+ shapeElementToPage={shapeElementToPage}
307
+ >
308
+ {content}
309
+ </ShapeClipBoundary>
310
+ ),
311
+ children,
312
+ );
313
+ }
314
+
315
+ function ShapeClipBoundary({
316
+ ancestorId,
317
+ children,
318
+ shapeElementToPage,
319
+ }: {
320
+ ancestorId: ShapeId;
321
+ children: ReactNode;
322
+ shapeElementToPage: Mat;
323
+ }) {
324
+ const editor = useEditor();
325
+ const ancestor = useValue(editor.getShapeSignal(ancestorId));
326
+ if (!ancestor) {
327
+ return <>{children}</>;
328
+ }
329
+
330
+ const clipPath = getShapeElementClipPath(
331
+ editor,
332
+ ancestor,
333
+ shapeElementToPage,
334
+ );
335
+ if (!clipPath) {
336
+ return <>{children}</>;
337
+ }
338
+
339
+ const style: CSSProperties = {
340
+ width: "100%",
341
+ height: "100%",
342
+ clipPath,
343
+ WebkitClipPath: clipPath,
344
+ };
345
+
346
+ return (
347
+ <div
348
+ data-ui="shape-clip-boundary"
349
+ data-clip-ancestor-id={ancestorId}
350
+ style={style}
351
+ >
352
+ {children}
353
+ </div>
354
+ );
355
+ }
356
+
357
+ function getShapeFrame(
358
+ editor: Editor,
359
+ shape: ShapeRecord,
360
+ ): ShapeFrame {
361
+ const renderBounds = editor.computeShapeRenderBounds(shape);
362
+ const renderToPage = editor.computeShapeRenderToPageTransform(shape);
363
+ const elementToPage = renderToPage.compose(
364
+ Mat.Translate(renderBounds.x, renderBounds.y),
365
+ );
366
+ return {
367
+ left: 0,
368
+ top: 0,
369
+ width: renderBounds.w,
370
+ height: renderBounds.h,
371
+ transform: elementToPage.toCssString(),
372
+ transformOrigin: "0 0",
373
+ elementToPage,
374
+ };
375
+ }
376
+
377
+ function getShapeElementClipPath(
378
+ editor: Editor,
379
+ ancestor: ShapeRecord,
380
+ shapeElementToPage: Mat,
381
+ ): string | null {
382
+ const util = editor.getShapeUtil(ancestor);
383
+ const clipBounds = util.getChildClipLocalBounds(ancestor);
384
+ if (!clipBounds || !isValidBox(clipBounds)) {
385
+ return null;
386
+ }
387
+
388
+ const ancestorLocalToPage = editor.computeShapeRenderToPageTransform(ancestor);
389
+ const pageToShapeElement = shapeElementToPage.invert();
390
+ const points = getBoxCorners(clipBounds).map((point) =>
391
+ pageToShapeElement.applyToPoint(
392
+ ancestorLocalToPage.applyToPoint(point),
393
+ ),
394
+ );
395
+
396
+ return `polygon(${points.map(formatClipPoint).join(", ")})`;
397
+ }
398
+
399
+ function getBoxCorners(box: Box): Vec[] {
400
+ return [
401
+ new Vec(box.minX, box.minY),
402
+ new Vec(box.maxX, box.minY),
403
+ new Vec(box.maxX, box.maxY),
404
+ new Vec(box.minX, box.maxY),
405
+ ];
406
+ }
407
+
408
+ function isValidBox(box: Box): boolean {
409
+ return (
410
+ Number.isFinite(box.x) &&
411
+ Number.isFinite(box.y) &&
412
+ Number.isFinite(box.w) &&
413
+ Number.isFinite(box.h) &&
414
+ box.w >= 0 &&
415
+ box.h >= 0
416
+ );
417
+ }
418
+
419
+ function formatClipPoint(point: Vec): string {
420
+ return `${formatCssNumber(point.x)}px ${formatCssNumber(point.y)}px`;
421
+ }
422
+
423
+ function formatCssNumber(value: number): string {
424
+ if (!Number.isFinite(value)) {
425
+ return "0";
426
+ }
427
+ return Number(value.toFixed(3)).toString();
428
+ }
@@ -0,0 +1,4 @@
1
+ export { Canvas } from "./CanvasView";
2
+ export { OverlayLayer } from "./OverlayLayer";
3
+ export { PageSpace } from "./PageSpace";
4
+ export { ShapeLayer } from "./ShapeLayer";
@@ -0,0 +1,11 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { Editor } from "@land/canvas-core";
3
+
4
+ export const EditorContext = createContext<Editor | null>(null);
5
+
6
+ export function useEditor(): Editor {
7
+ const editor = useContext(EditorContext);
8
+ if (!editor)
9
+ throw new Error("useEditor must be used within EditorContext.Provider");
10
+ return editor;
11
+ }
@@ -0,0 +1,14 @@
1
+ import { useEffect, useReducer } from "react";
2
+ import { observeValue, type ReadonlyValue } from "@land/canvas-core";
3
+
4
+ export function useValue<T>(signal: ReadonlyValue<T>): T {
5
+ const [, forceUpdate] = useReducer((c: number) => c + 1, 0);
6
+
7
+ useEffect(() => {
8
+ return observeValue("useValue", signal, () => {
9
+ forceUpdate();
10
+ });
11
+ }, [signal]);
12
+
13
+ return signal.get();
14
+ }
@@ -0,0 +1,14 @@
1
+ import { createContext, useContext } from "react";
2
+ import {
3
+ createCanvasRendererRegistry,
4
+ type CanvasRendererRegistry,
5
+ } from "./rendererRegistry";
6
+
7
+ const defaultCanvasRendererRegistry = createCanvasRendererRegistry();
8
+
9
+ export const CanvasRendererRegistryContext =
10
+ createContext<CanvasRendererRegistry>(defaultCanvasRendererRegistry);
11
+
12
+ export function useCanvasRendererRegistry(): CanvasRendererRegistry {
13
+ return useContext(CanvasRendererRegistryContext);
14
+ }
@@ -0,0 +1,61 @@
1
+ import type {
2
+ ContextMenuContext,
3
+ ContextMenuContribution,
4
+ ContextMenuContributionTarget,
5
+ ContextMenuSection,
6
+ LandCorePlugin,
7
+ } from "@land/canvas-core";
8
+
9
+ export class CanvasContextMenuRegistry {
10
+ private readonly contributions = new Map<string, ContextMenuContribution>();
11
+ private readonly duplicateContributionIds = new Set<string>();
12
+
13
+ registerContribution(
14
+ contribution: ContextMenuContribution,
15
+ options: { replace?: boolean } = {},
16
+ ): void {
17
+ if (this.contributions.has(contribution.id) && !options.replace) {
18
+ this.duplicateContributionIds.add(contribution.id);
19
+ return;
20
+ }
21
+ this.contributions.set(contribution.id, contribution);
22
+ }
23
+
24
+ registerCorePlugins(plugins: readonly LandCorePlugin[]): void {
25
+ for (const plugin of plugins) {
26
+ for (const contribution of plugin.contextMenuContributions ?? []) {
27
+ this.registerContribution(contribution);
28
+ }
29
+ }
30
+ }
31
+
32
+ getDuplicateContributionIds(): string[] {
33
+ return [...this.duplicateContributionIds];
34
+ }
35
+
36
+ getSections(
37
+ target: ContextMenuContributionTarget,
38
+ context: ContextMenuContext,
39
+ ): ContextMenuSection[] {
40
+ return [...this.contributions.values()]
41
+ .filter(
42
+ (contribution) =>
43
+ contribution.target === "any" || contribution.target === target,
44
+ )
45
+ .filter((contribution) => contribution.when?.(context) ?? true)
46
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
47
+ .flatMap((contribution) =>
48
+ [...contribution.getSections(context)].sort(
49
+ (a, b) => (a.order ?? 0) - (b.order ?? 0),
50
+ ),
51
+ );
52
+ }
53
+ }
54
+
55
+ export function createCanvasContextMenuRegistry(
56
+ plugins: readonly LandCorePlugin[] = [],
57
+ ): CanvasContextMenuRegistry {
58
+ const nextRegistry = new CanvasContextMenuRegistry();
59
+ nextRegistry.registerCorePlugins(plugins);
60
+ return nextRegistry;
61
+ }
@@ -0,0 +1,61 @@
1
+ import {
2
+ type CanvasDomEventGuard,
3
+ type CanvasDomEventGuardCursorInput,
4
+ type CanvasDomEventGuardInput,
5
+ type LandCanvasPlugin,
6
+ } from "@land/canvas-plugin-api";
7
+
8
+ export class CanvasDomEventGuardRegistry {
9
+ private readonly guards: CanvasDomEventGuard[] = [];
10
+ private readonly registeredGuardIds = new Set<string>();
11
+
12
+ register(guard: CanvasDomEventGuard): void {
13
+ if (this.registeredGuardIds.has(guard.id)) {
14
+ return;
15
+ }
16
+
17
+ this.guards.push(guard);
18
+ this.registeredGuardIds.add(guard.id);
19
+ }
20
+
21
+ registerCanvasPlugins(plugins: readonly LandCanvasPlugin[]): void {
22
+ for (const plugin of plugins) {
23
+ for (const guard of plugin.domEventGuards ?? []) {
24
+ this.register(guard);
25
+ }
26
+ }
27
+ }
28
+
29
+ getGuards(): readonly CanvasDomEventGuard[] {
30
+ return this.guards;
31
+ }
32
+
33
+ handle(input: CanvasDomEventGuardInput): "continue" | "handled" {
34
+ for (const guard of this.guards) {
35
+ if (guard.handle(input) === "handled") {
36
+ return "handled";
37
+ }
38
+ }
39
+
40
+ return "continue";
41
+ }
42
+
43
+ getCursor(input: CanvasDomEventGuardCursorInput): string | undefined {
44
+ for (const guard of this.guards) {
45
+ const cursor = guard.getCursor?.(input);
46
+ if (cursor !== undefined) {
47
+ return cursor;
48
+ }
49
+ }
50
+
51
+ return undefined;
52
+ }
53
+ }
54
+
55
+ export function createCanvasDomEventGuardRegistry(
56
+ plugins: readonly LandCanvasPlugin[] = [],
57
+ ): CanvasDomEventGuardRegistry {
58
+ const nextRegistry = new CanvasDomEventGuardRegistry();
59
+ nextRegistry.registerCanvasPlugins(plugins);
60
+ return nextRegistry;
61
+ }
@@ -0,0 +1,38 @@
1
+ export {
2
+ CanvasRendererRegistryContext,
3
+ useCanvasRendererRegistry,
4
+ } from "./CanvasRendererRegistryContext";
5
+ export {
6
+ CanvasDomEventGuardRegistry,
7
+ createCanvasDomEventGuardRegistry,
8
+ } from "./domEventGuards";
9
+ export {
10
+ CanvasRendererRegistry,
11
+ createCanvasRendererRegistry,
12
+ } from "./rendererRegistry";
13
+ export {
14
+ CanvasToolbarRegistry,
15
+ createCanvasToolbarRegistry,
16
+ } from "./toolbarRegistry";
17
+ export {
18
+ CanvasContextMenuRegistry,
19
+ createCanvasContextMenuRegistry,
20
+ } from "./contextMenuRegistry";
21
+ export type {
22
+ AnyRegisteredShapeRenderer,
23
+ RegisteredShapeRenderer,
24
+ ShapeRendererProps,
25
+ } from "@land/canvas-plugin-api";
26
+ export { createCanvasPluginHostRuntime } from "./pluginHost";
27
+ export type {
28
+ CanvasPluginHostManifestEntry,
29
+ CanvasPluginHostManifestToolbarContributionEntry,
30
+ CanvasPluginHostRuntime,
31
+ CanvasPluginHostShapeValidationSummary,
32
+ CanvasPluginHostSource,
33
+ CanvasPluginHostValidationIssue,
34
+ CanvasPluginHostValidationIssueSeverity,
35
+ CanvasPluginHostValidationSummary,
36
+ CanvasPluginHostVerdict,
37
+ CreateCanvasPluginHostRuntimeOptions,
38
+ } from "./pluginHost";