@markup-canvas/core 1.1.6 → 1.2.0

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 (163) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/lib/MarkupCanvas.d.ts +10 -12
  3. package/dist/lib/actions/config/getConfig.d.ts +2 -0
  4. package/dist/lib/actions/index.d.ts +3 -0
  5. package/dist/lib/actions/pan/centerContent.d.ts +2 -0
  6. package/dist/lib/actions/pan/index.d.ts +6 -0
  7. package/dist/lib/actions/pan/panDown.d.ts +2 -0
  8. package/dist/lib/actions/pan/panLeft.d.ts +2 -0
  9. package/dist/lib/actions/pan/panRight.d.ts +2 -0
  10. package/dist/lib/actions/pan/panUp.d.ts +2 -0
  11. package/dist/lib/actions/pan/scrollToPoint.d.ts +2 -0
  12. package/dist/lib/actions/transform/index.d.ts +2 -0
  13. package/dist/lib/actions/transform/resetTransform.d.ts +2 -0
  14. package/dist/lib/actions/transform/updateTransform.d.ts +2 -0
  15. package/dist/lib/actions/ui/grid/hideGrid.d.ts +2 -0
  16. package/dist/lib/actions/ui/grid/index.d.ts +4 -0
  17. package/dist/lib/actions/ui/grid/isGridVisible.d.ts +2 -0
  18. package/dist/lib/actions/ui/grid/showGrid.d.ts +2 -0
  19. package/dist/lib/actions/ui/grid/toggleGrid.d.ts +2 -0
  20. package/dist/lib/actions/ui/index.d.ts +4 -0
  21. package/dist/lib/actions/ui/rulers/areRulersVisible.d.ts +2 -0
  22. package/dist/lib/actions/ui/rulers/hideRulers.d.ts +2 -0
  23. package/dist/lib/actions/ui/rulers/index.d.ts +4 -0
  24. package/dist/lib/actions/ui/rulers/showRulers.d.ts +2 -0
  25. package/dist/lib/actions/ui/rulers/toggleRulers.d.ts +2 -0
  26. package/dist/lib/actions/ui/toggleTransition.d.ts +1 -0
  27. package/dist/lib/actions/ui/updateThemeMode.d.ts +2 -0
  28. package/dist/lib/actions/zoom/index.d.ts +6 -0
  29. package/dist/lib/actions/zoom/resetView.d.ts +2 -0
  30. package/dist/lib/actions/zoom/resetViewToCenter.d.ts +3 -0
  31. package/dist/lib/actions/zoom/setZoom.d.ts +3 -0
  32. package/dist/lib/actions/zoom/zoomIn.d.ts +3 -0
  33. package/dist/lib/actions/zoom/zoomOut.d.ts +3 -0
  34. package/dist/lib/actions/zoom/zoomToPoint.d.ts +2 -0
  35. package/dist/lib/canvas/createCanvas.d.ts +2 -2
  36. package/dist/lib/canvas/fitToScreen.d.ts +2 -0
  37. package/dist/lib/canvas/getCanvasBounds.d.ts +2 -2
  38. package/dist/lib/canvas/index.d.ts +1 -1
  39. package/dist/lib/events/emitTransformEvents.d.ts +3 -0
  40. package/dist/lib/events/keyboard/handleKeyDown.d.ts +3 -2
  41. package/dist/lib/events/keyboard/handleKeyUp.d.ts +3 -2
  42. package/dist/lib/events/keyboard/setupKeyboardEvents.d.ts +3 -2
  43. package/dist/lib/events/mouse/createMouseDragControls.d.ts +7 -0
  44. package/dist/lib/events/mouse/handleClickToZoom.d.ts +3 -2
  45. package/dist/lib/events/mouse/handleMouseDown.d.ts +3 -2
  46. package/dist/lib/events/mouse/handleMouseLeave.d.ts +3 -2
  47. package/dist/lib/events/mouse/handleMouseMove.d.ts +3 -2
  48. package/dist/lib/events/mouse/handleMouseUp.d.ts +3 -2
  49. package/dist/lib/events/mouse/setupMouseDrag.d.ts +4 -3
  50. package/dist/lib/events/mouse/setupMouseEvents.d.ts +4 -3
  51. package/dist/lib/events/touch/handleTouchMove.d.ts +3 -2
  52. package/dist/lib/events/touch/setupTouchEvents.d.ts +2 -2
  53. package/dist/lib/events/trackpad/createTrackpadPanHandler.d.ts +2 -2
  54. package/dist/lib/events/utils/getAdaptiveZoomSpeed.d.ts +2 -2
  55. package/dist/lib/events/utils/getViewportCenter.d.ts +5 -0
  56. package/dist/lib/events/utils/resetDragState.d.ts +3 -2
  57. package/dist/lib/events/utils/updateCursor.d.ts +3 -2
  58. package/dist/lib/events/wheel/handleWheel.d.ts +3 -2
  59. package/dist/lib/events/wheel/setupWheelEvents.d.ts +3 -2
  60. package/dist/lib/events/wheel/setupWheelHandler.d.ts +3 -2
  61. package/dist/lib/helpers/getVisibleArea.d.ts +7 -0
  62. package/dist/lib/helpers/index.d.ts +2 -0
  63. package/dist/lib/helpers/isPointVisible.d.ts +2 -0
  64. package/dist/lib/transform/applyZoomToCanvas.d.ts +2 -2
  65. package/dist/lib/window/bindCanvasToWindow.d.ts +3 -0
  66. package/dist/lib/window/broadcastEvent.d.ts +2 -0
  67. package/dist/lib/window/cleanupWindowBinding.d.ts +2 -0
  68. package/dist/lib/window/index.d.ts +3 -0
  69. package/dist/markup-canvas.cjs.js +715 -554
  70. package/dist/markup-canvas.esm.js +715 -554
  71. package/dist/markup-canvas.umd.js +711 -547
  72. package/dist/markup-canvas.umd.min.js +1 -1
  73. package/dist/types/canvas.d.ts +1 -47
  74. package/dist/types/config.d.ts +0 -3
  75. package/dist/types/events.d.ts +4 -1
  76. package/dist/types/index.d.ts +3 -2
  77. package/dist/types/window.d.ts +84 -0
  78. package/package.json +1 -1
  79. package/src/index.ts +1 -1
  80. package/src/lib/MarkupCanvas.ts +142 -308
  81. package/src/lib/actions/config/getConfig.ts +5 -0
  82. package/src/lib/actions/index.ts +6 -0
  83. package/src/lib/actions/pan/centerContent.ts +21 -0
  84. package/src/lib/actions/pan/index.ts +6 -0
  85. package/src/lib/actions/pan/panDown.ts +13 -0
  86. package/src/lib/actions/pan/panLeft.ts +13 -0
  87. package/src/lib/actions/pan/panRight.ts +13 -0
  88. package/src/lib/actions/pan/panUp.ts +13 -0
  89. package/src/lib/actions/pan/scrollToPoint.ts +27 -0
  90. package/src/lib/actions/transform/index.ts +2 -0
  91. package/src/lib/actions/transform/resetTransform.ts +11 -0
  92. package/src/lib/actions/transform/updateTransform.ts +9 -0
  93. package/src/lib/actions/ui/grid/hideGrid.ts +9 -0
  94. package/src/lib/actions/ui/grid/index.ts +4 -0
  95. package/src/lib/actions/ui/grid/isGridVisible.ts +8 -0
  96. package/src/lib/actions/ui/grid/showGrid.ts +9 -0
  97. package/src/lib/actions/ui/grid/toggleGrid.ts +9 -0
  98. package/src/lib/actions/ui/index.ts +4 -0
  99. package/src/lib/actions/ui/rulers/areRulersVisible.ts +8 -0
  100. package/src/lib/actions/ui/rulers/hideRulers.ts +9 -0
  101. package/src/lib/actions/ui/rulers/index.ts +4 -0
  102. package/src/lib/actions/ui/rulers/showRulers.ts +9 -0
  103. package/src/lib/actions/ui/rulers/toggleRulers.ts +14 -0
  104. package/src/lib/actions/ui/toggleTransition.ts +3 -0
  105. package/src/lib/actions/ui/updateThemeMode.ts +25 -0
  106. package/src/lib/actions/zoom/index.ts +6 -0
  107. package/src/lib/actions/zoom/resetView.ts +17 -0
  108. package/src/lib/actions/zoom/resetViewToCenter.ts +21 -0
  109. package/src/lib/actions/zoom/setZoom.ts +22 -0
  110. package/src/lib/actions/zoom/zoomIn.ts +21 -0
  111. package/src/lib/actions/zoom/zoomOut.ts +21 -0
  112. package/src/lib/actions/zoom/zoomToPoint.ts +18 -0
  113. package/src/lib/canvas/createCanvas.ts +6 -14
  114. package/src/lib/canvas/fitToScreen.ts +27 -0
  115. package/src/lib/canvas/getCanvasBounds.ts +3 -4
  116. package/src/lib/canvas/index.ts +1 -1
  117. package/src/lib/config/constants.ts +2 -6
  118. package/src/lib/config/presets/editor-preset.ts +4 -8
  119. package/src/lib/events/emitTransformEvents.ts +9 -0
  120. package/src/lib/events/keyboard/handleKeyDown.ts +3 -2
  121. package/src/lib/events/keyboard/handleKeyUp.ts +3 -2
  122. package/src/lib/events/keyboard/setupKeyboardEvents.ts +18 -38
  123. package/src/lib/events/mouse/createMouseDragControls.ts +21 -0
  124. package/src/lib/events/mouse/handleClickToZoom.ts +3 -2
  125. package/src/lib/events/mouse/handleMouseDown.ts +3 -2
  126. package/src/lib/events/mouse/handleMouseLeave.ts +3 -2
  127. package/src/lib/events/mouse/handleMouseMove.ts +3 -2
  128. package/src/lib/events/mouse/handleMouseUp.ts +3 -2
  129. package/src/lib/events/mouse/setupMouseDrag.ts +5 -4
  130. package/src/lib/events/mouse/setupMouseEvents.ts +5 -4
  131. package/src/lib/events/postMessage/setupPostMessageEvents.ts +10 -0
  132. package/src/lib/events/touch/handleTouchMove.ts +3 -2
  133. package/src/lib/events/touch/setupTouchEvents.ts +3 -2
  134. package/src/lib/events/trackpad/createTrackpadPanHandler.ts +3 -2
  135. package/src/lib/events/utils/getAdaptiveZoomSpeed.ts +2 -2
  136. package/src/lib/events/utils/getViewportCenter.ts +14 -0
  137. package/src/lib/events/utils/resetDragState.ts +3 -2
  138. package/src/lib/events/utils/updateCursor.ts +3 -2
  139. package/src/lib/events/wheel/handleWheel.ts +3 -2
  140. package/src/lib/events/wheel/setupWheelEvents.ts +3 -2
  141. package/src/lib/events/wheel/setupWheelHandler.ts +3 -2
  142. package/src/lib/helpers/getVisibleArea.ts +6 -0
  143. package/src/lib/helpers/index.ts +2 -0
  144. package/src/lib/helpers/isPointVisible.ts +7 -0
  145. package/src/lib/rulers/createRulers.ts +0 -1
  146. package/src/lib/transform/applyZoomToCanvas.ts +2 -2
  147. package/src/lib/window/bindCanvasToWindow.ts +128 -0
  148. package/src/lib/window/broadcastEvent.ts +38 -0
  149. package/src/lib/window/cleanupWindowBinding.ts +15 -0
  150. package/src/lib/window/index.ts +3 -0
  151. package/src/types/canvas.ts +1 -47
  152. package/src/types/config.ts +1 -7
  153. package/src/types/events.ts +7 -1
  154. package/src/types/index.ts +4 -2
  155. package/src/types/window.ts +77 -0
  156. package/dist/lib/canvas/config.d.ts +0 -2
  157. package/dist/lib/canvas/getCanvasMethods.d.ts +0 -12
  158. package/dist/lib/events/keyboard/setupKeyboardNavigation.d.ts +0 -2
  159. package/src/lib/canvas/config.ts +0 -29
  160. package/src/lib/canvas/getCanvasMethods.ts +0 -102
  161. package/src/lib/events/keyboard/setupKeyboardNavigation.ts +0 -115
  162. /package/dist/lib/canvas/{calcVisibleArea.d.ts → calculateVisibleArea.d.ts} +0 -0
  163. /package/src/lib/canvas/{calcVisibleArea.ts → calculateVisibleArea.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Markup Canvas
3
3
  * High-performance markup canvas with zoom and pan capabilities
4
- * @version 1.1.6
4
+ * @version 1.2.0
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -9,68 +9,6 @@
9
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MarkupCanvas = factory());
10
10
  })(this, (function () { 'use strict';
11
11
 
12
- // Default transform values
13
- const DEFAULT_ZOOM = 1.0;
14
- // Validation thresholds
15
- const ZOOM_CHANGE_THRESHOLD = 0.001;
16
- // CSS transition values
17
- const FALLBACK_TRANSITION_DURATION = 0.2;
18
- // Zoom to fit padding factor
19
- const ZOOM_FIT_PADDING = 0.9;
20
- // CSS class names
21
- const CANVAS_CONTAINER_CLASS = "canvas-container";
22
- const TRANSFORM_LAYER_CLASS = "transform-layer";
23
- const CONTENT_LAYER_CLASS = "content-layer";
24
-
25
- function moveExistingContent(existingContent, contentLayer, transformLayer) {
26
- existingContent.forEach((child) => {
27
- if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
28
- contentLayer.appendChild(child);
29
- }
30
- });
31
- }
32
-
33
- function setupContentLayer(contentLayer) {
34
- contentLayer.style.position = "relative";
35
- contentLayer.style.width = "100%";
36
- contentLayer.style.height = "100%";
37
- contentLayer.style.pointerEvents = "auto";
38
- }
39
-
40
- // Sets up the transform layer with proper styles and dimensions
41
- function setupTransformLayer(transformLayer, config) {
42
- transformLayer.style.position = "absolute";
43
- const rulerOffset = config.rulerSize;
44
- transformLayer.style.top = `${rulerOffset}px`;
45
- transformLayer.style.left = `${rulerOffset}px`;
46
- transformLayer.style.width = `${config.width}px`;
47
- transformLayer.style.height = `${config.height}px`;
48
- transformLayer.style.transformOrigin = "0 0";
49
- }
50
-
51
- function createCanvasLayers(container, config) {
52
- const existingContent = Array.from(container.children);
53
- // Create or find transform layer
54
- let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
55
- if (!transformLayer) {
56
- transformLayer = document.createElement("div");
57
- transformLayer.className = TRANSFORM_LAYER_CLASS;
58
- container.appendChild(transformLayer);
59
- }
60
- setupTransformLayer(transformLayer, config);
61
- // Create or find content layer
62
- let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
63
- if (!contentLayer) {
64
- contentLayer = document.createElement("div");
65
- contentLayer.className = CONTENT_LAYER_CLASS;
66
- transformLayer.appendChild(contentLayer);
67
- moveExistingContent(existingContent, contentLayer, transformLayer);
68
- }
69
- // Set content layer properties
70
- setupContentLayer(contentLayer);
71
- return { transformLayer, contentLayer };
72
- }
73
-
74
12
  function canvasToContent(canvasX, canvasY, matrix) {
75
13
  if (!matrix?.inverse) {
76
14
  return { x: canvasX, y: canvasY };
@@ -98,6 +36,19 @@
98
36
  return new DOMMatrix([scale, 0, 0, scale, translateX, translateY]);
99
37
  }
100
38
 
39
+ // Default transform values
40
+ const DEFAULT_ZOOM = 1.0;
41
+ // Validation thresholds
42
+ const ZOOM_CHANGE_THRESHOLD = 0.001;
43
+ // CSS transition values
44
+ const FALLBACK_TRANSITION_DURATION = 0.2;
45
+ // Zoom to fit padding factor
46
+ const ZOOM_FIT_PADDING = 0.9;
47
+ // CSS class names
48
+ const CANVAS_CONTAINER_CLASS = "canvas-container";
49
+ const TRANSFORM_LAYER_CLASS = "transform-layer";
50
+ const CONTENT_LAYER_CLASS = "content-layer";
51
+
101
52
  function getZoomToMouseTransform(mouseX, mouseY, currentTransform, zoomFactor, config) {
102
53
  const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
103
54
  const transform = currentTransform || {
@@ -159,6 +110,16 @@
159
110
  };
160
111
  }
161
112
 
113
+ function getVisibleArea(canvas) {
114
+ const bounds = canvas.getBounds();
115
+ return bounds.visibleArea;
116
+ }
117
+
118
+ function isPointVisible(canvas, x, y) {
119
+ const visibleArea = getVisibleArea(canvas);
120
+ return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
121
+ }
122
+
162
123
  function withClampedZoom(config, operation) {
163
124
  const clampFunction = (scale) => clampZoom(scale, config);
164
125
  return operation(clampFunction);
@@ -214,13 +175,6 @@
214
175
  const finalRulerSize = hasRulers ? rulerSize : 0;
215
176
  return operation(finalRulerSize);
216
177
  }
217
- function withRulerOffsets(canvas, rulerSize, x, y, operation) {
218
- return withRulerSize(canvas, rulerSize, (rulerSize) => {
219
- const adjustedX = x - rulerSize;
220
- const adjustedY = y - rulerSize;
221
- return operation(adjustedX, adjustedY);
222
- });
223
- }
224
178
  function withRulerOffsetObject(canvas, rulerSize, coords, operation) {
225
179
  return withRulerSize(canvas, rulerSize, (rulerSize) => {
226
180
  const adjusted = {
@@ -252,8 +206,7 @@
252
206
  width: 8000,
253
207
  height: 8000,
254
208
  enableAcceleration: true,
255
- // Global Binding & Instance Access
256
- bindToWindow: false,
209
+ // Global Instance Access
257
210
  name: "markupCanvas",
258
211
  enablePostMessageAPI: false,
259
212
  // Interaction controls
@@ -261,7 +214,7 @@
261
214
  enablePan: true,
262
215
  enableTouch: true,
263
216
  enableKeyboard: true,
264
- bindKeyboardEventsTo: "canvas",
217
+ bindKeyboardEventsTo: "document",
265
218
  // Zoom behavior
266
219
  zoomSpeed: 1.5,
267
220
  minZoom: 0.05,
@@ -307,14 +260,11 @@
307
260
  gridColorDark: "rgba(232, 86, 193, 0.5)",
308
261
  // Theme
309
262
  themeMode: "light",
310
- // Callbacks
311
- onTransformUpdate: () => { },
312
263
  };
313
264
 
314
- function getCanvasBounds(canvas) {
265
+ function getCanvasBounds(canvas, config) {
315
266
  try {
316
267
  const container = canvas.container;
317
- const config = canvas.config;
318
268
  const transform = canvas.transform || {
319
269
  scale: 1.0,
320
270
  translateX: 0,
@@ -364,6 +314,116 @@
364
314
  }
365
315
  }
366
316
 
317
+ function disableTransition(element, config) {
318
+ try {
319
+ if (config.enableTransition) {
320
+ if (window.__markupCanvasTransitionTimeout) {
321
+ clearTimeout(window.__markupCanvasTransitionTimeout);
322
+ window.__markupCanvasTransitionTimeout = undefined;
323
+ }
324
+ const delay = (config.transitionDuration ?? FALLBACK_TRANSITION_DURATION) * 1000;
325
+ withDebounce("disableTransition", delay, () => {
326
+ element.style.transition = "none";
327
+ window.__markupCanvasTransitionTimeout = undefined;
328
+ });
329
+ return true;
330
+ }
331
+ return false;
332
+ }
333
+ catch (error) {
334
+ console.error("Failed to disable transitions:", error);
335
+ return true;
336
+ }
337
+ }
338
+
339
+ function enableTransition(element, config) {
340
+ try {
341
+ if (config.enableTransition) {
342
+ if (window.__markupCanvasTransitionTimeout) {
343
+ clearTimeout(window.__markupCanvasTransitionTimeout);
344
+ window.__markupCanvasTransitionTimeout = undefined;
345
+ }
346
+ element.style.transition = `transform ${config.transitionDuration}s linear`;
347
+ return true;
348
+ }
349
+ return false;
350
+ }
351
+ catch (error) {
352
+ console.error("Failed to enable transitions:", error);
353
+ return false;
354
+ }
355
+ }
356
+
357
+ function withTransition(element, config, operation) {
358
+ enableTransition(element, config);
359
+ try {
360
+ const result = operation();
361
+ return result;
362
+ }
363
+ finally {
364
+ disableTransition(element, config);
365
+ }
366
+ }
367
+
368
+ function centerContent(canvas, config, updateTransformFn, transformLayer) {
369
+ return withTransition(transformLayer, config, () => {
370
+ const bounds = getCanvasBounds(canvas, config);
371
+ const centerX = (bounds.width - bounds.contentWidth * canvas.transform.scale) / 2;
372
+ const centerY = (bounds.height - bounds.contentHeight * canvas.transform.scale) / 2;
373
+ return updateTransformFn({
374
+ translateX: centerX,
375
+ translateY: centerY,
376
+ });
377
+ });
378
+ }
379
+
380
+ function panDown(canvas, config, updateTransform) {
381
+ const panDistance = config.keyboardPanStep;
382
+ const newTransform = {
383
+ translateY: canvas.transform.translateY - panDistance,
384
+ };
385
+ return updateTransform(newTransform);
386
+ }
387
+
388
+ function panLeft(canvas, config, updateTransform) {
389
+ const panDistance = config.keyboardPanStep;
390
+ const newTransform = {
391
+ translateX: canvas.transform.translateX + panDistance,
392
+ };
393
+ return updateTransform(newTransform);
394
+ }
395
+
396
+ function panRight(canvas, config, updateTransform) {
397
+ const panDistance = config.keyboardPanStep;
398
+ const newTransform = {
399
+ translateX: canvas.transform.translateX - panDistance,
400
+ };
401
+ return updateTransform(newTransform);
402
+ }
403
+
404
+ function panUp(canvas, config, updateTransform) {
405
+ const panDistance = config.keyboardPanStep;
406
+ const newTransform = {
407
+ translateY: canvas.transform.translateY + panDistance,
408
+ };
409
+ return updateTransform(newTransform);
410
+ }
411
+
412
+ function scrollToPoint(canvas, config, x, y, updateTransform, transformLayer) {
413
+ return withTransition(transformLayer, config, () => {
414
+ const bounds = getCanvasBounds(canvas, config);
415
+ const centerX = bounds.width / 2;
416
+ const centerY = bounds.height / 2;
417
+ // Calculate new translation to center the point
418
+ const newTranslateX = centerX - x * canvas.transform.scale;
419
+ const newTranslateY = centerY - y * canvas.transform.scale;
420
+ return updateTransform({
421
+ translateX: newTranslateX,
422
+ translateY: newTranslateY,
423
+ });
424
+ });
425
+ }
426
+
367
427
  function createMatrixString(matrix) {
368
428
  return `matrix3d(${matrix.m11}, ${matrix.m12}, ${matrix.m13}, ${matrix.m14}, ${matrix.m21}, ${matrix.m22}, ${matrix.m23}, ${matrix.m24}, ${matrix.m31}, ${matrix.m32}, ${matrix.m33}, ${matrix.m34}, ${matrix.m41}, ${matrix.m42}, ${matrix.m43}, ${matrix.m44})`;
369
429
  }
@@ -395,136 +455,296 @@
395
455
  }
396
456
  }
397
457
 
398
- function disableTransition(element, config) {
399
- try {
400
- if (config.enableTransition) {
401
- if (window.__markupCanvasTransitionTimeout) {
402
- clearTimeout(window.__markupCanvasTransitionTimeout);
403
- window.__markupCanvasTransitionTimeout = undefined;
404
- }
405
- const delay = (config.transitionDuration ?? FALLBACK_TRANSITION_DURATION) * 1000;
406
- withDebounce("disableTransition", delay, () => {
407
- element.style.transition = "none";
408
- window.__markupCanvasTransitionTimeout = undefined;
409
- });
410
- return true;
411
- }
412
- return false;
458
+ function updateTransform(canvas, newTransform) {
459
+ canvas.transform = { ...canvas.transform, ...newTransform };
460
+ const matrix = createMatrix(canvas.transform.scale, canvas.transform.translateX, canvas.transform.translateY);
461
+ return applyTransform(canvas.transformLayer, matrix);
462
+ }
463
+
464
+ function resetTransform(canvas) {
465
+ const resetTransformData = {
466
+ scale: 1.0,
467
+ translateX: 0,
468
+ translateY: 0,
469
+ };
470
+ return updateTransform(canvas, resetTransformData);
471
+ }
472
+
473
+ function hideGrid(rulers) {
474
+ if (rulers?.gridOverlay) {
475
+ rulers.gridOverlay.style.display = "none";
476
+ return true;
413
477
  }
414
- catch (error) {
415
- console.error("Failed to disable transitions:", error);
478
+ return false;
479
+ }
480
+
481
+ function isGridVisible(rulers) {
482
+ if (rulers?.gridOverlay) {
483
+ return rulers.gridOverlay.style.display !== "none";
484
+ }
485
+ return false;
486
+ }
487
+
488
+ function showGrid(rulers) {
489
+ if (rulers?.gridOverlay) {
490
+ rulers.gridOverlay.style.display = "block";
416
491
  return true;
417
492
  }
493
+ return false;
418
494
  }
419
495
 
420
- function enableTransition(element, config) {
421
- try {
422
- if (config.enableTransition) {
423
- if (window.__markupCanvasTransitionTimeout) {
424
- clearTimeout(window.__markupCanvasTransitionTimeout);
425
- window.__markupCanvasTransitionTimeout = undefined;
426
- }
427
- element.style.transition = `transform ${config.transitionDuration}s linear`;
428
- return true;
496
+ function toggleGrid(rulers) {
497
+ if (rulers?.toggleGrid) {
498
+ rulers.toggleGrid();
499
+ return true;
500
+ }
501
+ return false;
502
+ }
503
+
504
+ function areRulersVisible(rulers) {
505
+ if (rulers?.horizontalRuler) {
506
+ return rulers.horizontalRuler.style.display !== "none";
507
+ }
508
+ return false;
509
+ }
510
+
511
+ function hideRulers(rulers) {
512
+ if (rulers) {
513
+ rulers.hide();
514
+ return true;
515
+ }
516
+ return false;
517
+ }
518
+
519
+ function showRulers(rulers) {
520
+ if (rulers) {
521
+ rulers.show();
522
+ return true;
523
+ }
524
+ return false;
525
+ }
526
+
527
+ function toggleRulers(rulers, areRulersVisible) {
528
+ if (rulers) {
529
+ const isVisible = areRulersVisible();
530
+ if (isVisible) {
531
+ rulers.hide();
429
532
  }
430
- return false;
533
+ else {
534
+ rulers.show();
535
+ }
536
+ return true;
537
+ }
538
+ return false;
539
+ }
540
+
541
+ function toggleTransition(enableTransition) {
542
+ return !enableTransition;
543
+ }
544
+
545
+ function createMarkupCanvasConfig(options = {}) {
546
+ const config = {
547
+ ...DEFAULT_CONFIG,
548
+ ...options,
549
+ };
550
+ if (typeof config.width !== "number" || config.width <= 0) {
551
+ console.warn("Invalid width, using default");
552
+ config.width = DEFAULT_CONFIG.width;
553
+ }
554
+ if (typeof config.height !== "number" || config.height <= 0) {
555
+ console.warn("Invalid height, using default");
556
+ config.height = DEFAULT_CONFIG.height;
557
+ }
558
+ if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
559
+ console.warn("Invalid zoomSpeed, using default");
560
+ config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
561
+ }
562
+ if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
563
+ console.warn("Invalid minZoom, using default");
564
+ config.minZoom = DEFAULT_CONFIG.minZoom;
565
+ }
566
+ if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
567
+ console.warn("Invalid maxZoom, using default");
568
+ config.maxZoom = DEFAULT_CONFIG.maxZoom;
569
+ }
570
+ if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
571
+ console.warn("Invalid keyboardPanStep, using default");
572
+ config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
573
+ }
574
+ if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
575
+ console.warn("Invalid keyboardFastMultiplier, using default");
576
+ config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
577
+ }
578
+ if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
579
+ console.warn("Invalid clickZoomLevel, using default");
580
+ config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
581
+ }
582
+ if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
583
+ console.warn("Invalid rulerFontSize, using default");
584
+ config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
585
+ }
586
+ if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
587
+ console.warn("Invalid rulerSize, using default");
588
+ config.rulerSize = DEFAULT_CONFIG.rulerSize;
589
+ }
590
+ return config;
591
+ }
592
+
593
+ function updateThemeMode(canvasContainer, config, rulers, mode) {
594
+ const newConfig = {
595
+ ...config,
596
+ themeMode: mode,
597
+ };
598
+ const updatedConfig = createMarkupCanvasConfig(newConfig);
599
+ // Update canvas background color
600
+ const backgroundColor = getThemeValue(updatedConfig, "canvasBackgroundColor");
601
+ canvasContainer.style.backgroundColor = backgroundColor;
602
+ // Update rulers if they exist
603
+ if (rulers) {
604
+ rulers.updateTheme(updatedConfig);
605
+ }
606
+ }
607
+
608
+ function resetView(canvas, transformLayer, config) {
609
+ return withTransition(transformLayer, config, () => {
610
+ return withRulerSize(canvas, config.rulerSize, (rulerSize) => {
611
+ const resetTransformData = {
612
+ scale: 1.0,
613
+ translateX: rulerSize * -1,
614
+ translateY: rulerSize * -1,
615
+ };
616
+ return updateTransform(canvas, resetTransformData);
617
+ });
618
+ });
619
+ }
620
+
621
+ function getViewportCenter(canvas) {
622
+ try {
623
+ const bounds = canvas.getBounds();
624
+ return {
625
+ x: bounds.width / 2,
626
+ y: bounds.height / 2,
627
+ };
431
628
  }
432
629
  catch (error) {
433
- console.error("Failed to enable transitions:", error);
434
- return false;
630
+ console.warn("Failed to calculate viewport center:", error);
631
+ return { x: 0, y: 0 };
435
632
  }
436
633
  }
437
634
 
438
- function withTransition(element, config, operation) {
439
- enableTransition(element, config);
440
- try {
441
- const result = operation();
442
- return result;
635
+ function resetViewToCenter(canvas, transformLayer, config, zoomToPoint) {
636
+ return withTransition(transformLayer, config, () => {
637
+ return withClampedZoom(config, (clamp) => {
638
+ const newScale = clamp(1.0);
639
+ const center = getViewportCenter(canvas);
640
+ return zoomToPoint(center.x, center.y, newScale);
641
+ });
642
+ });
643
+ }
644
+
645
+ function setZoom(canvas, transformLayer, config, zoomToPoint, zoomLevel) {
646
+ return withTransition(transformLayer, config, () => {
647
+ return withClampedZoom(config, (clamp) => {
648
+ const newScale = clamp(zoomLevel);
649
+ const center = getViewportCenter(canvas);
650
+ return zoomToPoint(center.x, center.y, newScale);
651
+ });
652
+ });
653
+ }
654
+
655
+ function zoomIn(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
656
+ return withTransition(transformLayer, config, () => {
657
+ return withClampedZoom(config, (clamp) => {
658
+ const newScale = clamp(canvas.transform.scale * (1 + factor));
659
+ const center = getViewportCenter(canvas);
660
+ return zoomToPoint(center.x, center.y, newScale);
661
+ });
662
+ });
663
+ }
664
+
665
+ function zoomOut(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
666
+ return withTransition(transformLayer, config, () => {
667
+ return withClampedZoom(config, (clamp) => {
668
+ const newScale = clamp(canvas.transform.scale * (1 - factor));
669
+ const center = getViewportCenter(canvas);
670
+ return zoomToPoint(center.x, center.y, newScale);
671
+ });
672
+ });
673
+ }
674
+
675
+ function zoomToPoint(canvas, transformLayer, config, x, y, targetScale) {
676
+ return withTransition(transformLayer, config, () => {
677
+ const newTransform = getZoomToMouseTransform(x, y, canvas.transform, targetScale / canvas.transform.scale, config);
678
+ return updateTransform(canvas, newTransform);
679
+ });
680
+ }
681
+
682
+ function fitToScreen(canvas, transformLayer, config) {
683
+ return withTransition(transformLayer, config, () => {
684
+ const bounds = getCanvasBounds(canvas, config);
685
+ const scaleX = bounds.width / config.width;
686
+ const scaleY = bounds.height / config.height;
687
+ const fitScale = withClampedZoom(config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
688
+ // Center the content
689
+ const scaledWidth = config.width * fitScale;
690
+ const scaledHeight = config.height * fitScale;
691
+ const centerX = (bounds.width - scaledWidth) / 2;
692
+ const centerY = (bounds.height - scaledHeight) / 2;
693
+ return updateTransform(canvas, {
694
+ scale: fitScale,
695
+ translateX: centerX,
696
+ translateY: centerY,
697
+ });
698
+ });
699
+ }
700
+
701
+ function moveExistingContent(existingContent, contentLayer, transformLayer) {
702
+ existingContent.forEach((child) => {
703
+ if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
704
+ contentLayer.appendChild(child);
705
+ }
706
+ });
707
+ }
708
+
709
+ function setupContentLayer(contentLayer) {
710
+ contentLayer.style.position = "relative";
711
+ contentLayer.style.width = "100%";
712
+ contentLayer.style.height = "100%";
713
+ contentLayer.style.pointerEvents = "auto";
714
+ }
715
+
716
+ // Sets up the transform layer with proper styles and dimensions
717
+ function setupTransformLayer(transformLayer, config) {
718
+ transformLayer.style.position = "absolute";
719
+ const rulerOffset = config.rulerSize;
720
+ transformLayer.style.top = `${rulerOffset}px`;
721
+ transformLayer.style.left = `${rulerOffset}px`;
722
+ transformLayer.style.width = `${config.width}px`;
723
+ transformLayer.style.height = `${config.height}px`;
724
+ transformLayer.style.transformOrigin = "0 0";
725
+ }
726
+
727
+ function createCanvasLayers(container, config) {
728
+ const existingContent = Array.from(container.children);
729
+ // Create or find transform layer
730
+ let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
731
+ if (!transformLayer) {
732
+ transformLayer = document.createElement("div");
733
+ transformLayer.className = TRANSFORM_LAYER_CLASS;
734
+ container.appendChild(transformLayer);
443
735
  }
444
- finally {
445
- disableTransition(element, config);
736
+ setupTransformLayer(transformLayer, config);
737
+ // Create or find content layer
738
+ let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
739
+ if (!contentLayer) {
740
+ contentLayer = document.createElement("div");
741
+ contentLayer.className = CONTENT_LAYER_CLASS;
742
+ transformLayer.appendChild(contentLayer);
743
+ moveExistingContent(existingContent, contentLayer, transformLayer);
446
744
  }
447
- }
448
-
449
- function getCanvasMethods() {
450
- return {
451
- // Utility methods
452
- getBounds: function () {
453
- return getCanvasBounds(this);
454
- },
455
- // Transform methods
456
- updateTransform: function (newTransform) {
457
- this.transform = { ...this.transform, ...newTransform };
458
- const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
459
- const result = applyTransform(this.transformLayer, matrix);
460
- withFeatureEnabled(this.config, "onTransformUpdate", () => {
461
- this.config.onTransformUpdate(this.transform);
462
- });
463
- return result;
464
- },
465
- // Reset method
466
- reset: function () {
467
- const resetTransform = {
468
- scale: 1.0,
469
- translateX: 0,
470
- translateY: 0,
471
- };
472
- return this.updateTransform(resetTransform);
473
- },
474
- // Handle canvas resize
475
- handleResize: function () {
476
- return true;
477
- },
478
- // Set zoom level
479
- setZoom: function (zoomLevel) {
480
- const newScale = withClampedZoom(this.config, (clamp) => clamp(zoomLevel));
481
- return this.updateTransform({ scale: newScale });
482
- },
483
- // Convert canvas coordinates to content coordinates
484
- canvasToContent: function (x, y) {
485
- const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
486
- return canvasToContent(x, y, matrix);
487
- },
488
- // Zoom to a specific point with animation
489
- zoomToPoint: function (x, y, targetScale) {
490
- return withTransition(this.transformLayer, this.config, () => {
491
- const newTransform = getZoomToMouseTransform(x, y, this.transform, targetScale / this.transform.scale, this.config);
492
- return this.updateTransform(newTransform);
493
- });
494
- },
495
- // Reset view with animation
496
- resetView: function () {
497
- return withTransition(this.transformLayer, this.config, () => {
498
- return withRulerSize(this, this.config.rulerSize, (rulerSize) => {
499
- const resetTransform = {
500
- scale: 1.0,
501
- translateX: rulerSize * -1,
502
- translateY: rulerSize * -1,
503
- };
504
- return this.updateTransform(resetTransform);
505
- });
506
- });
507
- },
508
- // Zoom to fit content in canvas
509
- zoomToFitContent: function () {
510
- return withTransition(this.transformLayer, this.config, () => {
511
- const bounds = this.getBounds();
512
- const scaleX = bounds.width / this.config.width;
513
- const scaleY = bounds.height / this.config.height;
514
- const fitScale = withClampedZoom(this.config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
515
- // Center the content
516
- const scaledWidth = this.config.width * fitScale;
517
- const scaledHeight = this.config.height * fitScale;
518
- const centerX = (bounds.width - scaledWidth) / 2;
519
- const centerY = (bounds.height - scaledHeight) / 2;
520
- return this.updateTransform({
521
- scale: fitScale,
522
- translateX: centerX,
523
- translateY: centerY,
524
- });
525
- });
526
- },
527
- };
745
+ // Set content layer properties
746
+ setupContentLayer(contentLayer);
747
+ return { transformLayer, contentLayer };
528
748
  }
529
749
 
530
750
  function checkContainerDimensions(container) {
@@ -560,7 +780,6 @@
560
780
  }
561
781
  }
562
782
 
563
- // Creates and initializes a canvas with the required DOM structure
564
783
  function createCanvas(container, config) {
565
784
  if (!container?.appendChild) {
566
785
  console.error("Invalid container element provided to createCanvas");
@@ -569,10 +788,9 @@
569
788
  try {
570
789
  setupCanvasContainer(container, config);
571
790
  const { transformLayer, contentLayer } = createCanvasLayers(container, config);
572
- // Enable hardware acceleration if requested
573
- if (config.enableAcceleration) {
791
+ withFeatureEnabled(config, "enableAcceleration", () => {
574
792
  enableHardwareAcceleration(transformLayer);
575
- }
793
+ });
576
794
  const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
577
795
  const initialTransform = {
578
796
  scale: DEFAULT_ZOOM,
@@ -587,12 +805,8 @@
587
805
  container,
588
806
  transformLayer,
589
807
  contentLayer,
590
- // Configuration
591
- config: config,
592
808
  // Current state
593
809
  transform: initialTransform,
594
- // Add all canvas methods
595
- ...getCanvasMethods(),
596
810
  };
597
811
  return canvas;
598
812
  }
@@ -602,54 +816,6 @@
602
816
  }
603
817
  }
604
818
 
605
- function createMarkupCanvasConfig(options = {}) {
606
- const config = {
607
- ...DEFAULT_CONFIG,
608
- ...options,
609
- };
610
- if (typeof config.width !== "number" || config.width <= 0) {
611
- console.warn("Invalid width, using default");
612
- config.width = DEFAULT_CONFIG.width;
613
- }
614
- if (typeof config.height !== "number" || config.height <= 0) {
615
- console.warn("Invalid height, using default");
616
- config.height = DEFAULT_CONFIG.height;
617
- }
618
- if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
619
- console.warn("Invalid zoomSpeed, using default");
620
- config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
621
- }
622
- if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
623
- console.warn("Invalid minZoom, using default");
624
- config.minZoom = DEFAULT_CONFIG.minZoom;
625
- }
626
- if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
627
- console.warn("Invalid maxZoom, using default");
628
- config.maxZoom = DEFAULT_CONFIG.maxZoom;
629
- }
630
- if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
631
- console.warn("Invalid keyboardPanStep, using default");
632
- config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
633
- }
634
- if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
635
- console.warn("Invalid keyboardFastMultiplier, using default");
636
- config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
637
- }
638
- if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
639
- console.warn("Invalid clickZoomLevel, using default");
640
- config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
641
- }
642
- if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
643
- console.warn("Invalid rulerFontSize, using default");
644
- config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
645
- }
646
- if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
647
- console.warn("Invalid rulerSize, using default");
648
- config.rulerSize = DEFAULT_CONFIG.rulerSize;
649
- }
650
- return config;
651
- }
652
-
653
819
  class EventEmitter {
654
820
  constructor() {
655
821
  this.listeners = new Map();
@@ -688,6 +854,13 @@
688
854
  }
689
855
  }
690
856
 
857
+ function emitTransformEvents(listen, canvas) {
858
+ const transform = canvas.transform;
859
+ listen.emit("transform", transform);
860
+ listen.emit("zoom", transform.scale);
861
+ listen.emit("pan", { x: transform.translateX, y: transform.translateY });
862
+ }
863
+
691
864
  const REFERENCE_DISPLAY_AREA = 1920 * 1080;
692
865
  const TRACKPAD_PINCH_SPEED_FACTOR = 0.05;
693
866
  const ADAPTIVE_ZOOM_FACTOR = 1;
@@ -714,42 +887,28 @@
714
887
  }
715
888
 
716
889
  function setupKeyboardEvents(canvas, config) {
717
- // Track mouse position
718
- let lastMouseX = 0;
719
- let lastMouseY = 0;
720
- function handleMouseMove(event) {
721
- const rect = canvas.container.getBoundingClientRect();
722
- const rawMouseX = event.clientX - rect.left;
723
- const rawMouseY = event.clientY - rect.top;
724
- withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
725
- lastMouseX = adjustedX;
726
- lastMouseY = adjustedY;
727
- });
728
- }
729
890
  function handleKeyDown(event) {
730
891
  if (!(event instanceof KeyboardEvent))
731
892
  return;
732
893
  if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
733
894
  return;
734
- const isFastPan = event.shiftKey;
735
- const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
736
895
  let handled = false;
737
896
  const newTransform = {};
738
897
  switch (event.key) {
739
898
  case "ArrowLeft":
740
- newTransform.translateX = canvas.transform.translateX + panDistance;
899
+ newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
741
900
  handled = true;
742
901
  break;
743
902
  case "ArrowRight":
744
- newTransform.translateX = canvas.transform.translateX - panDistance;
903
+ newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
745
904
  handled = true;
746
905
  break;
747
906
  case "ArrowUp":
748
- newTransform.translateY = canvas.transform.translateY + panDistance;
907
+ newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
749
908
  handled = true;
750
909
  break;
751
910
  case "ArrowDown":
752
- newTransform.translateY = canvas.transform.translateY - panDistance;
911
+ newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
753
912
  handled = true;
754
913
  break;
755
914
  case "=":
@@ -758,7 +917,7 @@
758
917
  const adaptiveZoomStep = config.enableAdaptiveSpeed
759
918
  ? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
760
919
  : config.keyboardZoomStep;
761
- newTransform.scale = clampZoom(canvas.transform.scale * (1 + adaptiveZoomStep), config);
920
+ canvas.zoomIn(adaptiveZoomStep);
762
921
  handled = true;
763
922
  }
764
923
  break;
@@ -767,16 +926,21 @@
767
926
  const adaptiveZoomStep = config.enableAdaptiveSpeed
768
927
  ? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
769
928
  : config.keyboardZoomStep;
770
- newTransform.scale = clampZoom(canvas.transform.scale * (1 - adaptiveZoomStep), config);
929
+ canvas.zoomOut(adaptiveZoomStep);
771
930
  handled = true;
772
931
  }
773
932
  break;
774
933
  case "0":
775
- if (event.metaKey || event.ctrlKey) {
776
- const targetScale = 1.0;
777
- const zoomFactor = targetScale / canvas.transform.scale;
778
- const zoomTransform = getZoomToMouseTransform(lastMouseX, lastMouseY, canvas.transform, zoomFactor, config);
779
- Object.assign(newTransform, zoomTransform);
934
+ if (event.ctrlKey) {
935
+ if (canvas.resetView) {
936
+ canvas.resetView();
937
+ }
938
+ handled = true;
939
+ }
940
+ else if (event.metaKey || event.ctrlKey) {
941
+ if (canvas.resetViewToCenter) {
942
+ canvas.resetViewToCenter();
943
+ }
780
944
  handled = true;
781
945
  }
782
946
  break;
@@ -804,10 +968,8 @@
804
968
  }
805
969
  const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
806
970
  keyboardTarget.addEventListener("keydown", handleKeyDown);
807
- canvas.container.addEventListener("mousemove", handleMouseMove);
808
971
  return () => {
809
972
  keyboardTarget.removeEventListener("keydown", handleKeyDown);
810
- canvas.container.removeEventListener("mousemove", handleMouseMove);
811
973
  };
812
974
  }
813
975
 
@@ -1193,6 +1355,17 @@
1193
1355
  const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
1194
1356
  canvas.updateThemeMode(newMode);
1195
1357
  }
1358
+ // Transition methods
1359
+ else if (action === "updateTransition") {
1360
+ const enabled = args[0];
1361
+ if (typeof enabled !== "boolean") {
1362
+ throw new Error(`Invalid transition enabled value: ${enabled}. Must be a boolean.`);
1363
+ }
1364
+ canvas.updateTransition(enabled);
1365
+ }
1366
+ else if (action === "toggleTransitionMode") {
1367
+ canvas.toggleTransitionMode();
1368
+ }
1196
1369
  else {
1197
1370
  throw new Error(`Unknown action: ${action}`);
1198
1371
  }
@@ -1790,8 +1963,6 @@
1790
1963
  elements.verticalRuler.style.display = "block";
1791
1964
  if (elements.cornerBox)
1792
1965
  elements.cornerBox.style.display = "flex";
1793
- if (elements.gridOverlay)
1794
- elements.gridOverlay.style.display = "block";
1795
1966
  },
1796
1967
  hide: () => {
1797
1968
  if (elements.horizontalRuler)
@@ -1835,14 +2006,163 @@
1835
2006
  }
1836
2007
  }
1837
2008
 
2009
+ function broadcastEvent(event, data, config) {
2010
+ if (typeof window === "undefined") {
2011
+ return;
2012
+ }
2013
+ // Receivers can get the instance from the window binding
2014
+ let broadcastData = data;
2015
+ if (event === "ready") {
2016
+ broadcastData = { ready: true };
2017
+ }
2018
+ window.postMessage({
2019
+ source: "markup-canvas",
2020
+ event,
2021
+ data: broadcastData,
2022
+ timestamp: Date.now(),
2023
+ canvasName: config.name,
2024
+ }, "*");
2025
+ if (window.parent) {
2026
+ window.parent.postMessage({
2027
+ source: "markup-canvas",
2028
+ event,
2029
+ data: broadcastData,
2030
+ timestamp: Date.now(),
2031
+ canvasName: config.name,
2032
+ }, "*");
2033
+ }
2034
+ }
2035
+
2036
+ function cleanupWindowBinding(config) {
2037
+ if (typeof window === "undefined") {
2038
+ return;
2039
+ }
2040
+ const canvasName = config.name || "markupCanvas";
2041
+ const windowObj = window;
2042
+ delete windowObj[canvasName];
2043
+ if (windowObj.__markupCanvasInstances) {
2044
+ windowObj.__markupCanvasInstances.delete(canvasName);
2045
+ }
2046
+ }
2047
+
2048
+ function bindCanvasToWindow(canvas, config) {
2049
+ if (typeof window === "undefined") {
2050
+ return;
2051
+ }
2052
+ const canvasName = config.name || "markupCanvas";
2053
+ const windowObj = window;
2054
+ const api = {
2055
+ config: {
2056
+ get current() {
2057
+ return canvas.config;
2058
+ },
2059
+ get: canvas.getConfig.bind(canvas),
2060
+ update: canvas.updateConfig.bind(canvas),
2061
+ },
2062
+ // Transform group
2063
+ transform: {
2064
+ update: canvas.updateTransform.bind(canvas),
2065
+ reset: canvas.reset.bind(canvas),
2066
+ },
2067
+ // Zoom group
2068
+ zoom: {
2069
+ set: canvas.setZoom.bind(canvas),
2070
+ toPoint: canvas.zoomToPoint.bind(canvas),
2071
+ in: canvas.zoomIn.bind(canvas),
2072
+ out: canvas.zoomOut.bind(canvas),
2073
+ reset: canvas.resetZoom.bind(canvas),
2074
+ resetToCenter: canvas.resetViewToCenter.bind(canvas),
2075
+ fitToScreen: canvas.fitToScreen.bind(canvas),
2076
+ },
2077
+ // Pan group
2078
+ pan: {
2079
+ left: canvas.panLeft.bind(canvas),
2080
+ right: canvas.panRight.bind(canvas),
2081
+ up: canvas.panUp.bind(canvas),
2082
+ down: canvas.panDown.bind(canvas),
2083
+ toPoint: canvas.scrollToPoint.bind(canvas),
2084
+ toCenter: canvas.centerContent.bind(canvas),
2085
+ },
2086
+ // Mouse drag group
2087
+ mouseDrag: {
2088
+ enable: canvas.enableMouseDrag.bind(canvas),
2089
+ disable: canvas.disableMouseDrag.bind(canvas),
2090
+ isEnabled: canvas.isMouseDragEnabled.bind(canvas),
2091
+ },
2092
+ // Grid group
2093
+ grid: {
2094
+ toggle: canvas.toggleGrid.bind(canvas),
2095
+ show: canvas.showGrid.bind(canvas),
2096
+ hide: canvas.hideGrid.bind(canvas),
2097
+ isVisible: canvas.isGridVisible.bind(canvas),
2098
+ },
2099
+ // Ruler group
2100
+ rulers: {
2101
+ toggle: canvas.toggleRulers.bind(canvas),
2102
+ show: canvas.showRulers.bind(canvas),
2103
+ hide: canvas.hideRulers.bind(canvas),
2104
+ isVisible: canvas.areRulersVisible.bind(canvas),
2105
+ },
2106
+ // Utility group
2107
+ canvas: {
2108
+ canvasToContent: canvas.canvasToContent.bind(canvas),
2109
+ getVisibleArea: canvas.getVisibleArea.bind(canvas),
2110
+ isPointVisible: canvas.isPointVisible.bind(canvas),
2111
+ getBounds: canvas.getBounds.bind(canvas),
2112
+ },
2113
+ theme: {
2114
+ get current() {
2115
+ return canvas.config.themeMode;
2116
+ },
2117
+ update: canvas.updateThemeMode.bind(canvas),
2118
+ toggle: canvas.toggleThemeMode.bind(canvas),
2119
+ },
2120
+ transition: {
2121
+ get current() {
2122
+ return canvas.config.enableTransition;
2123
+ },
2124
+ set: canvas.updateTransition.bind(canvas),
2125
+ toggle: canvas.toggleTransitionMode.bind(canvas),
2126
+ },
2127
+ // Event group
2128
+ event: canvas.event,
2129
+ // Lifecycle group
2130
+ lifecycle: {
2131
+ cleanup: canvas.cleanup.bind(canvas),
2132
+ destroy: canvas.destroy.bind(canvas),
2133
+ },
2134
+ // Properties/State group
2135
+ state: {
2136
+ get isReady() {
2137
+ return canvas.isReady;
2138
+ },
2139
+ get isTransforming() {
2140
+ return canvas.isTransforming;
2141
+ },
2142
+ get visibleBounds() {
2143
+ return canvas.visibleBounds;
2144
+ },
2145
+ get transform() {
2146
+ return canvas.transform;
2147
+ },
2148
+ },
2149
+ };
2150
+ // Bind public API to window
2151
+ windowObj[canvasName] = api;
2152
+ // Track all instances
2153
+ if (!windowObj.__markupCanvasInstances) {
2154
+ windowObj.__markupCanvasInstances = new Map();
2155
+ }
2156
+ windowObj.__markupCanvasInstances.set(canvasName, api);
2157
+ }
2158
+
1838
2159
  class MarkupCanvas {
1839
2160
  constructor(container, options = {}) {
1840
- this.cleanupFunctions = [];
2161
+ this.cleanupCallbacks = [];
1841
2162
  this.rulers = null;
1842
2163
  this.dragSetup = null;
2164
+ this.event = new EventEmitter();
1843
2165
  this._isReady = false;
1844
- this.listen = new EventEmitter();
1845
- this.postMessageCleanup = null;
1846
2166
  if (!container) {
1847
2167
  throw new Error("Container element is required");
1848
2168
  }
@@ -1851,100 +2171,48 @@
1851
2171
  if (!canvas) {
1852
2172
  throw new Error("Failed to create canvas");
1853
2173
  }
1854
- this.baseCanvas = canvas;
1855
- if (this.config.bindToWindow) {
1856
- this.listen.setEmitInterceptor((event, data) => {
1857
- this.broadcastEvent(event, data);
1858
- });
1859
- this.setupGlobalBinding();
1860
- // Set up postMessage listener
1861
- if (this.config.enablePostMessageAPI) {
1862
- this.postMessageCleanup = setupPostMessageEvents(this);
1863
- }
2174
+ this.canvas = canvas;
2175
+ // Always bind canvas to window
2176
+ this.event.setEmitInterceptor((event, data) => {
2177
+ broadcastEvent(event, data, this.config);
2178
+ });
2179
+ bindCanvasToWindow(this, this.config);
2180
+ // Set up postMessage listener
2181
+ if (this.config.enablePostMessageAPI) {
2182
+ const postMessageCleanup = setupPostMessageEvents(this);
2183
+ this.cleanupCallbacks.push(postMessageCleanup);
1864
2184
  }
1865
2185
  this.setupEventHandlers();
1866
2186
  this._isReady = true;
1867
2187
  // Emit ready event
1868
- this.listen.emit("ready", this);
1869
- }
1870
- setupGlobalBinding() {
1871
- if (typeof window === "undefined") {
1872
- return;
1873
- }
1874
- const canvasName = this.config.name || "markupCanvas";
1875
- const windowObj = window;
1876
- // Bind instance to window
1877
- windowObj[canvasName] = this;
1878
- // Track all instances
1879
- if (!windowObj.__markupCanvasInstances) {
1880
- windowObj.__markupCanvasInstances = new Map();
1881
- }
1882
- windowObj.__markupCanvasInstances.set(canvasName, this);
1883
- }
1884
- cleanupGlobalBinding() {
1885
- if (typeof window === "undefined") {
1886
- return;
1887
- }
1888
- const canvasName = this.config.name || "markupCanvas";
1889
- const windowObj = window;
1890
- delete windowObj[canvasName];
1891
- if (windowObj.__markupCanvasInstances) {
1892
- windowObj.__markupCanvasInstances.delete(canvasName);
1893
- }
1894
- }
1895
- broadcastEvent(event, data) {
1896
- if (typeof window === "undefined") {
1897
- return;
1898
- }
1899
- // Receivers can get the instance from the window binding
1900
- let broadcastData = data;
1901
- if (event === "ready") {
1902
- broadcastData = { ready: true };
1903
- }
1904
- window.postMessage({
1905
- source: "markup-canvas",
1906
- event,
1907
- data: broadcastData,
1908
- timestamp: Date.now(),
1909
- canvasName: this.config.name,
1910
- }, "*");
1911
- if (window.parent) {
1912
- window.parent.postMessage({
1913
- source: "markup-canvas",
1914
- event,
1915
- data: broadcastData,
1916
- timestamp: Date.now(),
1917
- canvasName: this.config.name,
1918
- }, "*");
1919
- }
2188
+ this.event.emit("ready", this);
1920
2189
  }
1921
2190
  setupEventHandlers() {
1922
2191
  try {
1923
- // Wheel zoom
2192
+ // Wheel events
1924
2193
  withFeatureEnabled(this.config, "enableZoom", () => {
1925
2194
  const wheelCleanup = setupWheelEvents(this, this.config);
1926
- this.cleanupFunctions.push(wheelCleanup);
2195
+ this.cleanupCallbacks.push(wheelCleanup);
1927
2196
  });
1928
- // Mouse events (drag and click-to-zoom)
1929
- // Set up mouse events if either pan or click-to-zoom is enabled
2197
+ // Mouse events
1930
2198
  if (this.config.enablePan || this.config.enableClickToZoom) {
1931
2199
  this.dragSetup = setupMouseEvents(this, this.config, true);
1932
- this.cleanupFunctions.push(this.dragSetup.cleanup);
2200
+ this.cleanupCallbacks.push(this.dragSetup.cleanup);
1933
2201
  }
1934
- // Keyboard navigation
2202
+ // Keyboard events
1935
2203
  withFeatureEnabled(this.config, "enableKeyboard", () => {
1936
2204
  const keyboardCleanup = setupKeyboardEvents(this, this.config);
1937
- this.cleanupFunctions.push(keyboardCleanup);
2205
+ this.cleanupCallbacks.push(keyboardCleanup);
1938
2206
  });
1939
- // Touch events (if enabled)
2207
+ // Touch events
1940
2208
  withFeatureEnabled(this.config, "enableTouch", () => {
1941
2209
  const touchCleanup = setupTouchEvents(this);
1942
- this.cleanupFunctions.push(touchCleanup);
2210
+ this.cleanupCallbacks.push(touchCleanup);
1943
2211
  });
1944
2212
  // Set up rulers and grid
1945
2213
  withFeatureEnabled(this.config, "enableRulers", () => {
1946
- this.rulers = createRulers(this.baseCanvas, this.config);
1947
- this.cleanupFunctions.push(() => {
2214
+ this.rulers = createRulers(this, this.config);
2215
+ this.cleanupCallbacks.push(() => {
1948
2216
  if (this.rulers) {
1949
2217
  this.rulers.destroy();
1950
2218
  }
@@ -1957,20 +2225,18 @@
1957
2225
  throw error;
1958
2226
  }
1959
2227
  }
1960
- // Base canvas properties and methods
1961
2228
  get container() {
1962
- return this.baseCanvas.container;
2229
+ return this.canvas.container;
1963
2230
  }
1964
2231
  get transformLayer() {
1965
- return this.baseCanvas.transformLayer;
2232
+ return this.canvas.transformLayer;
1966
2233
  }
1967
2234
  get contentLayer() {
1968
- return this.baseCanvas.contentLayer;
2235
+ return this.canvas.contentLayer;
1969
2236
  }
1970
2237
  get transform() {
1971
- return this.baseCanvas.transform;
2238
+ return this.canvas.transform;
1972
2239
  }
1973
- // State management getters for React integration
1974
2240
  get isReady() {
1975
2241
  return this._isReady;
1976
2242
  }
@@ -1978,125 +2244,65 @@
1978
2244
  return this.dragSetup?.isEnabled() || false;
1979
2245
  }
1980
2246
  get visibleBounds() {
1981
- return this.getVisibleArea();
2247
+ return getVisibleArea(this);
1982
2248
  }
1983
2249
  getBounds() {
1984
- return this.baseCanvas.getBounds();
2250
+ return getCanvasBounds(this.canvas, this.config);
1985
2251
  }
1986
2252
  updateTransform(newTransform) {
1987
- const result = this.baseCanvas.updateTransform(newTransform);
2253
+ const result = updateTransform(this.canvas, newTransform);
1988
2254
  if (result) {
1989
- this.emitTransformEvents();
2255
+ emitTransformEvents(this.event, this.canvas);
1990
2256
  }
1991
2257
  return result;
1992
2258
  }
1993
- emitTransformEvents() {
1994
- const transform = this.baseCanvas.transform;
1995
- this.listen.emit("transform", transform);
1996
- this.listen.emit("zoom", transform.scale);
1997
- this.listen.emit("pan", { x: transform.translateX, y: transform.translateY });
1998
- }
1999
2259
  reset() {
2000
- return this.baseCanvas.reset();
2001
- }
2002
- handleResize() {
2003
- return this.baseCanvas.handleResize();
2260
+ const result = resetTransform(this.canvas);
2261
+ if (result) {
2262
+ emitTransformEvents(this.event, this.canvas);
2263
+ }
2264
+ return result;
2004
2265
  }
2005
2266
  setZoom(zoomLevel) {
2006
- return withTransition(this.transformLayer, this.config, () => {
2007
- return withClampedZoom(this.config, (clamp) => {
2008
- const newScale = clamp(zoomLevel);
2009
- const newTransform = {
2010
- scale: newScale,
2011
- };
2012
- return this.updateTransform(newTransform);
2013
- });
2014
- });
2267
+ return setZoom(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), zoomLevel);
2015
2268
  }
2016
2269
  canvasToContent(x, y) {
2017
- return this.baseCanvas.canvasToContent(x, y);
2270
+ const matrix = createMatrix(this.canvas.transform.scale, this.canvas.transform.translateX, this.canvas.transform.translateY);
2271
+ return canvasToContent(x, y, matrix);
2018
2272
  }
2019
2273
  zoomToPoint(x, y, targetScale) {
2020
- return withTransition(this.transformLayer, this.config, () => {
2021
- const result = this.baseCanvas.zoomToPoint(x, y, targetScale);
2022
- if (result) {
2023
- this.emitTransformEvents();
2024
- }
2025
- return result;
2026
- });
2274
+ return zoomToPoint(this.canvas, this.transformLayer, this.config, x, y, targetScale);
2027
2275
  }
2028
2276
  resetView() {
2029
- return withTransition(this.transformLayer, this.config, () => {
2030
- const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
2031
- if (result) {
2032
- this.emitTransformEvents();
2033
- }
2034
- return result;
2035
- });
2277
+ return resetView(this.canvas, this.transformLayer, this.config);
2036
2278
  }
2037
- zoomToFitContent() {
2038
- return withTransition(this.transformLayer, this.config, () => {
2039
- const result = this.baseCanvas.zoomToFitContent();
2040
- if (result) {
2041
- this.emitTransformEvents();
2042
- }
2043
- return result;
2044
- });
2279
+ resetViewToCenter() {
2280
+ return resetViewToCenter(this, this.transformLayer, this.config, this.zoomToPoint.bind(this));
2045
2281
  }
2046
- // Pan methods
2047
2282
  panLeft(distance) {
2048
- const panDistance = distance ?? this.config.keyboardPanStep;
2049
- const newTransform = {
2050
- translateX: this.baseCanvas.transform.translateX + panDistance,
2051
- };
2052
- return this.updateTransform(newTransform);
2283
+ return (panLeft(this.canvas, this.config, this.updateTransform.bind(this)) ||
2284
+ (distance ? panLeft(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2053
2285
  }
2054
2286
  panRight(distance) {
2055
- const panDistance = distance ?? this.config.keyboardPanStep;
2056
- const newTransform = {
2057
- translateX: this.baseCanvas.transform.translateX - panDistance,
2058
- };
2059
- return this.updateTransform(newTransform);
2287
+ return (panRight(this.canvas, this.config, this.updateTransform.bind(this)) ||
2288
+ (distance ? panRight(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2060
2289
  }
2061
2290
  panUp(distance) {
2062
- const panDistance = distance ?? this.config.keyboardPanStep;
2063
- const newTransform = {
2064
- translateY: this.baseCanvas.transform.translateY + panDistance,
2065
- };
2066
- return this.updateTransform(newTransform);
2291
+ return (panUp(this.canvas, this.config, this.updateTransform.bind(this)) ||
2292
+ (distance ? panUp(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2067
2293
  }
2068
2294
  panDown(distance) {
2069
- const panDistance = distance ?? this.config.keyboardPanStep;
2070
- const newTransform = {
2071
- translateY: this.baseCanvas.transform.translateY - panDistance,
2072
- };
2073
- return this.updateTransform(newTransform);
2295
+ return (panDown(this.canvas, this.config, this.updateTransform.bind(this)) ||
2296
+ (distance ? panDown(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2074
2297
  }
2075
- // Zoom methods
2076
- zoomIn(factor = 0.1) {
2077
- return withTransition(this.transformLayer, this.config, () => {
2078
- return withClampedZoom(this.config, (clamp) => {
2079
- const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
2080
- const newTransform = {
2081
- scale: newScale,
2082
- };
2083
- return this.updateTransform(newTransform);
2084
- });
2085
- });
2298
+ zoomIn(factor = 0.5) {
2299
+ return zoomIn(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
2086
2300
  }
2087
- zoomOut(factor = 0.1) {
2088
- return withTransition(this.transformLayer, this.config, () => {
2089
- return withClampedZoom(this.config, (clamp) => {
2090
- const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
2091
- const newTransform = {
2092
- scale: newScale,
2093
- };
2094
- return this.updateTransform(newTransform);
2095
- });
2096
- });
2301
+ zoomOut(factor = 0.5) {
2302
+ return zoomOut(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
2097
2303
  }
2098
2304
  resetZoom() {
2099
- return this.resetView();
2305
+ return this.resetViewToCenter();
2100
2306
  }
2101
2307
  // Mouse drag control methods
2102
2308
  enableMouseDrag() {
@@ -2108,110 +2314,68 @@
2108
2314
  isMouseDragEnabled() {
2109
2315
  return this.dragSetup?.isEnabled() ?? false;
2110
2316
  }
2111
- // Grid control methods
2112
2317
  toggleGrid() {
2113
- if (this.rulers?.toggleGrid) {
2114
- this.rulers.toggleGrid();
2115
- return true;
2318
+ const result = toggleGrid(this.rulers);
2319
+ if (result) {
2320
+ this.event.emit("gridVisibility", this.isGridVisible());
2116
2321
  }
2117
- return false;
2322
+ return result;
2118
2323
  }
2119
2324
  showGrid() {
2120
- if (this.rulers?.gridOverlay) {
2121
- this.rulers.gridOverlay.style.display = "block";
2122
- return true;
2325
+ const result = showGrid(this.rulers);
2326
+ if (result) {
2327
+ this.event.emit("gridVisibility", true);
2123
2328
  }
2124
- return false;
2329
+ return result;
2125
2330
  }
2126
2331
  hideGrid() {
2127
- if (this.rulers?.gridOverlay) {
2128
- this.rulers.gridOverlay.style.display = "none";
2129
- return true;
2332
+ const result = hideGrid(this.rulers);
2333
+ if (result) {
2334
+ this.event.emit("gridVisibility", false);
2130
2335
  }
2131
- return false;
2336
+ return result;
2132
2337
  }
2133
2338
  isGridVisible() {
2134
- if (this.rulers?.gridOverlay) {
2135
- return this.rulers.gridOverlay.style.display !== "none";
2136
- }
2137
- return false;
2339
+ return isGridVisible(this.rulers);
2138
2340
  }
2139
- // Ruler control methods
2140
2341
  toggleRulers() {
2141
- if (this.rulers) {
2142
- const areVisible = this.areRulersVisible();
2143
- if (areVisible) {
2144
- this.rulers.hide();
2145
- }
2146
- else {
2147
- this.rulers.show();
2148
- }
2149
- return true;
2342
+ const result = toggleRulers(this.rulers, () => this.areRulersVisible());
2343
+ if (result) {
2344
+ this.event.emit("rulersVisibility", this.areRulersVisible());
2150
2345
  }
2151
- return false;
2346
+ return result;
2152
2347
  }
2153
2348
  showRulers() {
2154
- if (this.rulers) {
2155
- this.rulers.show();
2156
- return true;
2349
+ const result = showRulers(this.rulers);
2350
+ if (result) {
2351
+ this.event.emit("rulersVisibility", true);
2157
2352
  }
2158
- return false;
2353
+ return result;
2159
2354
  }
2160
2355
  hideRulers() {
2161
- if (this.rulers) {
2162
- this.rulers.hide();
2163
- return true;
2356
+ const result = hideRulers(this.rulers);
2357
+ if (result) {
2358
+ this.event.emit("rulersVisibility", false);
2164
2359
  }
2165
- return false;
2360
+ return result;
2166
2361
  }
2167
2362
  areRulersVisible() {
2168
- if (this.rulers?.horizontalRuler) {
2169
- return this.rulers.horizontalRuler.style.display !== "none";
2170
- }
2171
- return false;
2363
+ return areRulersVisible(this.rulers);
2172
2364
  }
2173
- // Utility methods
2174
2365
  centerContent() {
2175
- return withTransition(this.transformLayer, this.config, () => {
2176
- const bounds = this.baseCanvas.getBounds();
2177
- const centerX = (bounds.width - bounds.contentWidth * this.baseCanvas.transform.scale) / 2;
2178
- const centerY = (bounds.height - bounds.contentHeight * this.baseCanvas.transform.scale) / 2;
2179
- return this.updateTransform({
2180
- translateX: centerX,
2181
- translateY: centerY,
2182
- });
2183
- });
2366
+ return centerContent(this.canvas, this.config, this.updateTransform.bind(this), this.transformLayer);
2184
2367
  }
2185
2368
  fitToScreen() {
2186
- return withTransition(this.transformLayer, this.config, () => {
2187
- const result = this.baseCanvas.zoomToFitContent();
2188
- if (result) {
2189
- this.emitTransformEvents();
2190
- }
2191
- return result;
2192
- });
2369
+ return fitToScreen(this.canvas, this.transformLayer, this.config);
2193
2370
  }
2194
2371
  getVisibleArea() {
2195
- const bounds = this.baseCanvas.getBounds();
2196
- return bounds.visibleArea;
2372
+ return getVisibleArea(this);
2197
2373
  }
2198
2374
  isPointVisible(x, y) {
2199
- const visibleArea = this.getVisibleArea();
2200
- return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
2375
+ return isPointVisible(this, x, y);
2201
2376
  }
2202
2377
  scrollToPoint(x, y) {
2203
- return withTransition(this.transformLayer, this.config, () => {
2204
- const bounds = this.baseCanvas.getBounds();
2205
- const centerX = bounds.width / 2;
2206
- const centerY = bounds.height / 2;
2207
- // Calculate new translation to center the point
2208
- const newTranslateX = centerX - x * this.baseCanvas.transform.scale;
2209
- const newTranslateY = centerY - y * this.baseCanvas.transform.scale;
2210
- return this.updateTransform({
2211
- translateX: newTranslateX,
2212
- translateY: newTranslateY,
2213
- });
2214
- });
2378
+ return scrollToPoint(this.canvas, this.config, x, y, this.updateTransform.bind(this), this.transformLayer);
2215
2379
  }
2216
2380
  // Configuration access
2217
2381
  getConfig() {
@@ -2222,28 +2386,28 @@
2222
2386
  }
2223
2387
  // Theme management
2224
2388
  updateThemeMode(mode) {
2225
- const newConfig = {
2226
- ...this.config,
2227
- themeMode: mode,
2228
- };
2229
- this.config = createMarkupCanvasConfig(newConfig);
2230
- // Update canvas background color
2231
- const backgroundColor = getThemeValue(this.config, "canvasBackgroundColor");
2232
- this.baseCanvas.container.style.backgroundColor = backgroundColor;
2233
- // Update rulers if they exist
2234
- if (this.rulers) {
2235
- this.rulers.updateTheme(this.config);
2236
- }
2389
+ this.config = createMarkupCanvasConfig({ ...this.config, themeMode: mode });
2390
+ updateThemeMode(this.canvas.container, this.config, this.rulers, mode);
2391
+ }
2392
+ toggleThemeMode() {
2393
+ const currentMode = this.config.themeMode;
2394
+ const newMode = currentMode === "light" ? "dark" : "light";
2395
+ this.updateThemeMode(newMode);
2396
+ return newMode;
2397
+ }
2398
+ // Transition management
2399
+ updateTransition(enabled) {
2400
+ this.config = createMarkupCanvasConfig({ ...this.config, enableTransition: enabled });
2401
+ }
2402
+ toggleTransitionMode() {
2403
+ const newEnableTransition = toggleTransition(this.config.enableTransition);
2404
+ this.updateTransition(newEnableTransition);
2405
+ return newEnableTransition;
2237
2406
  }
2238
2407
  // Cleanup method
2239
2408
  cleanup() {
2240
- this.cleanupGlobalBinding();
2241
- // Cleanup postMessage listener
2242
- if (this.postMessageCleanup) {
2243
- this.postMessageCleanup();
2244
- this.postMessageCleanup = null;
2245
- }
2246
- this.cleanupFunctions.forEach((cleanup) => {
2409
+ cleanupWindowBinding(this.config);
2410
+ this.cleanupCallbacks.forEach((cleanup) => {
2247
2411
  try {
2248
2412
  cleanup();
2249
2413
  }
@@ -2251,22 +2415,22 @@
2251
2415
  console.warn("Error during cleanup:", cleanupError);
2252
2416
  }
2253
2417
  });
2254
- this.cleanupFunctions = [];
2418
+ this.cleanupCallbacks = [];
2255
2419
  // Remove all event listeners
2256
2420
  this.removeAllListeners();
2257
2421
  }
2258
2422
  // Event emitter delegation methods
2259
2423
  on(event, handler) {
2260
- this.listen.on(event, handler);
2424
+ this.event.on(event, handler);
2261
2425
  }
2262
2426
  off(event, handler) {
2263
- this.listen.off(event, handler);
2427
+ this.event.off(event, handler);
2264
2428
  }
2265
2429
  emit(event, data) {
2266
- this.listen.emit(event, data);
2430
+ this.event.emit(event, data);
2267
2431
  }
2268
2432
  removeAllListeners() {
2269
- this.listen.removeAllListeners();
2433
+ this.event.removeAllListeners();
2270
2434
  }
2271
2435
  destroy() {
2272
2436
  this.cleanup();