@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
  'use strict';
7
7
 
@@ -12,16 +12,15 @@ const EDITOR_PRESET = {
12
12
  width: 4000,
13
13
  height: 4000,
14
14
  enableAcceleration: true,
15
- // Global Binding & Instance Access
16
- bindToWindow: true,
15
+ // Global Instance Access
17
16
  name: "canvas",
18
17
  enablePostMessageAPI: true,
19
18
  // Interaction controls
20
19
  enableZoom: true,
21
20
  enablePan: true,
22
21
  enableTouch: true,
23
- enableKeyboard: false,
24
- bindKeyboardEventsTo: "canvas",
22
+ enableKeyboard: true,
23
+ bindKeyboardEventsTo: "document",
25
24
  // Zoom behavior
26
25
  zoomSpeed: 1.5,
27
26
  minZoom: 0.05,
@@ -43,7 +42,7 @@ const EDITOR_PRESET = {
43
42
  requireOptionForClickZoom: true,
44
43
  // Visual elements
45
44
  enableRulers: true,
46
- enableGrid: false,
45
+ enableGrid: true,
47
46
  showRulers: true,
48
47
  showGrid: false,
49
48
  rulerFontSize: 9,
@@ -67,72 +66,8 @@ const EDITOR_PRESET = {
67
66
  gridColorDark: "rgba(232, 86, 193, 0.5)",
68
67
  // Theme
69
68
  themeMode: "light",
70
- // Callbacks
71
- onTransformUpdate: () => { },
72
69
  };
73
70
 
74
- // Default transform values
75
- const DEFAULT_ZOOM = 1.0;
76
- // Validation thresholds
77
- const ZOOM_CHANGE_THRESHOLD = 0.001;
78
- // CSS transition values
79
- const FALLBACK_TRANSITION_DURATION = 0.2;
80
- // Zoom to fit padding factor
81
- const ZOOM_FIT_PADDING = 0.9;
82
- // CSS class names
83
- const CANVAS_CONTAINER_CLASS = "canvas-container";
84
- const TRANSFORM_LAYER_CLASS = "transform-layer";
85
- const CONTENT_LAYER_CLASS = "content-layer";
86
-
87
- function moveExistingContent(existingContent, contentLayer, transformLayer) {
88
- existingContent.forEach((child) => {
89
- if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
90
- contentLayer.appendChild(child);
91
- }
92
- });
93
- }
94
-
95
- function setupContentLayer(contentLayer) {
96
- contentLayer.style.position = "relative";
97
- contentLayer.style.width = "100%";
98
- contentLayer.style.height = "100%";
99
- contentLayer.style.pointerEvents = "auto";
100
- }
101
-
102
- // Sets up the transform layer with proper styles and dimensions
103
- function setupTransformLayer(transformLayer, config) {
104
- transformLayer.style.position = "absolute";
105
- const rulerOffset = config.rulerSize;
106
- transformLayer.style.top = `${rulerOffset}px`;
107
- transformLayer.style.left = `${rulerOffset}px`;
108
- transformLayer.style.width = `${config.width}px`;
109
- transformLayer.style.height = `${config.height}px`;
110
- transformLayer.style.transformOrigin = "0 0";
111
- }
112
-
113
- function createCanvasLayers(container, config) {
114
- const existingContent = Array.from(container.children);
115
- // Create or find transform layer
116
- let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
117
- if (!transformLayer) {
118
- transformLayer = document.createElement("div");
119
- transformLayer.className = TRANSFORM_LAYER_CLASS;
120
- container.appendChild(transformLayer);
121
- }
122
- setupTransformLayer(transformLayer, config);
123
- // Create or find content layer
124
- let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
125
- if (!contentLayer) {
126
- contentLayer = document.createElement("div");
127
- contentLayer.className = CONTENT_LAYER_CLASS;
128
- transformLayer.appendChild(contentLayer);
129
- moveExistingContent(existingContent, contentLayer, transformLayer);
130
- }
131
- // Set content layer properties
132
- setupContentLayer(contentLayer);
133
- return { transformLayer, contentLayer };
134
- }
135
-
136
71
  function canvasToContent(canvasX, canvasY, matrix) {
137
72
  if (!matrix?.inverse) {
138
73
  return { x: canvasX, y: canvasY };
@@ -160,6 +95,19 @@ function createMatrix(scale, translateX, translateY) {
160
95
  return new DOMMatrix([scale, 0, 0, scale, translateX, translateY]);
161
96
  }
162
97
 
98
+ // Default transform values
99
+ const DEFAULT_ZOOM = 1.0;
100
+ // Validation thresholds
101
+ const ZOOM_CHANGE_THRESHOLD = 0.001;
102
+ // CSS transition values
103
+ const FALLBACK_TRANSITION_DURATION = 0.2;
104
+ // Zoom to fit padding factor
105
+ const ZOOM_FIT_PADDING = 0.9;
106
+ // CSS class names
107
+ const CANVAS_CONTAINER_CLASS = "canvas-container";
108
+ const TRANSFORM_LAYER_CLASS = "transform-layer";
109
+ const CONTENT_LAYER_CLASS = "content-layer";
110
+
163
111
  function getZoomToMouseTransform(mouseX, mouseY, currentTransform, zoomFactor, config) {
164
112
  const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
165
113
  const transform = currentTransform || {
@@ -221,6 +169,16 @@ function getEmptyBounds() {
221
169
  };
222
170
  }
223
171
 
172
+ function getVisibleArea(canvas) {
173
+ const bounds = canvas.getBounds();
174
+ return bounds.visibleArea;
175
+ }
176
+
177
+ function isPointVisible(canvas, x, y) {
178
+ const visibleArea = getVisibleArea(canvas);
179
+ return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
180
+ }
181
+
224
182
  function withClampedZoom(config, operation) {
225
183
  const clampFunction = (scale) => clampZoom(scale, config);
226
184
  return operation(clampFunction);
@@ -276,13 +234,6 @@ function withRulerSize(canvas, rulerSize, operation) {
276
234
  const finalRulerSize = hasRulers ? rulerSize : 0;
277
235
  return operation(finalRulerSize);
278
236
  }
279
- function withRulerOffsets(canvas, rulerSize, x, y, operation) {
280
- return withRulerSize(canvas, rulerSize, (rulerSize) => {
281
- const adjustedX = x - rulerSize;
282
- const adjustedY = y - rulerSize;
283
- return operation(adjustedX, adjustedY);
284
- });
285
- }
286
237
  function withRulerOffsetObject(canvas, rulerSize, coords, operation) {
287
238
  return withRulerSize(canvas, rulerSize, (rulerSize) => {
288
239
  const adjusted = {
@@ -314,8 +265,7 @@ const DEFAULT_CONFIG = {
314
265
  width: 8000,
315
266
  height: 8000,
316
267
  enableAcceleration: true,
317
- // Global Binding & Instance Access
318
- bindToWindow: false,
268
+ // Global Instance Access
319
269
  name: "markupCanvas",
320
270
  enablePostMessageAPI: false,
321
271
  // Interaction controls
@@ -323,7 +273,7 @@ const DEFAULT_CONFIG = {
323
273
  enablePan: true,
324
274
  enableTouch: true,
325
275
  enableKeyboard: true,
326
- bindKeyboardEventsTo: "canvas",
276
+ bindKeyboardEventsTo: "document",
327
277
  // Zoom behavior
328
278
  zoomSpeed: 1.5,
329
279
  minZoom: 0.05,
@@ -369,14 +319,11 @@ const DEFAULT_CONFIG = {
369
319
  gridColorDark: "rgba(232, 86, 193, 0.5)",
370
320
  // Theme
371
321
  themeMode: "light",
372
- // Callbacks
373
- onTransformUpdate: () => { },
374
322
  };
375
323
 
376
- function getCanvasBounds(canvas) {
324
+ function getCanvasBounds(canvas, config) {
377
325
  try {
378
326
  const container = canvas.container;
379
- const config = canvas.config;
380
327
  const transform = canvas.transform || {
381
328
  scale: 1.0,
382
329
  translateX: 0,
@@ -426,6 +373,116 @@ function getCanvasBounds(canvas) {
426
373
  }
427
374
  }
428
375
 
376
+ function disableTransition(element, config) {
377
+ try {
378
+ if (config.enableTransition) {
379
+ if (window.__markupCanvasTransitionTimeout) {
380
+ clearTimeout(window.__markupCanvasTransitionTimeout);
381
+ window.__markupCanvasTransitionTimeout = undefined;
382
+ }
383
+ const delay = (config.transitionDuration ?? FALLBACK_TRANSITION_DURATION) * 1000;
384
+ withDebounce("disableTransition", delay, () => {
385
+ element.style.transition = "none";
386
+ window.__markupCanvasTransitionTimeout = undefined;
387
+ });
388
+ return true;
389
+ }
390
+ return false;
391
+ }
392
+ catch (error) {
393
+ console.error("Failed to disable transitions:", error);
394
+ return true;
395
+ }
396
+ }
397
+
398
+ function enableTransition(element, config) {
399
+ try {
400
+ if (config.enableTransition) {
401
+ if (window.__markupCanvasTransitionTimeout) {
402
+ clearTimeout(window.__markupCanvasTransitionTimeout);
403
+ window.__markupCanvasTransitionTimeout = undefined;
404
+ }
405
+ element.style.transition = `transform ${config.transitionDuration}s linear`;
406
+ return true;
407
+ }
408
+ return false;
409
+ }
410
+ catch (error) {
411
+ console.error("Failed to enable transitions:", error);
412
+ return false;
413
+ }
414
+ }
415
+
416
+ function withTransition(element, config, operation) {
417
+ enableTransition(element, config);
418
+ try {
419
+ const result = operation();
420
+ return result;
421
+ }
422
+ finally {
423
+ disableTransition(element, config);
424
+ }
425
+ }
426
+
427
+ function centerContent(canvas, config, updateTransformFn, transformLayer) {
428
+ return withTransition(transformLayer, config, () => {
429
+ const bounds = getCanvasBounds(canvas, config);
430
+ const centerX = (bounds.width - bounds.contentWidth * canvas.transform.scale) / 2;
431
+ const centerY = (bounds.height - bounds.contentHeight * canvas.transform.scale) / 2;
432
+ return updateTransformFn({
433
+ translateX: centerX,
434
+ translateY: centerY,
435
+ });
436
+ });
437
+ }
438
+
439
+ function panDown(canvas, config, updateTransform) {
440
+ const panDistance = config.keyboardPanStep;
441
+ const newTransform = {
442
+ translateY: canvas.transform.translateY - panDistance,
443
+ };
444
+ return updateTransform(newTransform);
445
+ }
446
+
447
+ function panLeft(canvas, config, updateTransform) {
448
+ const panDistance = config.keyboardPanStep;
449
+ const newTransform = {
450
+ translateX: canvas.transform.translateX + panDistance,
451
+ };
452
+ return updateTransform(newTransform);
453
+ }
454
+
455
+ function panRight(canvas, config, updateTransform) {
456
+ const panDistance = config.keyboardPanStep;
457
+ const newTransform = {
458
+ translateX: canvas.transform.translateX - panDistance,
459
+ };
460
+ return updateTransform(newTransform);
461
+ }
462
+
463
+ function panUp(canvas, config, updateTransform) {
464
+ const panDistance = config.keyboardPanStep;
465
+ const newTransform = {
466
+ translateY: canvas.transform.translateY + panDistance,
467
+ };
468
+ return updateTransform(newTransform);
469
+ }
470
+
471
+ function scrollToPoint(canvas, config, x, y, updateTransform, transformLayer) {
472
+ return withTransition(transformLayer, config, () => {
473
+ const bounds = getCanvasBounds(canvas, config);
474
+ const centerX = bounds.width / 2;
475
+ const centerY = bounds.height / 2;
476
+ // Calculate new translation to center the point
477
+ const newTranslateX = centerX - x * canvas.transform.scale;
478
+ const newTranslateY = centerY - y * canvas.transform.scale;
479
+ return updateTransform({
480
+ translateX: newTranslateX,
481
+ translateY: newTranslateY,
482
+ });
483
+ });
484
+ }
485
+
429
486
  function createMatrixString(matrix) {
430
487
  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})`;
431
488
  }
@@ -457,136 +514,296 @@ function enableHardwareAcceleration(element) {
457
514
  }
458
515
  }
459
516
 
460
- function disableTransition(element, config) {
461
- try {
462
- if (config.enableTransition) {
463
- if (window.__markupCanvasTransitionTimeout) {
464
- clearTimeout(window.__markupCanvasTransitionTimeout);
465
- window.__markupCanvasTransitionTimeout = undefined;
466
- }
467
- const delay = (config.transitionDuration ?? FALLBACK_TRANSITION_DURATION) * 1000;
468
- withDebounce("disableTransition", delay, () => {
469
- element.style.transition = "none";
470
- window.__markupCanvasTransitionTimeout = undefined;
471
- });
472
- return true;
473
- }
474
- return false;
517
+ function updateTransform(canvas, newTransform) {
518
+ canvas.transform = { ...canvas.transform, ...newTransform };
519
+ const matrix = createMatrix(canvas.transform.scale, canvas.transform.translateX, canvas.transform.translateY);
520
+ return applyTransform(canvas.transformLayer, matrix);
521
+ }
522
+
523
+ function resetTransform(canvas) {
524
+ const resetTransformData = {
525
+ scale: 1.0,
526
+ translateX: 0,
527
+ translateY: 0,
528
+ };
529
+ return updateTransform(canvas, resetTransformData);
530
+ }
531
+
532
+ function hideGrid(rulers) {
533
+ if (rulers?.gridOverlay) {
534
+ rulers.gridOverlay.style.display = "none";
535
+ return true;
475
536
  }
476
- catch (error) {
477
- console.error("Failed to disable transitions:", error);
537
+ return false;
538
+ }
539
+
540
+ function isGridVisible(rulers) {
541
+ if (rulers?.gridOverlay) {
542
+ return rulers.gridOverlay.style.display !== "none";
543
+ }
544
+ return false;
545
+ }
546
+
547
+ function showGrid(rulers) {
548
+ if (rulers?.gridOverlay) {
549
+ rulers.gridOverlay.style.display = "block";
478
550
  return true;
479
551
  }
552
+ return false;
480
553
  }
481
554
 
482
- function enableTransition(element, config) {
483
- try {
484
- if (config.enableTransition) {
485
- if (window.__markupCanvasTransitionTimeout) {
486
- clearTimeout(window.__markupCanvasTransitionTimeout);
487
- window.__markupCanvasTransitionTimeout = undefined;
488
- }
489
- element.style.transition = `transform ${config.transitionDuration}s linear`;
490
- return true;
555
+ function toggleGrid(rulers) {
556
+ if (rulers?.toggleGrid) {
557
+ rulers.toggleGrid();
558
+ return true;
559
+ }
560
+ return false;
561
+ }
562
+
563
+ function areRulersVisible(rulers) {
564
+ if (rulers?.horizontalRuler) {
565
+ return rulers.horizontalRuler.style.display !== "none";
566
+ }
567
+ return false;
568
+ }
569
+
570
+ function hideRulers(rulers) {
571
+ if (rulers) {
572
+ rulers.hide();
573
+ return true;
574
+ }
575
+ return false;
576
+ }
577
+
578
+ function showRulers(rulers) {
579
+ if (rulers) {
580
+ rulers.show();
581
+ return true;
582
+ }
583
+ return false;
584
+ }
585
+
586
+ function toggleRulers(rulers, areRulersVisible) {
587
+ if (rulers) {
588
+ const isVisible = areRulersVisible();
589
+ if (isVisible) {
590
+ rulers.hide();
491
591
  }
492
- return false;
592
+ else {
593
+ rulers.show();
594
+ }
595
+ return true;
596
+ }
597
+ return false;
598
+ }
599
+
600
+ function toggleTransition(enableTransition) {
601
+ return !enableTransition;
602
+ }
603
+
604
+ function createMarkupCanvasConfig(options = {}) {
605
+ const config = {
606
+ ...DEFAULT_CONFIG,
607
+ ...options,
608
+ };
609
+ if (typeof config.width !== "number" || config.width <= 0) {
610
+ console.warn("Invalid width, using default");
611
+ config.width = DEFAULT_CONFIG.width;
612
+ }
613
+ if (typeof config.height !== "number" || config.height <= 0) {
614
+ console.warn("Invalid height, using default");
615
+ config.height = DEFAULT_CONFIG.height;
616
+ }
617
+ if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
618
+ console.warn("Invalid zoomSpeed, using default");
619
+ config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
620
+ }
621
+ if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
622
+ console.warn("Invalid minZoom, using default");
623
+ config.minZoom = DEFAULT_CONFIG.minZoom;
624
+ }
625
+ if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
626
+ console.warn("Invalid maxZoom, using default");
627
+ config.maxZoom = DEFAULT_CONFIG.maxZoom;
628
+ }
629
+ if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
630
+ console.warn("Invalid keyboardPanStep, using default");
631
+ config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
632
+ }
633
+ if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
634
+ console.warn("Invalid keyboardFastMultiplier, using default");
635
+ config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
636
+ }
637
+ if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
638
+ console.warn("Invalid clickZoomLevel, using default");
639
+ config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
640
+ }
641
+ if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
642
+ console.warn("Invalid rulerFontSize, using default");
643
+ config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
644
+ }
645
+ if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
646
+ console.warn("Invalid rulerSize, using default");
647
+ config.rulerSize = DEFAULT_CONFIG.rulerSize;
648
+ }
649
+ return config;
650
+ }
651
+
652
+ function updateThemeMode(canvasContainer, config, rulers, mode) {
653
+ const newConfig = {
654
+ ...config,
655
+ themeMode: mode,
656
+ };
657
+ const updatedConfig = createMarkupCanvasConfig(newConfig);
658
+ // Update canvas background color
659
+ const backgroundColor = getThemeValue(updatedConfig, "canvasBackgroundColor");
660
+ canvasContainer.style.backgroundColor = backgroundColor;
661
+ // Update rulers if they exist
662
+ if (rulers) {
663
+ rulers.updateTheme(updatedConfig);
664
+ }
665
+ }
666
+
667
+ function resetView(canvas, transformLayer, config) {
668
+ return withTransition(transformLayer, config, () => {
669
+ return withRulerSize(canvas, config.rulerSize, (rulerSize) => {
670
+ const resetTransformData = {
671
+ scale: 1.0,
672
+ translateX: rulerSize * -1,
673
+ translateY: rulerSize * -1,
674
+ };
675
+ return updateTransform(canvas, resetTransformData);
676
+ });
677
+ });
678
+ }
679
+
680
+ function getViewportCenter(canvas) {
681
+ try {
682
+ const bounds = canvas.getBounds();
683
+ return {
684
+ x: bounds.width / 2,
685
+ y: bounds.height / 2,
686
+ };
493
687
  }
494
688
  catch (error) {
495
- console.error("Failed to enable transitions:", error);
496
- return false;
689
+ console.warn("Failed to calculate viewport center:", error);
690
+ return { x: 0, y: 0 };
497
691
  }
498
692
  }
499
693
 
500
- function withTransition(element, config, operation) {
501
- enableTransition(element, config);
502
- try {
503
- const result = operation();
504
- return result;
694
+ function resetViewToCenter(canvas, transformLayer, config, zoomToPoint) {
695
+ return withTransition(transformLayer, config, () => {
696
+ return withClampedZoom(config, (clamp) => {
697
+ const newScale = clamp(1.0);
698
+ const center = getViewportCenter(canvas);
699
+ return zoomToPoint(center.x, center.y, newScale);
700
+ });
701
+ });
702
+ }
703
+
704
+ function setZoom(canvas, transformLayer, config, zoomToPoint, zoomLevel) {
705
+ return withTransition(transformLayer, config, () => {
706
+ return withClampedZoom(config, (clamp) => {
707
+ const newScale = clamp(zoomLevel);
708
+ const center = getViewportCenter(canvas);
709
+ return zoomToPoint(center.x, center.y, newScale);
710
+ });
711
+ });
712
+ }
713
+
714
+ function zoomIn(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
715
+ return withTransition(transformLayer, config, () => {
716
+ return withClampedZoom(config, (clamp) => {
717
+ const newScale = clamp(canvas.transform.scale * (1 + factor));
718
+ const center = getViewportCenter(canvas);
719
+ return zoomToPoint(center.x, center.y, newScale);
720
+ });
721
+ });
722
+ }
723
+
724
+ function zoomOut(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
725
+ return withTransition(transformLayer, config, () => {
726
+ return withClampedZoom(config, (clamp) => {
727
+ const newScale = clamp(canvas.transform.scale * (1 - factor));
728
+ const center = getViewportCenter(canvas);
729
+ return zoomToPoint(center.x, center.y, newScale);
730
+ });
731
+ });
732
+ }
733
+
734
+ function zoomToPoint(canvas, transformLayer, config, x, y, targetScale) {
735
+ return withTransition(transformLayer, config, () => {
736
+ const newTransform = getZoomToMouseTransform(x, y, canvas.transform, targetScale / canvas.transform.scale, config);
737
+ return updateTransform(canvas, newTransform);
738
+ });
739
+ }
740
+
741
+ function fitToScreen(canvas, transformLayer, config) {
742
+ return withTransition(transformLayer, config, () => {
743
+ const bounds = getCanvasBounds(canvas, config);
744
+ const scaleX = bounds.width / config.width;
745
+ const scaleY = bounds.height / config.height;
746
+ const fitScale = withClampedZoom(config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
747
+ // Center the content
748
+ const scaledWidth = config.width * fitScale;
749
+ const scaledHeight = config.height * fitScale;
750
+ const centerX = (bounds.width - scaledWidth) / 2;
751
+ const centerY = (bounds.height - scaledHeight) / 2;
752
+ return updateTransform(canvas, {
753
+ scale: fitScale,
754
+ translateX: centerX,
755
+ translateY: centerY,
756
+ });
757
+ });
758
+ }
759
+
760
+ function moveExistingContent(existingContent, contentLayer, transformLayer) {
761
+ existingContent.forEach((child) => {
762
+ if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
763
+ contentLayer.appendChild(child);
764
+ }
765
+ });
766
+ }
767
+
768
+ function setupContentLayer(contentLayer) {
769
+ contentLayer.style.position = "relative";
770
+ contentLayer.style.width = "100%";
771
+ contentLayer.style.height = "100%";
772
+ contentLayer.style.pointerEvents = "auto";
773
+ }
774
+
775
+ // Sets up the transform layer with proper styles and dimensions
776
+ function setupTransformLayer(transformLayer, config) {
777
+ transformLayer.style.position = "absolute";
778
+ const rulerOffset = config.rulerSize;
779
+ transformLayer.style.top = `${rulerOffset}px`;
780
+ transformLayer.style.left = `${rulerOffset}px`;
781
+ transformLayer.style.width = `${config.width}px`;
782
+ transformLayer.style.height = `${config.height}px`;
783
+ transformLayer.style.transformOrigin = "0 0";
784
+ }
785
+
786
+ function createCanvasLayers(container, config) {
787
+ const existingContent = Array.from(container.children);
788
+ // Create or find transform layer
789
+ let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
790
+ if (!transformLayer) {
791
+ transformLayer = document.createElement("div");
792
+ transformLayer.className = TRANSFORM_LAYER_CLASS;
793
+ container.appendChild(transformLayer);
505
794
  }
506
- finally {
507
- disableTransition(element, config);
795
+ setupTransformLayer(transformLayer, config);
796
+ // Create or find content layer
797
+ let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
798
+ if (!contentLayer) {
799
+ contentLayer = document.createElement("div");
800
+ contentLayer.className = CONTENT_LAYER_CLASS;
801
+ transformLayer.appendChild(contentLayer);
802
+ moveExistingContent(existingContent, contentLayer, transformLayer);
508
803
  }
509
- }
510
-
511
- function getCanvasMethods() {
512
- return {
513
- // Utility methods
514
- getBounds: function () {
515
- return getCanvasBounds(this);
516
- },
517
- // Transform methods
518
- updateTransform: function (newTransform) {
519
- this.transform = { ...this.transform, ...newTransform };
520
- const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
521
- const result = applyTransform(this.transformLayer, matrix);
522
- withFeatureEnabled(this.config, "onTransformUpdate", () => {
523
- this.config.onTransformUpdate(this.transform);
524
- });
525
- return result;
526
- },
527
- // Reset method
528
- reset: function () {
529
- const resetTransform = {
530
- scale: 1.0,
531
- translateX: 0,
532
- translateY: 0,
533
- };
534
- return this.updateTransform(resetTransform);
535
- },
536
- // Handle canvas resize
537
- handleResize: function () {
538
- return true;
539
- },
540
- // Set zoom level
541
- setZoom: function (zoomLevel) {
542
- const newScale = withClampedZoom(this.config, (clamp) => clamp(zoomLevel));
543
- return this.updateTransform({ scale: newScale });
544
- },
545
- // Convert canvas coordinates to content coordinates
546
- canvasToContent: function (x, y) {
547
- const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
548
- return canvasToContent(x, y, matrix);
549
- },
550
- // Zoom to a specific point with animation
551
- zoomToPoint: function (x, y, targetScale) {
552
- return withTransition(this.transformLayer, this.config, () => {
553
- const newTransform = getZoomToMouseTransform(x, y, this.transform, targetScale / this.transform.scale, this.config);
554
- return this.updateTransform(newTransform);
555
- });
556
- },
557
- // Reset view with animation
558
- resetView: function () {
559
- return withTransition(this.transformLayer, this.config, () => {
560
- return withRulerSize(this, this.config.rulerSize, (rulerSize) => {
561
- const resetTransform = {
562
- scale: 1.0,
563
- translateX: rulerSize * -1,
564
- translateY: rulerSize * -1,
565
- };
566
- return this.updateTransform(resetTransform);
567
- });
568
- });
569
- },
570
- // Zoom to fit content in canvas
571
- zoomToFitContent: function () {
572
- return withTransition(this.transformLayer, this.config, () => {
573
- const bounds = this.getBounds();
574
- const scaleX = bounds.width / this.config.width;
575
- const scaleY = bounds.height / this.config.height;
576
- const fitScale = withClampedZoom(this.config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
577
- // Center the content
578
- const scaledWidth = this.config.width * fitScale;
579
- const scaledHeight = this.config.height * fitScale;
580
- const centerX = (bounds.width - scaledWidth) / 2;
581
- const centerY = (bounds.height - scaledHeight) / 2;
582
- return this.updateTransform({
583
- scale: fitScale,
584
- translateX: centerX,
585
- translateY: centerY,
586
- });
587
- });
588
- },
589
- };
804
+ // Set content layer properties
805
+ setupContentLayer(contentLayer);
806
+ return { transformLayer, contentLayer };
590
807
  }
591
808
 
592
809
  function checkContainerDimensions(container) {
@@ -622,7 +839,6 @@ function setupCanvasContainer(container, config) {
622
839
  }
623
840
  }
624
841
 
625
- // Creates and initializes a canvas with the required DOM structure
626
842
  function createCanvas(container, config) {
627
843
  if (!container?.appendChild) {
628
844
  console.error("Invalid container element provided to createCanvas");
@@ -631,10 +847,9 @@ function createCanvas(container, config) {
631
847
  try {
632
848
  setupCanvasContainer(container, config);
633
849
  const { transformLayer, contentLayer } = createCanvasLayers(container, config);
634
- // Enable hardware acceleration if requested
635
- if (config.enableAcceleration) {
850
+ withFeatureEnabled(config, "enableAcceleration", () => {
636
851
  enableHardwareAcceleration(transformLayer);
637
- }
852
+ });
638
853
  const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
639
854
  const initialTransform = {
640
855
  scale: DEFAULT_ZOOM,
@@ -649,12 +864,8 @@ function createCanvas(container, config) {
649
864
  container,
650
865
  transformLayer,
651
866
  contentLayer,
652
- // Configuration
653
- config: config,
654
867
  // Current state
655
868
  transform: initialTransform,
656
- // Add all canvas methods
657
- ...getCanvasMethods(),
658
869
  };
659
870
  return canvas;
660
871
  }
@@ -664,54 +875,6 @@ function createCanvas(container, config) {
664
875
  }
665
876
  }
666
877
 
667
- function createMarkupCanvasConfig(options = {}) {
668
- const config = {
669
- ...DEFAULT_CONFIG,
670
- ...options,
671
- };
672
- if (typeof config.width !== "number" || config.width <= 0) {
673
- console.warn("Invalid width, using default");
674
- config.width = DEFAULT_CONFIG.width;
675
- }
676
- if (typeof config.height !== "number" || config.height <= 0) {
677
- console.warn("Invalid height, using default");
678
- config.height = DEFAULT_CONFIG.height;
679
- }
680
- if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
681
- console.warn("Invalid zoomSpeed, using default");
682
- config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
683
- }
684
- if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
685
- console.warn("Invalid minZoom, using default");
686
- config.minZoom = DEFAULT_CONFIG.minZoom;
687
- }
688
- if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
689
- console.warn("Invalid maxZoom, using default");
690
- config.maxZoom = DEFAULT_CONFIG.maxZoom;
691
- }
692
- if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
693
- console.warn("Invalid keyboardPanStep, using default");
694
- config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
695
- }
696
- if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
697
- console.warn("Invalid keyboardFastMultiplier, using default");
698
- config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
699
- }
700
- if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
701
- console.warn("Invalid clickZoomLevel, using default");
702
- config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
703
- }
704
- if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
705
- console.warn("Invalid rulerFontSize, using default");
706
- config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
707
- }
708
- if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
709
- console.warn("Invalid rulerSize, using default");
710
- config.rulerSize = DEFAULT_CONFIG.rulerSize;
711
- }
712
- return config;
713
- }
714
-
715
878
  class EventEmitter {
716
879
  constructor() {
717
880
  this.listeners = new Map();
@@ -750,6 +913,13 @@ class EventEmitter {
750
913
  }
751
914
  }
752
915
 
916
+ function emitTransformEvents(listen, canvas) {
917
+ const transform = canvas.transform;
918
+ listen.emit("transform", transform);
919
+ listen.emit("zoom", transform.scale);
920
+ listen.emit("pan", { x: transform.translateX, y: transform.translateY });
921
+ }
922
+
753
923
  const REFERENCE_DISPLAY_AREA = 1920 * 1080;
754
924
  const TRACKPAD_PINCH_SPEED_FACTOR = 0.05;
755
925
  const ADAPTIVE_ZOOM_FACTOR = 1;
@@ -776,42 +946,28 @@ function getAdaptiveZoomSpeed(canvas, baseSpeed) {
776
946
  }
777
947
 
778
948
  function setupKeyboardEvents(canvas, config) {
779
- // Track mouse position
780
- let lastMouseX = 0;
781
- let lastMouseY = 0;
782
- function handleMouseMove(event) {
783
- const rect = canvas.container.getBoundingClientRect();
784
- const rawMouseX = event.clientX - rect.left;
785
- const rawMouseY = event.clientY - rect.top;
786
- withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
787
- lastMouseX = adjustedX;
788
- lastMouseY = adjustedY;
789
- });
790
- }
791
949
  function handleKeyDown(event) {
792
950
  if (!(event instanceof KeyboardEvent))
793
951
  return;
794
952
  if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
795
953
  return;
796
- const isFastPan = event.shiftKey;
797
- const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
798
954
  let handled = false;
799
955
  const newTransform = {};
800
956
  switch (event.key) {
801
957
  case "ArrowLeft":
802
- newTransform.translateX = canvas.transform.translateX + panDistance;
958
+ newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
803
959
  handled = true;
804
960
  break;
805
961
  case "ArrowRight":
806
- newTransform.translateX = canvas.transform.translateX - panDistance;
962
+ newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
807
963
  handled = true;
808
964
  break;
809
965
  case "ArrowUp":
810
- newTransform.translateY = canvas.transform.translateY + panDistance;
966
+ newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
811
967
  handled = true;
812
968
  break;
813
969
  case "ArrowDown":
814
- newTransform.translateY = canvas.transform.translateY - panDistance;
970
+ newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
815
971
  handled = true;
816
972
  break;
817
973
  case "=":
@@ -820,7 +976,7 @@ function setupKeyboardEvents(canvas, config) {
820
976
  const adaptiveZoomStep = config.enableAdaptiveSpeed
821
977
  ? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
822
978
  : config.keyboardZoomStep;
823
- newTransform.scale = clampZoom(canvas.transform.scale * (1 + adaptiveZoomStep), config);
979
+ canvas.zoomIn(adaptiveZoomStep);
824
980
  handled = true;
825
981
  }
826
982
  break;
@@ -829,16 +985,21 @@ function setupKeyboardEvents(canvas, config) {
829
985
  const adaptiveZoomStep = config.enableAdaptiveSpeed
830
986
  ? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
831
987
  : config.keyboardZoomStep;
832
- newTransform.scale = clampZoom(canvas.transform.scale * (1 - adaptiveZoomStep), config);
988
+ canvas.zoomOut(adaptiveZoomStep);
833
989
  handled = true;
834
990
  }
835
991
  break;
836
992
  case "0":
837
- if (event.metaKey || event.ctrlKey) {
838
- const targetScale = 1.0;
839
- const zoomFactor = targetScale / canvas.transform.scale;
840
- const zoomTransform = getZoomToMouseTransform(lastMouseX, lastMouseY, canvas.transform, zoomFactor, config);
841
- Object.assign(newTransform, zoomTransform);
993
+ if (event.ctrlKey) {
994
+ if (canvas.resetView) {
995
+ canvas.resetView();
996
+ }
997
+ handled = true;
998
+ }
999
+ else if (event.metaKey || event.ctrlKey) {
1000
+ if (canvas.resetViewToCenter) {
1001
+ canvas.resetViewToCenter();
1002
+ }
842
1003
  handled = true;
843
1004
  }
844
1005
  break;
@@ -866,10 +1027,8 @@ function setupKeyboardEvents(canvas, config) {
866
1027
  }
867
1028
  const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
868
1029
  keyboardTarget.addEventListener("keydown", handleKeyDown);
869
- canvas.container.addEventListener("mousemove", handleMouseMove);
870
1030
  return () => {
871
1031
  keyboardTarget.removeEventListener("keydown", handleKeyDown);
872
- canvas.container.removeEventListener("mousemove", handleMouseMove);
873
1032
  };
874
1033
  }
875
1034
 
@@ -1255,6 +1414,17 @@ function setupPostMessageEvents(canvas) {
1255
1414
  const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
1256
1415
  canvas.updateThemeMode(newMode);
1257
1416
  }
1417
+ // Transition methods
1418
+ else if (action === "updateTransition") {
1419
+ const enabled = args[0];
1420
+ if (typeof enabled !== "boolean") {
1421
+ throw new Error(`Invalid transition enabled value: ${enabled}. Must be a boolean.`);
1422
+ }
1423
+ canvas.updateTransition(enabled);
1424
+ }
1425
+ else if (action === "toggleTransitionMode") {
1426
+ canvas.toggleTransitionMode();
1427
+ }
1258
1428
  else {
1259
1429
  throw new Error(`Unknown action: ${action}`);
1260
1430
  }
@@ -1852,8 +2022,6 @@ function createRulers(canvas, config) {
1852
2022
  elements.verticalRuler.style.display = "block";
1853
2023
  if (elements.cornerBox)
1854
2024
  elements.cornerBox.style.display = "flex";
1855
- if (elements.gridOverlay)
1856
- elements.gridOverlay.style.display = "block";
1857
2025
  },
1858
2026
  hide: () => {
1859
2027
  if (elements.horizontalRuler)
@@ -1897,14 +2065,163 @@ function createRulers(canvas, config) {
1897
2065
  }
1898
2066
  }
1899
2067
 
2068
+ function broadcastEvent(event, data, config) {
2069
+ if (typeof window === "undefined") {
2070
+ return;
2071
+ }
2072
+ // Receivers can get the instance from the window binding
2073
+ let broadcastData = data;
2074
+ if (event === "ready") {
2075
+ broadcastData = { ready: true };
2076
+ }
2077
+ window.postMessage({
2078
+ source: "markup-canvas",
2079
+ event,
2080
+ data: broadcastData,
2081
+ timestamp: Date.now(),
2082
+ canvasName: config.name,
2083
+ }, "*");
2084
+ if (window.parent) {
2085
+ window.parent.postMessage({
2086
+ source: "markup-canvas",
2087
+ event,
2088
+ data: broadcastData,
2089
+ timestamp: Date.now(),
2090
+ canvasName: config.name,
2091
+ }, "*");
2092
+ }
2093
+ }
2094
+
2095
+ function cleanupWindowBinding(config) {
2096
+ if (typeof window === "undefined") {
2097
+ return;
2098
+ }
2099
+ const canvasName = config.name || "markupCanvas";
2100
+ const windowObj = window;
2101
+ delete windowObj[canvasName];
2102
+ if (windowObj.__markupCanvasInstances) {
2103
+ windowObj.__markupCanvasInstances.delete(canvasName);
2104
+ }
2105
+ }
2106
+
2107
+ function bindCanvasToWindow(canvas, config) {
2108
+ if (typeof window === "undefined") {
2109
+ return;
2110
+ }
2111
+ const canvasName = config.name || "markupCanvas";
2112
+ const windowObj = window;
2113
+ const api = {
2114
+ config: {
2115
+ get current() {
2116
+ return canvas.config;
2117
+ },
2118
+ get: canvas.getConfig.bind(canvas),
2119
+ update: canvas.updateConfig.bind(canvas),
2120
+ },
2121
+ // Transform group
2122
+ transform: {
2123
+ update: canvas.updateTransform.bind(canvas),
2124
+ reset: canvas.reset.bind(canvas),
2125
+ },
2126
+ // Zoom group
2127
+ zoom: {
2128
+ set: canvas.setZoom.bind(canvas),
2129
+ toPoint: canvas.zoomToPoint.bind(canvas),
2130
+ in: canvas.zoomIn.bind(canvas),
2131
+ out: canvas.zoomOut.bind(canvas),
2132
+ reset: canvas.resetZoom.bind(canvas),
2133
+ resetToCenter: canvas.resetViewToCenter.bind(canvas),
2134
+ fitToScreen: canvas.fitToScreen.bind(canvas),
2135
+ },
2136
+ // Pan group
2137
+ pan: {
2138
+ left: canvas.panLeft.bind(canvas),
2139
+ right: canvas.panRight.bind(canvas),
2140
+ up: canvas.panUp.bind(canvas),
2141
+ down: canvas.panDown.bind(canvas),
2142
+ toPoint: canvas.scrollToPoint.bind(canvas),
2143
+ toCenter: canvas.centerContent.bind(canvas),
2144
+ },
2145
+ // Mouse drag group
2146
+ mouseDrag: {
2147
+ enable: canvas.enableMouseDrag.bind(canvas),
2148
+ disable: canvas.disableMouseDrag.bind(canvas),
2149
+ isEnabled: canvas.isMouseDragEnabled.bind(canvas),
2150
+ },
2151
+ // Grid group
2152
+ grid: {
2153
+ toggle: canvas.toggleGrid.bind(canvas),
2154
+ show: canvas.showGrid.bind(canvas),
2155
+ hide: canvas.hideGrid.bind(canvas),
2156
+ isVisible: canvas.isGridVisible.bind(canvas),
2157
+ },
2158
+ // Ruler group
2159
+ rulers: {
2160
+ toggle: canvas.toggleRulers.bind(canvas),
2161
+ show: canvas.showRulers.bind(canvas),
2162
+ hide: canvas.hideRulers.bind(canvas),
2163
+ isVisible: canvas.areRulersVisible.bind(canvas),
2164
+ },
2165
+ // Utility group
2166
+ canvas: {
2167
+ canvasToContent: canvas.canvasToContent.bind(canvas),
2168
+ getVisibleArea: canvas.getVisibleArea.bind(canvas),
2169
+ isPointVisible: canvas.isPointVisible.bind(canvas),
2170
+ getBounds: canvas.getBounds.bind(canvas),
2171
+ },
2172
+ theme: {
2173
+ get current() {
2174
+ return canvas.config.themeMode;
2175
+ },
2176
+ update: canvas.updateThemeMode.bind(canvas),
2177
+ toggle: canvas.toggleThemeMode.bind(canvas),
2178
+ },
2179
+ transition: {
2180
+ get current() {
2181
+ return canvas.config.enableTransition;
2182
+ },
2183
+ set: canvas.updateTransition.bind(canvas),
2184
+ toggle: canvas.toggleTransitionMode.bind(canvas),
2185
+ },
2186
+ // Event group
2187
+ event: canvas.event,
2188
+ // Lifecycle group
2189
+ lifecycle: {
2190
+ cleanup: canvas.cleanup.bind(canvas),
2191
+ destroy: canvas.destroy.bind(canvas),
2192
+ },
2193
+ // Properties/State group
2194
+ state: {
2195
+ get isReady() {
2196
+ return canvas.isReady;
2197
+ },
2198
+ get isTransforming() {
2199
+ return canvas.isTransforming;
2200
+ },
2201
+ get visibleBounds() {
2202
+ return canvas.visibleBounds;
2203
+ },
2204
+ get transform() {
2205
+ return canvas.transform;
2206
+ },
2207
+ },
2208
+ };
2209
+ // Bind public API to window
2210
+ windowObj[canvasName] = api;
2211
+ // Track all instances
2212
+ if (!windowObj.__markupCanvasInstances) {
2213
+ windowObj.__markupCanvasInstances = new Map();
2214
+ }
2215
+ windowObj.__markupCanvasInstances.set(canvasName, api);
2216
+ }
2217
+
1900
2218
  class MarkupCanvas {
1901
2219
  constructor(container, options = {}) {
1902
- this.cleanupFunctions = [];
2220
+ this.cleanupCallbacks = [];
1903
2221
  this.rulers = null;
1904
2222
  this.dragSetup = null;
2223
+ this.event = new EventEmitter();
1905
2224
  this._isReady = false;
1906
- this.listen = new EventEmitter();
1907
- this.postMessageCleanup = null;
1908
2225
  if (!container) {
1909
2226
  throw new Error("Container element is required");
1910
2227
  }
@@ -1913,100 +2230,48 @@ class MarkupCanvas {
1913
2230
  if (!canvas) {
1914
2231
  throw new Error("Failed to create canvas");
1915
2232
  }
1916
- this.baseCanvas = canvas;
1917
- if (this.config.bindToWindow) {
1918
- this.listen.setEmitInterceptor((event, data) => {
1919
- this.broadcastEvent(event, data);
1920
- });
1921
- this.setupGlobalBinding();
1922
- // Set up postMessage listener
1923
- if (this.config.enablePostMessageAPI) {
1924
- this.postMessageCleanup = setupPostMessageEvents(this);
1925
- }
2233
+ this.canvas = canvas;
2234
+ // Always bind canvas to window
2235
+ this.event.setEmitInterceptor((event, data) => {
2236
+ broadcastEvent(event, data, this.config);
2237
+ });
2238
+ bindCanvasToWindow(this, this.config);
2239
+ // Set up postMessage listener
2240
+ if (this.config.enablePostMessageAPI) {
2241
+ const postMessageCleanup = setupPostMessageEvents(this);
2242
+ this.cleanupCallbacks.push(postMessageCleanup);
1926
2243
  }
1927
2244
  this.setupEventHandlers();
1928
2245
  this._isReady = true;
1929
2246
  // Emit ready event
1930
- this.listen.emit("ready", this);
1931
- }
1932
- setupGlobalBinding() {
1933
- if (typeof window === "undefined") {
1934
- return;
1935
- }
1936
- const canvasName = this.config.name || "markupCanvas";
1937
- const windowObj = window;
1938
- // Bind instance to window
1939
- windowObj[canvasName] = this;
1940
- // Track all instances
1941
- if (!windowObj.__markupCanvasInstances) {
1942
- windowObj.__markupCanvasInstances = new Map();
1943
- }
1944
- windowObj.__markupCanvasInstances.set(canvasName, this);
1945
- }
1946
- cleanupGlobalBinding() {
1947
- if (typeof window === "undefined") {
1948
- return;
1949
- }
1950
- const canvasName = this.config.name || "markupCanvas";
1951
- const windowObj = window;
1952
- delete windowObj[canvasName];
1953
- if (windowObj.__markupCanvasInstances) {
1954
- windowObj.__markupCanvasInstances.delete(canvasName);
1955
- }
1956
- }
1957
- broadcastEvent(event, data) {
1958
- if (typeof window === "undefined") {
1959
- return;
1960
- }
1961
- // Receivers can get the instance from the window binding
1962
- let broadcastData = data;
1963
- if (event === "ready") {
1964
- broadcastData = { ready: true };
1965
- }
1966
- window.postMessage({
1967
- source: "markup-canvas",
1968
- event,
1969
- data: broadcastData,
1970
- timestamp: Date.now(),
1971
- canvasName: this.config.name,
1972
- }, "*");
1973
- if (window.parent) {
1974
- window.parent.postMessage({
1975
- source: "markup-canvas",
1976
- event,
1977
- data: broadcastData,
1978
- timestamp: Date.now(),
1979
- canvasName: this.config.name,
1980
- }, "*");
1981
- }
2247
+ this.event.emit("ready", this);
1982
2248
  }
1983
2249
  setupEventHandlers() {
1984
2250
  try {
1985
- // Wheel zoom
2251
+ // Wheel events
1986
2252
  withFeatureEnabled(this.config, "enableZoom", () => {
1987
2253
  const wheelCleanup = setupWheelEvents(this, this.config);
1988
- this.cleanupFunctions.push(wheelCleanup);
2254
+ this.cleanupCallbacks.push(wheelCleanup);
1989
2255
  });
1990
- // Mouse events (drag and click-to-zoom)
1991
- // Set up mouse events if either pan or click-to-zoom is enabled
2256
+ // Mouse events
1992
2257
  if (this.config.enablePan || this.config.enableClickToZoom) {
1993
2258
  this.dragSetup = setupMouseEvents(this, this.config, true);
1994
- this.cleanupFunctions.push(this.dragSetup.cleanup);
2259
+ this.cleanupCallbacks.push(this.dragSetup.cleanup);
1995
2260
  }
1996
- // Keyboard navigation
2261
+ // Keyboard events
1997
2262
  withFeatureEnabled(this.config, "enableKeyboard", () => {
1998
2263
  const keyboardCleanup = setupKeyboardEvents(this, this.config);
1999
- this.cleanupFunctions.push(keyboardCleanup);
2264
+ this.cleanupCallbacks.push(keyboardCleanup);
2000
2265
  });
2001
- // Touch events (if enabled)
2266
+ // Touch events
2002
2267
  withFeatureEnabled(this.config, "enableTouch", () => {
2003
2268
  const touchCleanup = setupTouchEvents(this);
2004
- this.cleanupFunctions.push(touchCleanup);
2269
+ this.cleanupCallbacks.push(touchCleanup);
2005
2270
  });
2006
2271
  // Set up rulers and grid
2007
2272
  withFeatureEnabled(this.config, "enableRulers", () => {
2008
- this.rulers = createRulers(this.baseCanvas, this.config);
2009
- this.cleanupFunctions.push(() => {
2273
+ this.rulers = createRulers(this, this.config);
2274
+ this.cleanupCallbacks.push(() => {
2010
2275
  if (this.rulers) {
2011
2276
  this.rulers.destroy();
2012
2277
  }
@@ -2019,20 +2284,18 @@ class MarkupCanvas {
2019
2284
  throw error;
2020
2285
  }
2021
2286
  }
2022
- // Base canvas properties and methods
2023
2287
  get container() {
2024
- return this.baseCanvas.container;
2288
+ return this.canvas.container;
2025
2289
  }
2026
2290
  get transformLayer() {
2027
- return this.baseCanvas.transformLayer;
2291
+ return this.canvas.transformLayer;
2028
2292
  }
2029
2293
  get contentLayer() {
2030
- return this.baseCanvas.contentLayer;
2294
+ return this.canvas.contentLayer;
2031
2295
  }
2032
2296
  get transform() {
2033
- return this.baseCanvas.transform;
2297
+ return this.canvas.transform;
2034
2298
  }
2035
- // State management getters for React integration
2036
2299
  get isReady() {
2037
2300
  return this._isReady;
2038
2301
  }
@@ -2040,125 +2303,65 @@ class MarkupCanvas {
2040
2303
  return this.dragSetup?.isEnabled() || false;
2041
2304
  }
2042
2305
  get visibleBounds() {
2043
- return this.getVisibleArea();
2306
+ return getVisibleArea(this);
2044
2307
  }
2045
2308
  getBounds() {
2046
- return this.baseCanvas.getBounds();
2309
+ return getCanvasBounds(this.canvas, this.config);
2047
2310
  }
2048
2311
  updateTransform(newTransform) {
2049
- const result = this.baseCanvas.updateTransform(newTransform);
2312
+ const result = updateTransform(this.canvas, newTransform);
2050
2313
  if (result) {
2051
- this.emitTransformEvents();
2314
+ emitTransformEvents(this.event, this.canvas);
2052
2315
  }
2053
2316
  return result;
2054
2317
  }
2055
- emitTransformEvents() {
2056
- const transform = this.baseCanvas.transform;
2057
- this.listen.emit("transform", transform);
2058
- this.listen.emit("zoom", transform.scale);
2059
- this.listen.emit("pan", { x: transform.translateX, y: transform.translateY });
2060
- }
2061
2318
  reset() {
2062
- return this.baseCanvas.reset();
2063
- }
2064
- handleResize() {
2065
- return this.baseCanvas.handleResize();
2319
+ const result = resetTransform(this.canvas);
2320
+ if (result) {
2321
+ emitTransformEvents(this.event, this.canvas);
2322
+ }
2323
+ return result;
2066
2324
  }
2067
2325
  setZoom(zoomLevel) {
2068
- return withTransition(this.transformLayer, this.config, () => {
2069
- return withClampedZoom(this.config, (clamp) => {
2070
- const newScale = clamp(zoomLevel);
2071
- const newTransform = {
2072
- scale: newScale,
2073
- };
2074
- return this.updateTransform(newTransform);
2075
- });
2076
- });
2326
+ return setZoom(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), zoomLevel);
2077
2327
  }
2078
2328
  canvasToContent(x, y) {
2079
- return this.baseCanvas.canvasToContent(x, y);
2329
+ const matrix = createMatrix(this.canvas.transform.scale, this.canvas.transform.translateX, this.canvas.transform.translateY);
2330
+ return canvasToContent(x, y, matrix);
2080
2331
  }
2081
2332
  zoomToPoint(x, y, targetScale) {
2082
- return withTransition(this.transformLayer, this.config, () => {
2083
- const result = this.baseCanvas.zoomToPoint(x, y, targetScale);
2084
- if (result) {
2085
- this.emitTransformEvents();
2086
- }
2087
- return result;
2088
- });
2333
+ return zoomToPoint(this.canvas, this.transformLayer, this.config, x, y, targetScale);
2089
2334
  }
2090
2335
  resetView() {
2091
- return withTransition(this.transformLayer, this.config, () => {
2092
- const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
2093
- if (result) {
2094
- this.emitTransformEvents();
2095
- }
2096
- return result;
2097
- });
2336
+ return resetView(this.canvas, this.transformLayer, this.config);
2098
2337
  }
2099
- zoomToFitContent() {
2100
- return withTransition(this.transformLayer, this.config, () => {
2101
- const result = this.baseCanvas.zoomToFitContent();
2102
- if (result) {
2103
- this.emitTransformEvents();
2104
- }
2105
- return result;
2106
- });
2338
+ resetViewToCenter() {
2339
+ return resetViewToCenter(this, this.transformLayer, this.config, this.zoomToPoint.bind(this));
2107
2340
  }
2108
- // Pan methods
2109
2341
  panLeft(distance) {
2110
- const panDistance = distance ?? this.config.keyboardPanStep;
2111
- const newTransform = {
2112
- translateX: this.baseCanvas.transform.translateX + panDistance,
2113
- };
2114
- return this.updateTransform(newTransform);
2342
+ return (panLeft(this.canvas, this.config, this.updateTransform.bind(this)) ||
2343
+ (distance ? panLeft(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2115
2344
  }
2116
2345
  panRight(distance) {
2117
- const panDistance = distance ?? this.config.keyboardPanStep;
2118
- const newTransform = {
2119
- translateX: this.baseCanvas.transform.translateX - panDistance,
2120
- };
2121
- return this.updateTransform(newTransform);
2346
+ return (panRight(this.canvas, this.config, this.updateTransform.bind(this)) ||
2347
+ (distance ? panRight(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2122
2348
  }
2123
2349
  panUp(distance) {
2124
- const panDistance = distance ?? this.config.keyboardPanStep;
2125
- const newTransform = {
2126
- translateY: this.baseCanvas.transform.translateY + panDistance,
2127
- };
2128
- return this.updateTransform(newTransform);
2350
+ return (panUp(this.canvas, this.config, this.updateTransform.bind(this)) ||
2351
+ (distance ? panUp(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2129
2352
  }
2130
2353
  panDown(distance) {
2131
- const panDistance = distance ?? this.config.keyboardPanStep;
2132
- const newTransform = {
2133
- translateY: this.baseCanvas.transform.translateY - panDistance,
2134
- };
2135
- return this.updateTransform(newTransform);
2354
+ return (panDown(this.canvas, this.config, this.updateTransform.bind(this)) ||
2355
+ (distance ? panDown(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2136
2356
  }
2137
- // Zoom methods
2138
- zoomIn(factor = 0.1) {
2139
- return withTransition(this.transformLayer, this.config, () => {
2140
- return withClampedZoom(this.config, (clamp) => {
2141
- const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
2142
- const newTransform = {
2143
- scale: newScale,
2144
- };
2145
- return this.updateTransform(newTransform);
2146
- });
2147
- });
2357
+ zoomIn(factor = 0.5) {
2358
+ return zoomIn(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
2148
2359
  }
2149
- zoomOut(factor = 0.1) {
2150
- return withTransition(this.transformLayer, this.config, () => {
2151
- return withClampedZoom(this.config, (clamp) => {
2152
- const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
2153
- const newTransform = {
2154
- scale: newScale,
2155
- };
2156
- return this.updateTransform(newTransform);
2157
- });
2158
- });
2360
+ zoomOut(factor = 0.5) {
2361
+ return zoomOut(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
2159
2362
  }
2160
2363
  resetZoom() {
2161
- return this.resetView();
2364
+ return this.resetViewToCenter();
2162
2365
  }
2163
2366
  // Mouse drag control methods
2164
2367
  enableMouseDrag() {
@@ -2170,110 +2373,68 @@ class MarkupCanvas {
2170
2373
  isMouseDragEnabled() {
2171
2374
  return this.dragSetup?.isEnabled() ?? false;
2172
2375
  }
2173
- // Grid control methods
2174
2376
  toggleGrid() {
2175
- if (this.rulers?.toggleGrid) {
2176
- this.rulers.toggleGrid();
2177
- return true;
2377
+ const result = toggleGrid(this.rulers);
2378
+ if (result) {
2379
+ this.event.emit("gridVisibility", this.isGridVisible());
2178
2380
  }
2179
- return false;
2381
+ return result;
2180
2382
  }
2181
2383
  showGrid() {
2182
- if (this.rulers?.gridOverlay) {
2183
- this.rulers.gridOverlay.style.display = "block";
2184
- return true;
2384
+ const result = showGrid(this.rulers);
2385
+ if (result) {
2386
+ this.event.emit("gridVisibility", true);
2185
2387
  }
2186
- return false;
2388
+ return result;
2187
2389
  }
2188
2390
  hideGrid() {
2189
- if (this.rulers?.gridOverlay) {
2190
- this.rulers.gridOverlay.style.display = "none";
2191
- return true;
2391
+ const result = hideGrid(this.rulers);
2392
+ if (result) {
2393
+ this.event.emit("gridVisibility", false);
2192
2394
  }
2193
- return false;
2395
+ return result;
2194
2396
  }
2195
2397
  isGridVisible() {
2196
- if (this.rulers?.gridOverlay) {
2197
- return this.rulers.gridOverlay.style.display !== "none";
2198
- }
2199
- return false;
2398
+ return isGridVisible(this.rulers);
2200
2399
  }
2201
- // Ruler control methods
2202
2400
  toggleRulers() {
2203
- if (this.rulers) {
2204
- const areVisible = this.areRulersVisible();
2205
- if (areVisible) {
2206
- this.rulers.hide();
2207
- }
2208
- else {
2209
- this.rulers.show();
2210
- }
2211
- return true;
2401
+ const result = toggleRulers(this.rulers, () => this.areRulersVisible());
2402
+ if (result) {
2403
+ this.event.emit("rulersVisibility", this.areRulersVisible());
2212
2404
  }
2213
- return false;
2405
+ return result;
2214
2406
  }
2215
2407
  showRulers() {
2216
- if (this.rulers) {
2217
- this.rulers.show();
2218
- return true;
2408
+ const result = showRulers(this.rulers);
2409
+ if (result) {
2410
+ this.event.emit("rulersVisibility", true);
2219
2411
  }
2220
- return false;
2412
+ return result;
2221
2413
  }
2222
2414
  hideRulers() {
2223
- if (this.rulers) {
2224
- this.rulers.hide();
2225
- return true;
2415
+ const result = hideRulers(this.rulers);
2416
+ if (result) {
2417
+ this.event.emit("rulersVisibility", false);
2226
2418
  }
2227
- return false;
2419
+ return result;
2228
2420
  }
2229
2421
  areRulersVisible() {
2230
- if (this.rulers?.horizontalRuler) {
2231
- return this.rulers.horizontalRuler.style.display !== "none";
2232
- }
2233
- return false;
2422
+ return areRulersVisible(this.rulers);
2234
2423
  }
2235
- // Utility methods
2236
2424
  centerContent() {
2237
- return withTransition(this.transformLayer, this.config, () => {
2238
- const bounds = this.baseCanvas.getBounds();
2239
- const centerX = (bounds.width - bounds.contentWidth * this.baseCanvas.transform.scale) / 2;
2240
- const centerY = (bounds.height - bounds.contentHeight * this.baseCanvas.transform.scale) / 2;
2241
- return this.updateTransform({
2242
- translateX: centerX,
2243
- translateY: centerY,
2244
- });
2245
- });
2425
+ return centerContent(this.canvas, this.config, this.updateTransform.bind(this), this.transformLayer);
2246
2426
  }
2247
2427
  fitToScreen() {
2248
- return withTransition(this.transformLayer, this.config, () => {
2249
- const result = this.baseCanvas.zoomToFitContent();
2250
- if (result) {
2251
- this.emitTransformEvents();
2252
- }
2253
- return result;
2254
- });
2428
+ return fitToScreen(this.canvas, this.transformLayer, this.config);
2255
2429
  }
2256
2430
  getVisibleArea() {
2257
- const bounds = this.baseCanvas.getBounds();
2258
- return bounds.visibleArea;
2431
+ return getVisibleArea(this);
2259
2432
  }
2260
2433
  isPointVisible(x, y) {
2261
- const visibleArea = this.getVisibleArea();
2262
- return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
2434
+ return isPointVisible(this, x, y);
2263
2435
  }
2264
2436
  scrollToPoint(x, y) {
2265
- return withTransition(this.transformLayer, this.config, () => {
2266
- const bounds = this.baseCanvas.getBounds();
2267
- const centerX = bounds.width / 2;
2268
- const centerY = bounds.height / 2;
2269
- // Calculate new translation to center the point
2270
- const newTranslateX = centerX - x * this.baseCanvas.transform.scale;
2271
- const newTranslateY = centerY - y * this.baseCanvas.transform.scale;
2272
- return this.updateTransform({
2273
- translateX: newTranslateX,
2274
- translateY: newTranslateY,
2275
- });
2276
- });
2437
+ return scrollToPoint(this.canvas, this.config, x, y, this.updateTransform.bind(this), this.transformLayer);
2277
2438
  }
2278
2439
  // Configuration access
2279
2440
  getConfig() {
@@ -2284,28 +2445,28 @@ class MarkupCanvas {
2284
2445
  }
2285
2446
  // Theme management
2286
2447
  updateThemeMode(mode) {
2287
- const newConfig = {
2288
- ...this.config,
2289
- themeMode: mode,
2290
- };
2291
- this.config = createMarkupCanvasConfig(newConfig);
2292
- // Update canvas background color
2293
- const backgroundColor = getThemeValue(this.config, "canvasBackgroundColor");
2294
- this.baseCanvas.container.style.backgroundColor = backgroundColor;
2295
- // Update rulers if they exist
2296
- if (this.rulers) {
2297
- this.rulers.updateTheme(this.config);
2298
- }
2448
+ this.config = createMarkupCanvasConfig({ ...this.config, themeMode: mode });
2449
+ updateThemeMode(this.canvas.container, this.config, this.rulers, mode);
2450
+ }
2451
+ toggleThemeMode() {
2452
+ const currentMode = this.config.themeMode;
2453
+ const newMode = currentMode === "light" ? "dark" : "light";
2454
+ this.updateThemeMode(newMode);
2455
+ return newMode;
2456
+ }
2457
+ // Transition management
2458
+ updateTransition(enabled) {
2459
+ this.config = createMarkupCanvasConfig({ ...this.config, enableTransition: enabled });
2460
+ }
2461
+ toggleTransitionMode() {
2462
+ const newEnableTransition = toggleTransition(this.config.enableTransition);
2463
+ this.updateTransition(newEnableTransition);
2464
+ return newEnableTransition;
2299
2465
  }
2300
2466
  // Cleanup method
2301
2467
  cleanup() {
2302
- this.cleanupGlobalBinding();
2303
- // Cleanup postMessage listener
2304
- if (this.postMessageCleanup) {
2305
- this.postMessageCleanup();
2306
- this.postMessageCleanup = null;
2307
- }
2308
- this.cleanupFunctions.forEach((cleanup) => {
2468
+ cleanupWindowBinding(this.config);
2469
+ this.cleanupCallbacks.forEach((cleanup) => {
2309
2470
  try {
2310
2471
  cleanup();
2311
2472
  }
@@ -2313,22 +2474,22 @@ class MarkupCanvas {
2313
2474
  console.warn("Error during cleanup:", cleanupError);
2314
2475
  }
2315
2476
  });
2316
- this.cleanupFunctions = [];
2477
+ this.cleanupCallbacks = [];
2317
2478
  // Remove all event listeners
2318
2479
  this.removeAllListeners();
2319
2480
  }
2320
2481
  // Event emitter delegation methods
2321
2482
  on(event, handler) {
2322
- this.listen.on(event, handler);
2483
+ this.event.on(event, handler);
2323
2484
  }
2324
2485
  off(event, handler) {
2325
- this.listen.off(event, handler);
2486
+ this.event.off(event, handler);
2326
2487
  }
2327
2488
  emit(event, data) {
2328
- this.listen.emit(event, data);
2489
+ this.event.emit(event, data);
2329
2490
  }
2330
2491
  removeAllListeners() {
2331
- this.listen.removeAllListeners();
2492
+ this.event.removeAllListeners();
2332
2493
  }
2333
2494
  destroy() {
2334
2495
  this.cleanup();