@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,156 @@
1
+ import { useCallback, useState } from "react";
2
+ import type {
3
+ Editor,
4
+ ExportIntegrityReport,
5
+ ShapeCapabilityManifest,
6
+ ShapeId,
7
+ SvgExportSkippedShape,
8
+ } from "@land/canvas-core";
9
+
10
+ export interface SvgAssetInliner {
11
+ inline(input: {
12
+ editor: Editor;
13
+ exportedShapeIds: readonly ShapeId[];
14
+ svg: string;
15
+ }): Promise<string>;
16
+ }
17
+
18
+ export type ExportStatus = {
19
+ tone: "success" | "warning" | "error";
20
+ title: string;
21
+ details: string;
22
+ integrity: ExportIntegrityReport;
23
+ capabilityManifest: ShapeCapabilityManifest;
24
+ };
25
+
26
+ export interface SvgExportResult {
27
+ status: ExportStatus;
28
+ svg: string | null;
29
+ exportedShapeIds: readonly ShapeId[];
30
+ skippedShapes: readonly SvgExportSkippedShape[];
31
+ }
32
+
33
+ export interface SvgExportControls {
34
+ status: ExportStatus | null;
35
+ exportSvg(shapeIds: readonly ShapeId[]): Promise<SvgExportResult>;
36
+ }
37
+
38
+ export function useSvgExport({
39
+ assetInliners = [],
40
+ editor,
41
+ }: {
42
+ assetInliners?: readonly SvgAssetInliner[];
43
+ editor: Editor;
44
+ }): SvgExportControls {
45
+ const [status, setStatus] = useState<ExportStatus | null>(null);
46
+ const exportSvg = useCallback(
47
+ async (shapeIds: readonly ShapeId[]): Promise<SvgExportResult> => {
48
+ const repair = editor.repairExportIntegrity(shapeIds);
49
+ const result = editor.getSvgString(shapeIds, { padding: 16 });
50
+ const integrity = editor.getExportIntegrityReport(shapeIds);
51
+ const capabilityManifest = editor.getShapeCapabilityManifest();
52
+ const nextStatus = getExportStatus({ capabilityManifest, integrity });
53
+ setStatus(nextStatus);
54
+
55
+ if (repair.before.issues.length > 0 || integrity.issues.length > 0) {
56
+ console.warn("SVG export integrity report", {
57
+ before: repair.before,
58
+ after: repair.after,
59
+ final: integrity,
60
+ });
61
+ }
62
+
63
+ if (!result) {
64
+ return {
65
+ status: nextStatus,
66
+ svg: null,
67
+ exportedShapeIds: [],
68
+ skippedShapes: [],
69
+ };
70
+ }
71
+ if (result.skippedShapes.length > 0) {
72
+ console.warn("Skipped shapes during SVG export", result.skippedShapes);
73
+ }
74
+
75
+ let svg = result.svg;
76
+ for (const inliner of assetInliners) {
77
+ svg = await inliner.inline({
78
+ editor,
79
+ exportedShapeIds: result.exportedShapeIds,
80
+ svg,
81
+ });
82
+ }
83
+
84
+ return {
85
+ status: nextStatus,
86
+ svg,
87
+ exportedShapeIds: result.exportedShapeIds,
88
+ skippedShapes: result.skippedShapes,
89
+ };
90
+ },
91
+ [assetInliners, editor],
92
+ );
93
+
94
+ return {
95
+ status,
96
+ exportSvg,
97
+ };
98
+ }
99
+
100
+ function formatCountMap(counts: Partial<Record<string, number>>): string {
101
+ const entries = Object.entries(counts).filter(
102
+ (entry): entry is [string, number] => typeof entry[1] === "number",
103
+ );
104
+ if (entries.length === 0) {
105
+ return "none";
106
+ }
107
+ return entries.map(([kind, count]) => `${kind} ${count}`).join(", ");
108
+ }
109
+
110
+ function countExportIntegrityIssues(
111
+ integrity: ExportIntegrityReport,
112
+ ): Partial<Record<string, number>> {
113
+ const counts: Partial<Record<string, number>> = {};
114
+ for (const issue of integrity.issues) {
115
+ counts[issue.kind] = (counts[issue.kind] ?? 0) + 1;
116
+ }
117
+ return counts;
118
+ }
119
+
120
+ function getExportStatus({
121
+ capabilityManifest,
122
+ integrity,
123
+ }: {
124
+ capabilityManifest: ShapeCapabilityManifest;
125
+ integrity: ExportIntegrityReport;
126
+ }): ExportStatus {
127
+ const issues = formatCountMap(countExportIntegrityIssues(integrity));
128
+
129
+ if (integrity.fatalIssueCount === 0 && integrity.issues.length === 0) {
130
+ return {
131
+ tone: "success",
132
+ title: "SVG export ready",
133
+ details: `${integrity.requestedShapeIds.length} shape(s) ready. Issues: ${issues}.`,
134
+ integrity,
135
+ capabilityManifest,
136
+ };
137
+ }
138
+
139
+ if (integrity.canExport) {
140
+ return {
141
+ tone: "warning",
142
+ title: "SVG export repaired",
143
+ details: `${integrity.requestedShapeIds.length} shape(s) exportable. Issues: ${issues}.`,
144
+ integrity,
145
+ capabilityManifest,
146
+ };
147
+ }
148
+
149
+ return {
150
+ tone: "error",
151
+ title: "SVG export blocked",
152
+ details: `Fatal export issue(s): ${issues}.`,
153
+ integrity,
154
+ capabilityManifest,
155
+ };
156
+ }
@@ -0,0 +1,23 @@
1
+ import { useValue } from "@land/canvas-react";
2
+ import type { Editor } from "@land/canvas-core";
3
+
4
+ export interface HistoryControls {
5
+ canUndo: boolean;
6
+ canRedo: boolean;
7
+ undo(): void;
8
+ redo(): void;
9
+ }
10
+
11
+ export function useHistoryControls(editor: Editor): HistoryControls {
12
+ const historyState = useValue(editor.historyStateSignal);
13
+ return {
14
+ canUndo: historyState.canUndo,
15
+ canRedo: historyState.canRedo,
16
+ undo() {
17
+ editor.undo();
18
+ },
19
+ redo() {
20
+ editor.redo();
21
+ },
22
+ };
23
+ }
@@ -0,0 +1,41 @@
1
+ import { useValue } from "@land/canvas-react";
2
+ import type { Editor, PageId, PageRecord } from "@land/canvas-core";
3
+
4
+ export interface PageControls {
5
+ pages: readonly PageRecord[];
6
+ currentPage: PageRecord | null;
7
+ currentPageId: PageId;
8
+ canDeleteCurrentPage: boolean;
9
+ createPage(): void;
10
+ deleteCurrentPage(): void;
11
+ renameCurrentPage(nextName: string): void;
12
+ selectPage(pageId: PageId): void;
13
+ }
14
+
15
+ export function usePageControls(editor: Editor): PageControls {
16
+ const pageState = useValue(editor.pageStateSignal);
17
+ return {
18
+ pages: pageState.pages,
19
+ currentPage: pageState.currentPage,
20
+ currentPageId: pageState.currentPageId,
21
+ canDeleteCurrentPage: pageState.canDeleteCurrentPage,
22
+ createPage() {
23
+ editor.createPageAndSelect(undefined, "user");
24
+ },
25
+ deleteCurrentPage() {
26
+ if (!pageState.currentPage) {
27
+ return;
28
+ }
29
+ editor.deletePage(pageState.currentPage.id, "user");
30
+ },
31
+ renameCurrentPage(nextName) {
32
+ if (!pageState.currentPage) {
33
+ return;
34
+ }
35
+ editor.renamePage(pageState.currentPage.id, nextName, "user");
36
+ },
37
+ selectPage(pageId) {
38
+ editor.selectPage(pageId, "user");
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,490 @@
1
+ import {
2
+ SET_ACTIVE_TOOL_TOOLBAR_ACTION_ID,
3
+ TOGGLE_TOOL_LOCKED_TOOLBAR_ACTION_ID,
4
+ type Editor,
5
+ type ShapeId,
6
+ type ToolbarAction,
7
+ type ToolbarContext,
8
+ type ToolbarContribution,
9
+ type ToolbarGroup,
10
+ } from "@land/canvas-core";
11
+ import { type CanvasPluginHostRuntime } from "@land/canvas-react/host";
12
+
13
+ export const SYSTEM_SELECTION_DELETE_ACTION_ID = "system.selection.delete";
14
+ export const SYSTEM_SELECTION_GROUP_ACTION_ID = "system.selection.group";
15
+ export const SYSTEM_SELECTION_UNGROUP_ACTION_ID = "system.selection.ungroup";
16
+
17
+ const systemToolbarActions: readonly ToolbarAction[] = [
18
+ {
19
+ id: SET_ACTIVE_TOOL_TOOLBAR_ACTION_ID,
20
+ getValue(context) {
21
+ return context.tool.activeToolId;
22
+ },
23
+ run({ context, value }) {
24
+ if (typeof value !== "string" || !context.editor.tools.hasTool(value)) {
25
+ return;
26
+ }
27
+ context.editor.setActiveTool(value, "system");
28
+ },
29
+ },
30
+ {
31
+ id: TOGGLE_TOOL_LOCKED_TOOLBAR_ACTION_ID,
32
+ getValue(context) {
33
+ return context.tool.isToolLocked;
34
+ },
35
+ run({ context }) {
36
+ context.editor.toggleToolLocked("system");
37
+ },
38
+ },
39
+ {
40
+ id: SYSTEM_SELECTION_GROUP_ACTION_ID,
41
+ isVisible(context) {
42
+ return context.selection.count > 1;
43
+ },
44
+ isEnabled(context) {
45
+ return context.editor.canGroupSelection();
46
+ },
47
+ run({ context }) {
48
+ context.editor.groupSelection("user");
49
+ },
50
+ },
51
+ {
52
+ id: SYSTEM_SELECTION_UNGROUP_ACTION_ID,
53
+ isVisible(context) {
54
+ return context.editor.canUngroupSelection();
55
+ },
56
+ isEnabled(context) {
57
+ return context.editor.canUngroupSelection();
58
+ },
59
+ run({ context }) {
60
+ context.editor.ungroupSelection("user");
61
+ },
62
+ },
63
+ {
64
+ id: "system.selection.reset-rotation",
65
+ isVisible(context) {
66
+ return context.selection.canResetRotation;
67
+ },
68
+ run({ context }) {
69
+ context.editor.resetSelectionRotation("user");
70
+ },
71
+ },
72
+ {
73
+ id: "system.selection.flip-horizontal",
74
+ isVisible(context) {
75
+ return context.selection.count > 0;
76
+ },
77
+ isEnabled(context) {
78
+ return context.selection.count > 0;
79
+ },
80
+ run({ context }) {
81
+ context.editor.flipSelection("horizontal", "user");
82
+ },
83
+ },
84
+ {
85
+ id: "system.selection.flip-vertical",
86
+ isVisible(context) {
87
+ return context.selection.count > 0;
88
+ },
89
+ isEnabled(context) {
90
+ return context.selection.count > 0;
91
+ },
92
+ run({ context }) {
93
+ context.editor.flipSelection("vertical", "user");
94
+ },
95
+ },
96
+ {
97
+ id: "system.selection.send-to-back",
98
+ isVisible(context) {
99
+ return (
100
+ context.selection.count > 0 &&
101
+ !isSingleSelectedFrameChild(context)
102
+ );
103
+ },
104
+ isEnabled(context) {
105
+ return context.editor.canMoveSelectionInParentOrder("backward");
106
+ },
107
+ run({ context }) {
108
+ context.editor.moveSelectionToParentOrderEdge("back", "user");
109
+ },
110
+ },
111
+ {
112
+ id: "system.selection.send-backward",
113
+ isVisible(context) {
114
+ return (
115
+ context.selection.count > 0 &&
116
+ !isSingleSelectedFrameChild(context)
117
+ );
118
+ },
119
+ isEnabled(context) {
120
+ return context.editor.canMoveSelectionInParentOrder("backward");
121
+ },
122
+ run({ context }) {
123
+ context.editor.moveSelectionInParentOrder("backward", "user");
124
+ },
125
+ },
126
+ {
127
+ id: "system.selection.bring-forward",
128
+ isVisible(context) {
129
+ return (
130
+ context.selection.count > 0 &&
131
+ !isSingleSelectedFrameChild(context)
132
+ );
133
+ },
134
+ isEnabled(context) {
135
+ return context.editor.canMoveSelectionInParentOrder("forward");
136
+ },
137
+ run({ context }) {
138
+ context.editor.moveSelectionInParentOrder("forward", "user");
139
+ },
140
+ },
141
+ {
142
+ id: "system.selection.bring-to-front",
143
+ isVisible(context) {
144
+ return (
145
+ context.selection.count > 0 &&
146
+ !isSingleSelectedFrameChild(context)
147
+ );
148
+ },
149
+ isEnabled(context) {
150
+ return context.editor.canMoveSelectionInParentOrder("forward");
151
+ },
152
+ run({ context }) {
153
+ context.editor.moveSelectionToParentOrderEdge("front", "user");
154
+ },
155
+ },
156
+ {
157
+ id: SYSTEM_SELECTION_DELETE_ACTION_ID,
158
+ isEnabled(context) {
159
+ return context.selection.count > 0;
160
+ },
161
+ run({ context }) {
162
+ context.editor.deleteSelection("user");
163
+ },
164
+ },
165
+ ];
166
+
167
+ function isSingleSelectedFrameChild(context: ToolbarContext): boolean {
168
+ const [shape] = context.selection.selectedShapes;
169
+ if (context.selection.count !== 1 || !shape?.parentId.startsWith("shape:")) {
170
+ return false;
171
+ }
172
+ return context.editor.getShape(shape.parentId as ShapeId)?.type === "frame";
173
+ }
174
+
175
+ const systemSelectToolToolbarContribution: ToolbarContribution = {
176
+ id: "system.select-tool-toolbar",
177
+ surface: "tool",
178
+ placement: "chrome",
179
+ order: 0,
180
+ actionIds: [SET_ACTIVE_TOOL_TOOLBAR_ACTION_ID],
181
+ getGroups() {
182
+ return [
183
+ {
184
+ id: "system.select-tool",
185
+ items: [
186
+ {
187
+ kind: "button",
188
+ id: "system.select-tool",
189
+ actionId: SET_ACTIVE_TOOL_TOOLBAR_ACTION_ID,
190
+ value: "select",
191
+ label: "Select",
192
+ shortcut: "V",
193
+ icon: "mouse-pointer-2",
194
+ dataUi: "select-tool-button",
195
+ },
196
+ {
197
+ kind: "button",
198
+ id: "system.hand-tool",
199
+ actionId: SET_ACTIVE_TOOL_TOOLBAR_ACTION_ID,
200
+ value: "hand",
201
+ label: "Hand",
202
+ shortcut: "H",
203
+ icon: "hand",
204
+ dataUi: "hand-tool-button",
205
+ },
206
+ ],
207
+ },
208
+ ];
209
+ },
210
+ };
211
+
212
+ const systemToolLockToolbarContribution: ToolbarContribution = {
213
+ id: "system.tool-lock-toolbar",
214
+ surface: "tool",
215
+ placement: "chrome",
216
+ order: 1000,
217
+ actionIds: [TOGGLE_TOOL_LOCKED_TOOLBAR_ACTION_ID],
218
+ getGroups(context) {
219
+ return [
220
+ {
221
+ id: "system.tool-lock",
222
+ items: [
223
+ {
224
+ kind: "separator",
225
+ id: "system.tool-lock-separator",
226
+ },
227
+ {
228
+ kind: "button",
229
+ id: "system.tool-lock",
230
+ actionId: TOGGLE_TOOL_LOCKED_TOOLBAR_ACTION_ID,
231
+ value: true,
232
+ label: context.tool.isToolLocked ? "Unlock tool" : "Lock tool",
233
+ icon: "lock",
234
+ dataUi: "tool-lock-button",
235
+ },
236
+ ],
237
+ },
238
+ ];
239
+ },
240
+ };
241
+
242
+ const systemSelectionToolbarContribution: ToolbarContribution = {
243
+ id: "system.selection-toolbar",
244
+ surface: "selection",
245
+ order: 1000,
246
+ actionIds: [
247
+ "system.selection.reset-rotation",
248
+ "system.selection.flip-horizontal",
249
+ "system.selection.flip-vertical",
250
+ "system.selection.send-to-back",
251
+ "system.selection.send-backward",
252
+ "system.selection.bring-forward",
253
+ "system.selection.bring-to-front",
254
+ SYSTEM_SELECTION_GROUP_ACTION_ID,
255
+ SYSTEM_SELECTION_UNGROUP_ACTION_ID,
256
+ SYSTEM_SELECTION_DELETE_ACTION_ID,
257
+ ],
258
+ getGroups(context) {
259
+ const showGenericOrderActions =
260
+ context.selection.count > 0 && !isSingleSelectedFrameChild(context);
261
+ return [
262
+ {
263
+ id: "system.selection-actions",
264
+ items: [
265
+ ...(context.selection.canResetRotation
266
+ ? [
267
+ {
268
+ kind: "button" as const,
269
+ id: "system.selection.reset-rotation",
270
+ actionId: "system.selection.reset-rotation",
271
+ label: context.selection.isMixedFrameAndPointHandleSelection
272
+ ? "Reset shape rotation"
273
+ : "Reset rotation",
274
+ icon: "rotate-ccw",
275
+ dataUi: "reset-selection-rotation-button",
276
+ },
277
+ {
278
+ kind: "separator" as const,
279
+ id: "system.selection.reset-delete-separator",
280
+ },
281
+ ]
282
+ : []),
283
+ {
284
+ kind: "button" as const,
285
+ id: "system.selection.flip-horizontal",
286
+ actionId: "system.selection.flip-horizontal",
287
+ label: "Flip horizontal",
288
+ icon: "flip-horizontal-2",
289
+ dataUi: "flip-selection-horizontal-button",
290
+ },
291
+ {
292
+ kind: "button" as const,
293
+ id: "system.selection.flip-vertical",
294
+ actionId: "system.selection.flip-vertical",
295
+ label: "Flip vertical",
296
+ icon: "flip-vertical-2",
297
+ dataUi: "flip-selection-vertical-button",
298
+ },
299
+ {
300
+ kind: "separator" as const,
301
+ id: "system.selection.flip-order-separator",
302
+ },
303
+ ...(showGenericOrderActions
304
+ ? [
305
+ {
306
+ kind: "button" as const,
307
+ id: "system.selection.send-to-back",
308
+ actionId: "system.selection.send-to-back",
309
+ label: "Send to back",
310
+ icon: "chevrons-down",
311
+ dataUi: "send-selection-to-back-button",
312
+ },
313
+ {
314
+ kind: "button" as const,
315
+ id: "system.selection.send-backward",
316
+ actionId: "system.selection.send-backward",
317
+ label: "Send backward",
318
+ icon: "move-down",
319
+ dataUi: "send-selection-backward-button",
320
+ },
321
+ {
322
+ kind: "button" as const,
323
+ id: "system.selection.bring-forward",
324
+ actionId: "system.selection.bring-forward",
325
+ label: "Bring forward",
326
+ icon: "move-up",
327
+ dataUi: "bring-selection-forward-button",
328
+ },
329
+ {
330
+ kind: "button" as const,
331
+ id: "system.selection.bring-to-front",
332
+ actionId: "system.selection.bring-to-front",
333
+ label: "Bring to front",
334
+ icon: "chevrons-up",
335
+ dataUi: "bring-selection-to-front-button",
336
+ },
337
+ {
338
+ kind: "separator" as const,
339
+ id: "system.selection.order-delete-separator",
340
+ },
341
+ ]
342
+ : []),
343
+ {
344
+ kind: "button" as const,
345
+ id: SYSTEM_SELECTION_GROUP_ACTION_ID,
346
+ actionId: SYSTEM_SELECTION_GROUP_ACTION_ID,
347
+ label: "Group",
348
+ icon: "group",
349
+ dataUi: "group-selection-button",
350
+ },
351
+ {
352
+ kind: "button" as const,
353
+ id: SYSTEM_SELECTION_UNGROUP_ACTION_ID,
354
+ actionId: SYSTEM_SELECTION_UNGROUP_ACTION_ID,
355
+ label: "Ungroup",
356
+ icon: "ungroup",
357
+ dataUi: "ungroup-selection-button",
358
+ },
359
+ {
360
+ kind: "separator" as const,
361
+ id: "system.selection.group-delete-separator",
362
+ },
363
+ {
364
+ kind: "button",
365
+ id: SYSTEM_SELECTION_DELETE_ACTION_ID,
366
+ actionId: SYSTEM_SELECTION_DELETE_ACTION_ID,
367
+ label: "Delete selection",
368
+ icon: "trash-2",
369
+ dataUi: "delete-selection-toolbar-button",
370
+ },
371
+ ],
372
+ },
373
+ ];
374
+ },
375
+ };
376
+
377
+ export function registerSystemToolbarContributions(
378
+ pluginHostRuntime: CanvasPluginHostRuntime,
379
+ ) {
380
+ for (const action of systemToolbarActions) {
381
+ pluginHostRuntime.toolbarRegistry.registerAction(action);
382
+ }
383
+ pluginHostRuntime.toolbarRegistry.registerContribution(
384
+ systemSelectToolToolbarContribution,
385
+ );
386
+ pluginHostRuntime.toolbarRegistry.registerContribution(
387
+ systemToolLockToolbarContribution,
388
+ );
389
+ pluginHostRuntime.toolbarRegistry.registerContribution(
390
+ systemSelectionToolbarContribution,
391
+ );
392
+ }
393
+
394
+ export function createToolbarContext({
395
+ editor,
396
+ toolState,
397
+ selectionState,
398
+ }: {
399
+ editor: Editor;
400
+ toolState: {
401
+ activeToolId: string;
402
+ isToolLocked: boolean;
403
+ };
404
+ selectionState: {
405
+ count: number;
406
+ singleShapeType: ToolbarContext["selection"]["singleShapeType"];
407
+ commonShapeType: ToolbarContext["selection"]["commonShapeType"];
408
+ canResetSelectionRotation: boolean;
409
+ isMixedFrameAndPointHandleSelection: boolean;
410
+ };
411
+ }): ToolbarContext {
412
+ return {
413
+ editor,
414
+ selection: {
415
+ selectedShapeIds: editor.getSelectedShapeIds(),
416
+ selectedShapes: editor.getSelectedShapes(),
417
+ count: selectionState.count,
418
+ singleShapeType: selectionState.singleShapeType,
419
+ commonShapeType: selectionState.commonShapeType,
420
+ canResetRotation: selectionState.canResetSelectionRotation,
421
+ isMixedFrameAndPointHandleSelection:
422
+ selectionState.isMixedFrameAndPointHandleSelection,
423
+ },
424
+ tool: {
425
+ activeToolId: toolState.activeToolId,
426
+ isToolLocked: toolState.isToolLocked,
427
+ },
428
+ };
429
+ }
430
+
431
+ export function getActiveToolLabel(
432
+ groups: readonly ToolbarGroup[],
433
+ activeToolId: string,
434
+ ): string {
435
+ for (const group of groups) {
436
+ for (const item of group.items) {
437
+ if (
438
+ item.kind === "button" &&
439
+ item.actionId === SET_ACTIVE_TOOL_TOOLBAR_ACTION_ID &&
440
+ item.value === activeToolId
441
+ ) {
442
+ return item.label;
443
+ }
444
+ }
445
+ }
446
+ return activeToolId;
447
+ }
448
+
449
+ export function getSelectionToolbarTitle({
450
+ commonShapeType,
451
+ count,
452
+ isMixedFrameAndPointHandleSelection,
453
+ singleShapeType,
454
+ }: {
455
+ commonShapeType: string | null;
456
+ count: number;
457
+ isMixedFrameAndPointHandleSelection: boolean;
458
+ singleShapeType: string | null;
459
+ }): string {
460
+ const shapeType = singleShapeType ?? commonShapeType;
461
+ if (!shapeType) {
462
+ return isMixedFrameAndPointHandleSelection
463
+ ? "Mixed selection"
464
+ : `${count} selected`;
465
+ }
466
+
467
+ const suffix = count > 1 ? ` (${count})` : "";
468
+ if (shapeType === "geo") return `Shape${suffix}`;
469
+ if (shapeType === "text") return `Text${suffix}`;
470
+ return `${shapeType.charAt(0).toUpperCase()}${shapeType.slice(1)}${suffix}`;
471
+ }
472
+
473
+ export function runToolbarAction({
474
+ context,
475
+ pluginHostRuntime,
476
+ actionId,
477
+ }: {
478
+ context: ToolbarContext;
479
+ pluginHostRuntime: CanvasPluginHostRuntime;
480
+ actionId: string;
481
+ }): void {
482
+ const action = pluginHostRuntime.toolbarRegistry.getAction(actionId);
483
+ if (!action || action.isVisible?.(context) === false) {
484
+ return;
485
+ }
486
+ if (action.isEnabled?.(context) === false) {
487
+ return;
488
+ }
489
+ action.run({ context, value: action.getValue?.(context) ?? null });
490
+ }