@markup-canvas/core 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,6 +66,8 @@ const canvas = new MarkupCanvas(container, {
66
66
  | `enableTouch` | `boolean` | `true` | Enable touch gestures |
67
67
  | `enableKeyboard` | `boolean` | `true` | Enable keyboard controls |
68
68
  | `limitKeyboardEventsToCanvas` | `boolean` | `true` | Limit keyboard events to when canvas is focused |
69
+ | `bindToWindow` | `boolean` | `false` | Bind canvas instance to window (enables cross-iframe communication) |
70
+ | `enablePostMessageAPI` | `boolean` | `false` | Enable postMessage API for controlling canvas from outside iframe |
69
71
 
70
72
  #### Zoom Behavior
71
73
 
@@ -136,6 +138,87 @@ const canvas = new MarkupCanvas(container, {
136
138
  |--------|------|-------------|
137
139
  | `onTransformUpdate` | `(transform: Transform) => void` | Called continuously during transform updates |
138
140
 
141
+ ### PostMessage API - Control from Outside Iframe
142
+
143
+ Control the MarkupCanvas from a parent window or external application. Useful for Sandpack, iframes, and cross-origin scenarios.
144
+
145
+ #### Setup
146
+
147
+ ```javascript
148
+ const canvas = new MarkupCanvas(container, {
149
+ width: 20000,
150
+ height: 15000,
151
+ bindToWindow: true, // Required: binds canvas to window
152
+ enablePostMessageAPI: true, // Enable postMessage listeners
153
+ name: "canvas", // Canvas instance name
154
+ });
155
+ ```
156
+
157
+ #### Send Commands from Parent
158
+
159
+ ```javascript
160
+ // Get reference to the iframe
161
+ const iframe = document.querySelector('iframe');
162
+
163
+ // Zoom to 2x (200%)
164
+ iframe.contentWindow.postMessage({
165
+ source: "markup-canvas",
166
+ canvasName: "canvas",
167
+ action: "setZoom",
168
+ args: [2.0]
169
+ }, "*");
170
+
171
+ // Pan left by 100 pixels
172
+ iframe.contentWindow.postMessage({
173
+ source: "markup-canvas",
174
+ canvasName: "canvas",
175
+ action: "panLeft",
176
+ args: [100]
177
+ }, "*");
178
+
179
+ // Toggle theme
180
+ iframe.contentWindow.postMessage({
181
+ source: "markup-canvas",
182
+ canvasName: "canvas",
183
+ action: "toggleThemeMode"
184
+ }, "*");
185
+ ```
186
+
187
+ #### Available PostMessage Actions
188
+
189
+ **View Control:**
190
+ - `zoomIn(factor?)` - Zoom in by factor (default 0.1)
191
+ - `zoomOut(factor?)` - Zoom out by factor (default 0.1)
192
+ - `setZoom(level)` - Set zoom to specific level
193
+ - `resetZoom()` - Reset to 100%
194
+ - `panLeft(distance?)`, `panRight(distance?)`, `panUp(distance?)`, `panDown(distance?)` - Pan operations
195
+ - `fitToScreen()` - Fit content to viewport
196
+ - `centerContent()` - Center content
197
+ - `scrollToPoint(x, y)` - Pan to specific coordinates
198
+
199
+ **Rulers & Grid:**
200
+ - `toggleRulers()`, `showRulers()`, `hideRulers()`
201
+ - `toggleGrid()`, `showGrid()`, `hideGrid()`
202
+
203
+ **Theme:**
204
+ - `updateThemeMode(mode)` - Set theme ("light" or "dark")
205
+ - `toggleThemeMode()` - Toggle between themes
206
+
207
+ #### Error Handling
208
+
209
+ Listen for errors when actions fail:
210
+
211
+ ```javascript
212
+ window.addEventListener("message", (event) => {
213
+ if (event.data.source === "markup-canvas-error") {
214
+ console.error(
215
+ `Action failed: ${event.data.action}`,
216
+ event.data.error
217
+ );
218
+ }
219
+ });
220
+ ```
221
+
139
222
  ### Event Handling
140
223
 
141
224
  Listen to various events emitted by the canvas:
@@ -12,6 +12,7 @@ export declare class MarkupCanvas implements Canvas {
12
12
  config: Required<MarkupCanvasConfig>;
13
13
  private _isReady;
14
14
  private listen;
15
+ private postMessageCleanup;
15
16
  constructor(container: HTMLElement, options?: MarkupCanvasConfig);
16
17
  private setupGlobalBinding;
17
18
  private cleanupGlobalBinding;
@@ -1,5 +1,6 @@
1
1
  export { setupKeyboardEvents } from "./keyboard/setupKeyboardEvents.js";
2
2
  export { setupMouseEvents } from "./mouse/setupMouseEvents.js";
3
+ export { setupPostMessageEvents } from "./postMessage/setupPostMessageEvents.js";
3
4
  export { setupTouchEvents } from "./touch/setupTouchEvents.js";
4
5
  export { detectTrackpadGesture } from "./trackpad/detectTrackpadGesture.js";
5
6
  export { getAdaptiveZoomSpeed } from "./utils/getAdaptiveZoomSpeed.js";
@@ -0,0 +1 @@
1
+ export declare function sendPostMessageError(canvasName: string, action: string, error: string): void;
@@ -0,0 +1,2 @@
1
+ import type { MarkupCanvas } from "@/lib/MarkupCanvas.js";
2
+ export declare function setupPostMessageEvents(canvas: MarkupCanvas): () => void;
@@ -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.1
4
+ * @version 1.1.3
5
5
  */
6
6
  'use strict';
7
7
 
@@ -15,6 +15,7 @@ const EDITOR_PRESET = {
15
15
  // Global Binding & Instance Access
16
16
  bindToWindow: true,
17
17
  name: "canvas",
18
+ enablePostMessageAPI: true,
18
19
  // Interaction controls
19
20
  enableZoom: true,
20
21
  enablePan: true,
@@ -313,6 +314,10 @@ const DEFAULT_CONFIG = {
313
314
  width: 8000,
314
315
  height: 8000,
315
316
  enableAcceleration: true,
317
+ // Global Binding & Instance Access
318
+ bindToWindow: false,
319
+ name: "markupCanvas",
320
+ enablePostMessageAPI: false,
316
321
  // Interaction controls
317
322
  enableZoom: true,
318
323
  enablePan: true,
@@ -364,9 +369,6 @@ const DEFAULT_CONFIG = {
364
369
  gridColorDark: "rgba(232, 86, 193, 0.5)",
365
370
  // Theme
366
371
  themeMode: "light",
367
- // Global Binding & Instance Access
368
- bindToWindow: false,
369
- name: "markupCanvas",
370
372
  // Callbacks
371
373
  onTransformUpdate: () => { },
372
374
  };
@@ -842,14 +844,14 @@ function setupKeyboardEvents(canvas, config) {
842
844
  break;
843
845
  case "g":
844
846
  case "G":
845
- if (canvas.toggleGrid) {
847
+ if (event.shiftKey && canvas.toggleGrid) {
846
848
  canvas.toggleGrid();
847
849
  }
848
- handled = true;
850
+ handled = event.shiftKey;
849
851
  break;
850
852
  case "r":
851
853
  case "R":
852
- if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
854
+ if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
853
855
  canvas.toggleRulers();
854
856
  handled = true;
855
857
  }
@@ -1154,6 +1156,122 @@ function setupMouseEvents(canvas, config, withControls = true) {
1154
1156
  return cleanup;
1155
1157
  }
1156
1158
 
1159
+ function sendPostMessageError(canvasName, action, error) {
1160
+ window.postMessage({
1161
+ source: "markup-canvas-error",
1162
+ canvasName,
1163
+ action,
1164
+ error,
1165
+ timestamp: Date.now(),
1166
+ }, "*");
1167
+ }
1168
+
1169
+ function setupPostMessageEvents(canvas) {
1170
+ const handleMessage = (event) => {
1171
+ const data = event.data;
1172
+ // Validate message structure
1173
+ if (data.source !== "markup-canvas") {
1174
+ return;
1175
+ }
1176
+ const canvasName = canvas.config.name || "markupCanvas";
1177
+ if (data.canvasName !== canvasName) {
1178
+ return;
1179
+ }
1180
+ const action = data.action;
1181
+ const args = data.args || [];
1182
+ try {
1183
+ // View methods
1184
+ if (action === "zoomIn") {
1185
+ canvas.zoomIn(args[0]);
1186
+ }
1187
+ else if (action === "zoomOut") {
1188
+ canvas.zoomOut(args[0]);
1189
+ }
1190
+ else if (action === "setZoom") {
1191
+ const zoomLevel = args[0];
1192
+ if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
1193
+ throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
1194
+ }
1195
+ canvas.setZoom(zoomLevel);
1196
+ }
1197
+ else if (action === "resetZoom") {
1198
+ canvas.resetZoom();
1199
+ }
1200
+ else if (action === "panLeft") {
1201
+ canvas.panLeft(args[0]);
1202
+ }
1203
+ else if (action === "panRight") {
1204
+ canvas.panRight(args[0]);
1205
+ }
1206
+ else if (action === "panUp") {
1207
+ canvas.panUp(args[0]);
1208
+ }
1209
+ else if (action === "panDown") {
1210
+ canvas.panDown(args[0]);
1211
+ }
1212
+ else if (action === "fitToScreen") {
1213
+ canvas.fitToScreen();
1214
+ }
1215
+ else if (action === "centerContent") {
1216
+ canvas.centerContent();
1217
+ }
1218
+ else if (action === "scrollToPoint") {
1219
+ canvas.scrollToPoint(args[0], args[1]);
1220
+ }
1221
+ else if (action === "resetView") {
1222
+ canvas.resetView();
1223
+ }
1224
+ // Ruler/Grid methods
1225
+ else if (action === "toggleRulers") {
1226
+ canvas.toggleRulers();
1227
+ }
1228
+ else if (action === "showRulers") {
1229
+ canvas.showRulers();
1230
+ }
1231
+ else if (action === "hideRulers") {
1232
+ canvas.hideRulers();
1233
+ }
1234
+ else if (action === "toggleGrid") {
1235
+ canvas.toggleGrid();
1236
+ }
1237
+ else if (action === "showGrid") {
1238
+ canvas.showGrid();
1239
+ }
1240
+ else if (action === "hideGrid") {
1241
+ canvas.hideGrid();
1242
+ }
1243
+ // Config methods
1244
+ else if (action === "updateThemeMode") {
1245
+ const mode = args[0];
1246
+ if (mode !== "light" && mode !== "dark") {
1247
+ throw new Error(`Invalid theme mode: ${mode}`);
1248
+ }
1249
+ canvas.updateThemeMode(mode);
1250
+ }
1251
+ else if (action === "toggleThemeMode") {
1252
+ const currentConfig = canvas.getConfig();
1253
+ const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
1254
+ canvas.updateThemeMode(newMode);
1255
+ }
1256
+ else {
1257
+ throw new Error(`Unknown action: ${action}`);
1258
+ }
1259
+ }
1260
+ catch (error) {
1261
+ const errorMessage = error instanceof Error ? error.message : String(error);
1262
+ sendPostMessageError(canvasName, action, errorMessage);
1263
+ }
1264
+ };
1265
+ if (typeof window !== "undefined") {
1266
+ window.addEventListener("message", handleMessage);
1267
+ }
1268
+ return () => {
1269
+ if (typeof window !== "undefined") {
1270
+ window.removeEventListener("message", handleMessage);
1271
+ }
1272
+ };
1273
+ }
1274
+
1157
1275
  function handleTouchEnd(event, touchState) {
1158
1276
  touchState.touches = Array.from(event.touches);
1159
1277
  if (touchState.touches.length < 2) {
@@ -1784,6 +1902,7 @@ class MarkupCanvas {
1784
1902
  this.dragSetup = null;
1785
1903
  this._isReady = false;
1786
1904
  this.listen = new EventEmitter();
1905
+ this.postMessageCleanup = null;
1787
1906
  if (!container) {
1788
1907
  throw new Error("Container element is required");
1789
1908
  }
@@ -1799,6 +1918,10 @@ class MarkupCanvas {
1799
1918
  this.broadcastEvent(event, data);
1800
1919
  });
1801
1920
  this.setupGlobalBinding();
1921
+ // Set up postMessage listener
1922
+ if (this.config.enablePostMessageAPI) {
1923
+ this.postMessageCleanup = setupPostMessageEvents(this);
1924
+ }
1802
1925
  }
1803
1926
  this.setupEventHandlers();
1804
1927
  this._isReady = true;
@@ -2161,6 +2284,11 @@ class MarkupCanvas {
2161
2284
  // Cleanup method
2162
2285
  cleanup() {
2163
2286
  this.cleanupGlobalBinding();
2287
+ // Cleanup postMessage listener
2288
+ if (this.postMessageCleanup) {
2289
+ this.postMessageCleanup();
2290
+ this.postMessageCleanup = null;
2291
+ }
2164
2292
  this.cleanupFunctions.forEach((cleanup) => {
2165
2293
  try {
2166
2294
  cleanup();
@@ -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.1
4
+ * @version 1.1.3
5
5
  */
6
6
  const EDITOR_PRESET = {
7
7
  // Canvas dimensions
@@ -11,6 +11,7 @@ const EDITOR_PRESET = {
11
11
  // Global Binding & Instance Access
12
12
  bindToWindow: true,
13
13
  name: "canvas",
14
+ enablePostMessageAPI: true,
14
15
  // Interaction controls
15
16
  enableZoom: true,
16
17
  enablePan: true,
@@ -309,6 +310,10 @@ const DEFAULT_CONFIG = {
309
310
  width: 8000,
310
311
  height: 8000,
311
312
  enableAcceleration: true,
313
+ // Global Binding & Instance Access
314
+ bindToWindow: false,
315
+ name: "markupCanvas",
316
+ enablePostMessageAPI: false,
312
317
  // Interaction controls
313
318
  enableZoom: true,
314
319
  enablePan: true,
@@ -360,9 +365,6 @@ const DEFAULT_CONFIG = {
360
365
  gridColorDark: "rgba(232, 86, 193, 0.5)",
361
366
  // Theme
362
367
  themeMode: "light",
363
- // Global Binding & Instance Access
364
- bindToWindow: false,
365
- name: "markupCanvas",
366
368
  // Callbacks
367
369
  onTransformUpdate: () => { },
368
370
  };
@@ -838,14 +840,14 @@ function setupKeyboardEvents(canvas, config) {
838
840
  break;
839
841
  case "g":
840
842
  case "G":
841
- if (canvas.toggleGrid) {
843
+ if (event.shiftKey && canvas.toggleGrid) {
842
844
  canvas.toggleGrid();
843
845
  }
844
- handled = true;
846
+ handled = event.shiftKey;
845
847
  break;
846
848
  case "r":
847
849
  case "R":
848
- if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
850
+ if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
849
851
  canvas.toggleRulers();
850
852
  handled = true;
851
853
  }
@@ -1150,6 +1152,122 @@ function setupMouseEvents(canvas, config, withControls = true) {
1150
1152
  return cleanup;
1151
1153
  }
1152
1154
 
1155
+ function sendPostMessageError(canvasName, action, error) {
1156
+ window.postMessage({
1157
+ source: "markup-canvas-error",
1158
+ canvasName,
1159
+ action,
1160
+ error,
1161
+ timestamp: Date.now(),
1162
+ }, "*");
1163
+ }
1164
+
1165
+ function setupPostMessageEvents(canvas) {
1166
+ const handleMessage = (event) => {
1167
+ const data = event.data;
1168
+ // Validate message structure
1169
+ if (data.source !== "markup-canvas") {
1170
+ return;
1171
+ }
1172
+ const canvasName = canvas.config.name || "markupCanvas";
1173
+ if (data.canvasName !== canvasName) {
1174
+ return;
1175
+ }
1176
+ const action = data.action;
1177
+ const args = data.args || [];
1178
+ try {
1179
+ // View methods
1180
+ if (action === "zoomIn") {
1181
+ canvas.zoomIn(args[0]);
1182
+ }
1183
+ else if (action === "zoomOut") {
1184
+ canvas.zoomOut(args[0]);
1185
+ }
1186
+ else if (action === "setZoom") {
1187
+ const zoomLevel = args[0];
1188
+ if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
1189
+ throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
1190
+ }
1191
+ canvas.setZoom(zoomLevel);
1192
+ }
1193
+ else if (action === "resetZoom") {
1194
+ canvas.resetZoom();
1195
+ }
1196
+ else if (action === "panLeft") {
1197
+ canvas.panLeft(args[0]);
1198
+ }
1199
+ else if (action === "panRight") {
1200
+ canvas.panRight(args[0]);
1201
+ }
1202
+ else if (action === "panUp") {
1203
+ canvas.panUp(args[0]);
1204
+ }
1205
+ else if (action === "panDown") {
1206
+ canvas.panDown(args[0]);
1207
+ }
1208
+ else if (action === "fitToScreen") {
1209
+ canvas.fitToScreen();
1210
+ }
1211
+ else if (action === "centerContent") {
1212
+ canvas.centerContent();
1213
+ }
1214
+ else if (action === "scrollToPoint") {
1215
+ canvas.scrollToPoint(args[0], args[1]);
1216
+ }
1217
+ else if (action === "resetView") {
1218
+ canvas.resetView();
1219
+ }
1220
+ // Ruler/Grid methods
1221
+ else if (action === "toggleRulers") {
1222
+ canvas.toggleRulers();
1223
+ }
1224
+ else if (action === "showRulers") {
1225
+ canvas.showRulers();
1226
+ }
1227
+ else if (action === "hideRulers") {
1228
+ canvas.hideRulers();
1229
+ }
1230
+ else if (action === "toggleGrid") {
1231
+ canvas.toggleGrid();
1232
+ }
1233
+ else if (action === "showGrid") {
1234
+ canvas.showGrid();
1235
+ }
1236
+ else if (action === "hideGrid") {
1237
+ canvas.hideGrid();
1238
+ }
1239
+ // Config methods
1240
+ else if (action === "updateThemeMode") {
1241
+ const mode = args[0];
1242
+ if (mode !== "light" && mode !== "dark") {
1243
+ throw new Error(`Invalid theme mode: ${mode}`);
1244
+ }
1245
+ canvas.updateThemeMode(mode);
1246
+ }
1247
+ else if (action === "toggleThemeMode") {
1248
+ const currentConfig = canvas.getConfig();
1249
+ const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
1250
+ canvas.updateThemeMode(newMode);
1251
+ }
1252
+ else {
1253
+ throw new Error(`Unknown action: ${action}`);
1254
+ }
1255
+ }
1256
+ catch (error) {
1257
+ const errorMessage = error instanceof Error ? error.message : String(error);
1258
+ sendPostMessageError(canvasName, action, errorMessage);
1259
+ }
1260
+ };
1261
+ if (typeof window !== "undefined") {
1262
+ window.addEventListener("message", handleMessage);
1263
+ }
1264
+ return () => {
1265
+ if (typeof window !== "undefined") {
1266
+ window.removeEventListener("message", handleMessage);
1267
+ }
1268
+ };
1269
+ }
1270
+
1153
1271
  function handleTouchEnd(event, touchState) {
1154
1272
  touchState.touches = Array.from(event.touches);
1155
1273
  if (touchState.touches.length < 2) {
@@ -1780,6 +1898,7 @@ class MarkupCanvas {
1780
1898
  this.dragSetup = null;
1781
1899
  this._isReady = false;
1782
1900
  this.listen = new EventEmitter();
1901
+ this.postMessageCleanup = null;
1783
1902
  if (!container) {
1784
1903
  throw new Error("Container element is required");
1785
1904
  }
@@ -1795,6 +1914,10 @@ class MarkupCanvas {
1795
1914
  this.broadcastEvent(event, data);
1796
1915
  });
1797
1916
  this.setupGlobalBinding();
1917
+ // Set up postMessage listener
1918
+ if (this.config.enablePostMessageAPI) {
1919
+ this.postMessageCleanup = setupPostMessageEvents(this);
1920
+ }
1798
1921
  }
1799
1922
  this.setupEventHandlers();
1800
1923
  this._isReady = true;
@@ -2157,6 +2280,11 @@ class MarkupCanvas {
2157
2280
  // Cleanup method
2158
2281
  cleanup() {
2159
2282
  this.cleanupGlobalBinding();
2283
+ // Cleanup postMessage listener
2284
+ if (this.postMessageCleanup) {
2285
+ this.postMessageCleanup();
2286
+ this.postMessageCleanup = null;
2287
+ }
2160
2288
  this.cleanupFunctions.forEach((cleanup) => {
2161
2289
  try {
2162
2290
  cleanup();
@@ -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.1
4
+ * @version 1.1.3
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -252,6 +252,10 @@
252
252
  width: 8000,
253
253
  height: 8000,
254
254
  enableAcceleration: true,
255
+ // Global Binding & Instance Access
256
+ bindToWindow: false,
257
+ name: "markupCanvas",
258
+ enablePostMessageAPI: false,
255
259
  // Interaction controls
256
260
  enableZoom: true,
257
261
  enablePan: true,
@@ -303,9 +307,6 @@
303
307
  gridColorDark: "rgba(232, 86, 193, 0.5)",
304
308
  // Theme
305
309
  themeMode: "light",
306
- // Global Binding & Instance Access
307
- bindToWindow: false,
308
- name: "markupCanvas",
309
310
  // Callbacks
310
311
  onTransformUpdate: () => { },
311
312
  };
@@ -781,14 +782,14 @@
781
782
  break;
782
783
  case "g":
783
784
  case "G":
784
- if (canvas.toggleGrid) {
785
+ if (event.shiftKey && canvas.toggleGrid) {
785
786
  canvas.toggleGrid();
786
787
  }
787
- handled = true;
788
+ handled = event.shiftKey;
788
789
  break;
789
790
  case "r":
790
791
  case "R":
791
- if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
792
+ if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
792
793
  canvas.toggleRulers();
793
794
  handled = true;
794
795
  }
@@ -1093,6 +1094,122 @@
1093
1094
  return cleanup;
1094
1095
  }
1095
1096
 
1097
+ function sendPostMessageError(canvasName, action, error) {
1098
+ window.postMessage({
1099
+ source: "markup-canvas-error",
1100
+ canvasName,
1101
+ action,
1102
+ error,
1103
+ timestamp: Date.now(),
1104
+ }, "*");
1105
+ }
1106
+
1107
+ function setupPostMessageEvents(canvas) {
1108
+ const handleMessage = (event) => {
1109
+ const data = event.data;
1110
+ // Validate message structure
1111
+ if (data.source !== "markup-canvas") {
1112
+ return;
1113
+ }
1114
+ const canvasName = canvas.config.name || "markupCanvas";
1115
+ if (data.canvasName !== canvasName) {
1116
+ return;
1117
+ }
1118
+ const action = data.action;
1119
+ const args = data.args || [];
1120
+ try {
1121
+ // View methods
1122
+ if (action === "zoomIn") {
1123
+ canvas.zoomIn(args[0]);
1124
+ }
1125
+ else if (action === "zoomOut") {
1126
+ canvas.zoomOut(args[0]);
1127
+ }
1128
+ else if (action === "setZoom") {
1129
+ const zoomLevel = args[0];
1130
+ if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
1131
+ throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
1132
+ }
1133
+ canvas.setZoom(zoomLevel);
1134
+ }
1135
+ else if (action === "resetZoom") {
1136
+ canvas.resetZoom();
1137
+ }
1138
+ else if (action === "panLeft") {
1139
+ canvas.panLeft(args[0]);
1140
+ }
1141
+ else if (action === "panRight") {
1142
+ canvas.panRight(args[0]);
1143
+ }
1144
+ else if (action === "panUp") {
1145
+ canvas.panUp(args[0]);
1146
+ }
1147
+ else if (action === "panDown") {
1148
+ canvas.panDown(args[0]);
1149
+ }
1150
+ else if (action === "fitToScreen") {
1151
+ canvas.fitToScreen();
1152
+ }
1153
+ else if (action === "centerContent") {
1154
+ canvas.centerContent();
1155
+ }
1156
+ else if (action === "scrollToPoint") {
1157
+ canvas.scrollToPoint(args[0], args[1]);
1158
+ }
1159
+ else if (action === "resetView") {
1160
+ canvas.resetView();
1161
+ }
1162
+ // Ruler/Grid methods
1163
+ else if (action === "toggleRulers") {
1164
+ canvas.toggleRulers();
1165
+ }
1166
+ else if (action === "showRulers") {
1167
+ canvas.showRulers();
1168
+ }
1169
+ else if (action === "hideRulers") {
1170
+ canvas.hideRulers();
1171
+ }
1172
+ else if (action === "toggleGrid") {
1173
+ canvas.toggleGrid();
1174
+ }
1175
+ else if (action === "showGrid") {
1176
+ canvas.showGrid();
1177
+ }
1178
+ else if (action === "hideGrid") {
1179
+ canvas.hideGrid();
1180
+ }
1181
+ // Config methods
1182
+ else if (action === "updateThemeMode") {
1183
+ const mode = args[0];
1184
+ if (mode !== "light" && mode !== "dark") {
1185
+ throw new Error(`Invalid theme mode: ${mode}`);
1186
+ }
1187
+ canvas.updateThemeMode(mode);
1188
+ }
1189
+ else if (action === "toggleThemeMode") {
1190
+ const currentConfig = canvas.getConfig();
1191
+ const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
1192
+ canvas.updateThemeMode(newMode);
1193
+ }
1194
+ else {
1195
+ throw new Error(`Unknown action: ${action}`);
1196
+ }
1197
+ }
1198
+ catch (error) {
1199
+ const errorMessage = error instanceof Error ? error.message : String(error);
1200
+ sendPostMessageError(canvasName, action, errorMessage);
1201
+ }
1202
+ };
1203
+ if (typeof window !== "undefined") {
1204
+ window.addEventListener("message", handleMessage);
1205
+ }
1206
+ return () => {
1207
+ if (typeof window !== "undefined") {
1208
+ window.removeEventListener("message", handleMessage);
1209
+ }
1210
+ };
1211
+ }
1212
+
1096
1213
  function handleTouchEnd(event, touchState) {
1097
1214
  touchState.touches = Array.from(event.touches);
1098
1215
  if (touchState.touches.length < 2) {
@@ -1723,6 +1840,7 @@
1723
1840
  this.dragSetup = null;
1724
1841
  this._isReady = false;
1725
1842
  this.listen = new EventEmitter();
1843
+ this.postMessageCleanup = null;
1726
1844
  if (!container) {
1727
1845
  throw new Error("Container element is required");
1728
1846
  }
@@ -1738,6 +1856,10 @@
1738
1856
  this.broadcastEvent(event, data);
1739
1857
  });
1740
1858
  this.setupGlobalBinding();
1859
+ // Set up postMessage listener
1860
+ if (this.config.enablePostMessageAPI) {
1861
+ this.postMessageCleanup = setupPostMessageEvents(this);
1862
+ }
1741
1863
  }
1742
1864
  this.setupEventHandlers();
1743
1865
  this._isReady = true;
@@ -2100,6 +2222,11 @@
2100
2222
  // Cleanup method
2101
2223
  cleanup() {
2102
2224
  this.cleanupGlobalBinding();
2225
+ // Cleanup postMessage listener
2226
+ if (this.postMessageCleanup) {
2227
+ this.postMessageCleanup();
2228
+ this.postMessageCleanup = null;
2229
+ }
2103
2230
  this.cleanupFunctions.forEach((cleanup) => {
2104
2231
  try {
2105
2232
  cleanup();
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas=t()}(this,function(){"use strict";const e="canvas-container",t="transform-layer",n="content-layer";function r(e,r){const o=Array.from(e.children);let a=e.querySelector(`.${t}`);a||(a=document.createElement("div"),a.className=t,e.appendChild(a)),function(e,t){e.style.position="absolute";const n=t.rulerSize;e.style.top=`${n}px`,e.style.left=`${n}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`,e.style.transformOrigin="0 0"}(a,r);let s=a.querySelector(`.${n}`);return s||(s=document.createElement("div"),s.className=n,a.appendChild(s),function(e,n,r){e.forEach(e=>{e===r||e.classList.contains(t)||n.appendChild(e)})}(o,s,a)),function(e){e.style.position="relative",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}(s),{transformLayer:a,contentLayer:s}}function o(e,t,n){if(!n?.inverse)return{x:e,y:t};try{const r=n.inverse(),o=new DOMPoint(e,t).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:e,y:t}}}function a(e,t){return Math.max(t.minZoom,Math.min(t.maxZoom,e))}function s(e,t,n){return new DOMMatrix([e,0,0,e,t,n])}function i(e,t,n,r,o){const s=o.enableRulers?-o.rulerSize:0,i=n||{scale:1,translateX:s,translateY:s},{scale:l,translateX:c,translateY:u}=i,d=a(l*r,o);if(Math.abs(d-l)<.001)return{scale:l,translateX:c,translateY:u};return{scale:d,translateX:e-(e-c)/l*d,translateY:t-(t-u)/l*d}}function l(e,t){return t(t=>a(t,e))}const c=new Map;function u(e,t,n){return e[t]?n():null}function d(e){let t=null,n=null;const r=(...r)=>{n=r,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},r}function h(e,t,n){return n(null!==e.container.querySelector(".canvas-ruler")?t:0)}function m(e,t,n,r,o){const a=null!==e.container.querySelector(".canvas-ruler");return o(a?t-r:t,a?n-r:n)}function g(e,t){if("dark"===e.themeMode){return e[`${t}Dark`]}return e[t]}const f={width:8e3,height:8e3,enableAcceleration:!0,enableZoom:!0,enablePan:!0,enableTouch:!0,enableKeyboard:!0,bindKeyboardEventsTo:"canvas",zoomSpeed:1.5,minZoom:.05,maxZoom:80,enableTransition:!0,transitionDuration:.2,enableAdaptiveSpeed:!0,enableLeftDrag:!0,enableMiddleDrag:!0,requireSpaceForMouseDrag:!1,keyboardPanStep:50,keyboardFastMultiplier:20,keyboardZoomStep:.2,enableClickToZoom:!0,clickZoomLevel:1,requireOptionForClickZoom:!1,enableRulers:!0,enableGrid:!1,showRulers:!0,showGrid:!1,rulerFontSize:9,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",rulerSize:20,canvasBackgroundColor:"rgba(250, 250, 250, 1)",canvasBackgroundColorDark:"rgba(40, 40, 40, 1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"rgba(240, 240, 240, 1)",rulerTextColor:"rgba(102, 102, 102, 1)",rulerTickColor:"rgba(204, 204, 204, 1)",gridColor:"rgba(232, 86, 193, 0.5)",rulerBackgroundColorDark:"rgba(30, 30, 30, 0.95)",rulerBorderColorDark:"rgba(68, 68, 68, 1)",rulerTextColorDark:"rgba(170, 170, 170, 1)",rulerTickColorDark:"rgba(104, 104, 104, 1)",gridColorDark:"rgba(232, 86, 193, 0.5)",themeMode:"light",bindToWindow:!1,name:"markupCanvas",onTransformUpdate:()=>{}};function p(e){try{const t=e.container,n=e.config,r=e.transform||{scale:1,translateX:0,translateY:0},a=t.getBoundingClientRect(),i=a.width||t.clientWidth||0,l=a.height||t.clientHeight||0,c=h({container:t},n.rulerSize,e=>Math.max(0,i-e)),u=h({container:t},n.rulerSize,e=>Math.max(0,l-e)),d=n.width||f.width,m=n.height||f.height,g=function(e,t,n,r,a){const i=o(0,0,s(a.scale,a.translateX,a.translateY)),l=o(e,t,s(a.scale,a.translateX,a.translateY));return{x:Math.max(0,Math.min(n,i.x)),y:Math.max(0,Math.min(r,i.y)),width:Math.max(0,Math.min(n-i.x,l.x-i.x)),height:Math.max(0,Math.min(r-i.y,l.y-i.y))}}(c,u,d,m,r);return{width:c,height:u,contentWidth:d,contentHeight:m,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:g,scaledContentWidth:d*r.scale,scaledContentHeight:m*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+d*r.scale>c,canPanUp:r.translateY<0,canPanDown:r.translateY+m*r.scale>u,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(e){return console.error("Failed to calculate canvas bounds:",e),{width:0,height:0,contentWidth:0,contentHeight:0,scale:1,translateX:0,translateY:0,visibleArea:{x:0,y:0,width:0,height:0},scaledContentWidth:0,scaledContentHeight:0,canPanLeft:!1,canPanRight:!1,canPanUp:!1,canPanDown:!1,canZoomIn:!1,canZoomOut:!1}}}function v(e,t){if(!e?.style||!t)return!1;try{return e.style.transform=function(e){return`matrix3d(${e.m11}, ${e.m12}, ${e.m13}, ${e.m14}, ${e.m21}, ${e.m22}, ${e.m23}, ${e.m24}, ${e.m31}, ${e.m32}, ${e.m33}, ${e.m34}, ${e.m41}, ${e.m42}, ${e.m43}, ${e.m44})`}(t),!0}catch(e){return console.warn("Transform application failed:",e),!1}}function y(e,t){try{if(t.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(e,t,n){const r=c.get(e);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),c.delete(e)},t);c.set(e,o)}("disableTransition",1e3*(t.transitionDuration??.2),()=>{e.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(e){return console.error("Failed to disable transitions:",e),!0}}function b(e,t,n){!function(e,t){try{return!!t.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),e.style.transition=`transform ${t.transitionDuration}s linear`,!0)}catch(e){return console.error("Failed to enable transitions:",e),!1}}(e,t);try{return n()}finally{y(e,t)}}function w(t,n){if("static"===getComputedStyle(t).position&&(t.style.position="relative"),t.style.overflow="hidden",t.style.cursor="grab",t.style.overscrollBehavior="none",n){const e=g(n,"canvasBackgroundColor");t.style.backgroundColor=e}t.hasAttribute("tabindex")||t.setAttribute("tabindex","0"),function(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e);0===t.height&&"auto"===n.height&&console.error("MarkupCanvas: Container height is 0. Please set a height on your container element using CSS.","Examples: height: 100vh, height: 500px, or use flexbox/grid layout.",e),0===t.width&&"auto"===n.width&&console.error("MarkupCanvas: Container width is 0. Please set a width on your container element using CSS.","Examples: width: 100vw, width: 800px, or use flexbox/grid layout.",e)}(t),t.classList.contains(e)||t.classList.add(e)}function x(e,t){if(!e?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{w(e,t);const{transformLayer:n,contentLayer:a}=r(e,t);t.enableAcceleration&&function(e){try{return e.style.transform=e.style.transform||"translateZ(0)",e.style.backfaceVisibility="hidden",!0}catch(e){return console.error("Failed to enable hardware acceleration:",e),!1}}(n);const c=t.enableRulers?-t.rulerSize:0,d={scale:1,translateX:c,translateY:c};v(n,s(d.scale,d.translateX,d.translateY));return{container:e,transformLayer:n,contentLayer:a,config:t,transform:d,getBounds:function(){return p(this)},updateTransform:function(e){this.transform={...this.transform,...e};const t=s(this.transform.scale,this.transform.translateX,this.transform.translateY),n=v(this.transformLayer,t);return u(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return!0},setZoom:function(e){const t=l(this.config,t=>t(e));return this.updateTransform({scale:t})},canvasToContent:function(e,t){return o(e,t,s(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(e,t,n){return b(this.transformLayer,this.config,()=>{const r=i(e,t,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return b(this.transformLayer,this.config,()=>h(this,this.config.rulerSize,e=>{const t={scale:1,translateX:-1*e,translateY:-1*e};return this.updateTransform(t)}))},zoomToFitContent:function(){return b(this.transformLayer,this.config,()=>{const e=this.getBounds(),t=e.width/this.config.width,n=e.height/this.config.height,r=l(this.config,e=>e(.9*Math.min(t,n))),o=this.config.width*r,a=this.config.height*r,s=(e.width-o)/2,i=(e.height-a)/2;return this.updateTransform({scale:r,translateX:s,translateY:i})})}}}catch(e){return console.error("Failed to create canvas:",e),null}}function C(e={}){const t={...f,...e};return("number"!=typeof t.width||t.width<=0)&&(console.warn("Invalid width, using default"),t.width=f.width),("number"!=typeof t.height||t.height<=0)&&(console.warn("Invalid height, using default"),t.height=f.height),("number"!=typeof t.zoomSpeed||t.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),t.zoomSpeed=f.zoomSpeed),("number"!=typeof t.minZoom||t.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),t.minZoom=f.minZoom),("number"!=typeof t.maxZoom||t.maxZoom<=t.minZoom)&&(console.warn("Invalid maxZoom, using default"),t.maxZoom=f.maxZoom),("number"!=typeof t.keyboardPanStep||t.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),t.keyboardPanStep=f.keyboardPanStep),("number"!=typeof t.keyboardFastMultiplier||t.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),t.keyboardFastMultiplier=f.keyboardFastMultiplier),("number"!=typeof t.clickZoomLevel||t.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),t.clickZoomLevel=f.clickZoomLevel),("number"!=typeof t.rulerFontSize||t.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),t.rulerFontSize=f.rulerFontSize),("number"!=typeof t.rulerSize||t.rulerSize<=0)&&(console.warn("Invalid rulerSize, using default"),t.rulerSize=f.rulerSize),t}class k{constructor(){this.listeners=new Map}setEmitInterceptor(e){this.emitInterceptor=e}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,t){this.emitInterceptor?.(e,t);const n=this.listeners.get(e);n&&n.forEach(n=>{try{n(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(){this.listeners.clear()}}const T=300,S=5;function D(e,t){if(!e?.getBounds)return t;try{const n=e.getBounds(),r=n.width*n.height;return t*(r/2073600)**1}catch(e){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",e),t}}function z(e,t){let n=0,r=0;function o(o){const a=e.container.getBoundingClientRect(),s=o.clientX-a.left,i=o.clientY-a.top;!function(e,t,n,r,o){h(e,t,e=>o(n-e,r-e))}(e,t.rulerSize,s,i,(e,t)=>{n=e,r=t})}function s(o){if(!(o instanceof KeyboardEvent))return;if("canvas"===t.bindKeyboardEventsTo&&document.activeElement!==e.container)return;const s=o.shiftKey,l=t.keyboardPanStep*(s?t.keyboardFastMultiplier:1);let c=!1;const u={};switch(o.key){case"ArrowLeft":u.translateX=e.transform.translateX+l,c=!0;break;case"ArrowRight":u.translateX=e.transform.translateX-l,c=!0;break;case"ArrowUp":u.translateY=e.transform.translateY+l,c=!0;break;case"ArrowDown":u.translateY=e.transform.translateY-l,c=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1+n),t),c=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1-n),t),c=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/e.transform.scale,a=i(n,r,e.transform,o,t);Object.assign(u,a),c=!0}break;case"g":case"G":e.toggleGrid&&e.toggleGrid(),c=!0;break;case"r":case"R":o.metaKey||o.ctrlKey||o.altKey||!e.toggleRulers||(e.toggleRulers(),c=!0)}c&&(o.preventDefault(),Object.keys(u).length>0&&e.updateTransform(u))}const l="canvas"===t.bindKeyboardEventsTo?e.container:document;return l.addEventListener("keydown",s),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",s),e.container.removeEventListener("mousemove",o)}}function M(e,t,n,r,o){n?t.requireSpaceForMouseDrag?e.container.style.cursor=r?"grab":"default":e.container.style.cursor=o?"grabbing":"grab":e.container.style.cursor="default"}function L(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),M(e,t,n,r,!1)}function E(e,t,n,r,o,a,s,i,l,c){a&&e.button===s&&L(t,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===e.button&&n.enableClickToZoom&&i>0&&function(e,t,n,r,o,a){const s=Date.now()-r,i=e.altKey,l=!n.requireOptionForClickZoom||i;if(s<T&&!o&&!a&&l){e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{clickX:s,clickY:i}=m(t,o,a,n.rulerSize,(e,t)=>({clickX:e,clickY:t})),l=t.canvasToContent(s,i),c=r.width/2,u=r.height/2,d=n.clickZoomLevel,h={scale:d,translateX:c-l.x*d,translateY:u-l.y*d};b(t.transformLayer,t.config,()=>{t.updateTransform(h)})}}(e,t,n,i,l,a),0===e.button&&function(e){e.setMouseDownTime(0),e.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function R(e,t,n=!0){let r=!0,o=!1,a=0,s=0,i=-1,l=!1,c=0,u=0,h=0,m=!1;const g={setIsDragging:e=>{o=e},setDragButton:e=>{i=e},setIsSpacePressed:e=>{l=e},setMouseDownTime:e=>{c=e},setMouseDownX:e=>{u=e},setMouseDownY:e=>{h=e},setHasDragged:e=>{m=e},setLastMouseX:e=>{a=e},setLastMouseY:e=>{s=e}},f=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!0),M(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!1),M(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:a.setIsDragging,setDragButton:a.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed,setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})},v=n=>{!function(e,t,n,r,o,a){const s=0===e.button,i=1===e.button;if(s&&(a.setMouseDownTime(Date.now()),a.setMouseDownX(e.clientX),a.setMouseDownY(e.clientY),a.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(s&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(e.preventDefault(),a.setDragButton(e.button),a.setLastMouseX(e.clientX),a.setLastMouseY(e.clientY),M(t,n,r,o,!1))}(n,e,t,r,l,g)},y=t=>{!function(e,t,n,r,o,a,s,i,l,c){if(o>0){const t=Math.abs(e.clientX-a),o=Math.abs(e.clientY-s);(t>S||o>S)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;e.preventDefault(),d((...e)=>{const o=e[0];if(!r||!n)return;const a=o.clientX-i,s=o.clientY-l,u={translateX:t.transform.translateX+a,translateY:t.transform.translateY+s};t.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(e)}(t,e,r,o,c,u,h,a,s,{setHasDragged:g.setHasDragged,setIsDragging:g.setIsDragging,setLastMouseX:g.setLastMouseX,setLastMouseY:g.setLastMouseY})},b=n=>{E(n,e,t,r,l,o,i,c,m,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton,setMouseDownTime:g.setMouseDownTime,setHasDragged:g.setHasDragged})},w=()=>{!function(e,t,n,r,o,a){o&&L(e,t,n,r,a)}(e,t,r,l,o,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})};e.container.addEventListener("mousedown",v),document.addEventListener("mousemove",y),document.addEventListener("mouseup",b),e.container.addEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.addEventListener("keydown",f),document.addEventListener("keyup",p)),M(e,t,r,l,o);const x=()=>{e.container.removeEventListener("mousedown",v),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",b),e.container.removeEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",f),document.removeEventListener("keyup",p))};return n?{cleanup:x,enable:()=>(r=!0,M(e,t,r,l,o),!0),disable:()=>(r=!1,o&&L(e,t,r,l,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton}),M(e,t,r,l,o),!0),isEnabled:()=>r}:x}function Y(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}function X(e,t){const n=e.clientX-t.clientX,r=e.clientY-t.clientY;return Math.sqrt(n*n+r*r)}function B(e,t,n,r){const o=i(n,r,e.transform,t,e.config);return e.updateTransform(o)}function F(e,t,n){e.preventDefault();const r=Array.from(e.touches);d((...e)=>{const r=e[0];if(1===r.length){if(1===n.touches.length){const e=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,a={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(a)}}else if(2===r.length){const e=X(r[0],r[1]),o=Y(r[0],r[1]);if(n.lastDistance>0){const r=e/n.lastDistance,a=t.container.getBoundingClientRect();let s=o.x-a.left,i=o.y-a.top;const l=function(e,t,n,r){return h(e,t,e=>{const t={...n,x:n.x-e,y:n.y-e};return r(t)})}(t,t.config.rulerSize,{x:s,y:i},e=>e);s=l.x,i=l.y,B(t,r,s,i)}n.lastDistance=e,n.lastCenter=o}n.touches=r})(r)}function $(e){const t={touches:[],lastDistance:0,lastCenter:{}},n=e=>{!function(e,t){e.preventDefault(),t.touches=Array.from(e.touches),2===t.touches.length&&(t.lastDistance=X(t.touches[0],t.touches[1]),t.lastCenter=Y(t.touches[0],t.touches[1]))}(e,t)},r=n=>{F(n,e,t)},o=e=>{!function(e,t){t.touches=Array.from(e.touches),t.touches.length<2&&(t.lastDistance=0)}(e,t)};return e.container.addEventListener("touchstart",n,{passive:!1}),e.container.addEventListener("touchmove",r,{passive:!1}),e.container.addEventListener("touchend",o,{passive:!1}),()=>{e.container.removeEventListener("touchstart",n),e.container.removeEventListener("touchmove",r),e.container.removeEventListener("touchend",o)}}function P(e){const t=e.ctrlKey||e.metaKey,n=[0===e.deltaMode,Math.abs(e.deltaY)<50,e.deltaY%1!=0,Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!t,isTrackpadPinch:n&&t,isZoomGesture:t}}function I(e,t){const n=(e=>d((...t)=>{const n=t[0];if(!n||!e?.updateTransform)return!1;try{const t=e.transform,r=1,o=n.deltaX*r,a=n.deltaY*r,s={scale:t.scale,translateX:t.translateX-o,translateY:t.translateY-a};return y(e.transformLayer,e.config),e.updateTransform(s)}catch(e){return console.error("Error handling trackpad pan:",e),!1}}))(e),r=r=>P(r).isTrackpadScroll?n(r):function(e,t,n){if(!e||"number"!=typeof e.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!t?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{mouseX:s,mouseY:i}=m(t,o,a,n.rulerSize,(e,t)=>({mouseX:e,mouseY:t})),l=n.zoomSpeed,c=P(e);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?D(t,l):l;if(c.isTrackpadPinch){const e=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?D(t,e):e}return B(t,(e.deltaY<0?1:-1)>0?1+u:1/(1+u),s,i)}catch(e){return console.error("Error handling wheel event:",e),!1}}(r,e,t);return e.container.addEventListener("wheel",r,{passive:!1}),()=>{e.container.removeEventListener("wheel",r)}}const Z=100,O=1e3,_=1001,A=4,G=4,H=100,N=100,q=20,K=200;function V(e,t){const n=function(e){const t=document.createElement("div");return t.className="canvas-ruler horizontal-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: ${e.rulerSize}px;\n\tright: 0;\n\theight: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),r=function(e){const t=document.createElement("div");return t.className="canvas-ruler vertical-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: ${e.rulerSize}px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),o=function(e){const t=document.createElement("div");return t.className="canvas-ruler corner-box",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: ${e.rulerSize}px;\n\t\theight: ${e.rulerSize}px;\n\t\tbackground: var(--ruler-background-color);\n\t\tborder-right: 1px solid var(--ruler-border-color);\n\t\tborder-bottom: 1px solid var(--ruler-border-color);\n\t\tz-index: ${_};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${e.rulerFontFamily};\n\t\tfont-size: ${e.rulerFontSize-2}px;\n\t\tcolor: var(--ruler-text-color);\n\t\tpointer-events: none;\n\t`,t.textContent=e.rulerUnits,t}(t),a=t.enableGrid?function(e){const t=document.createElement("div");return t.className="canvas-ruler grid-overlay",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${e.rulerSize}px;\n\t\tleft: ${e.rulerSize}px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${Z};\n\t\tbackground-image: \n\t\t\tlinear-gradient(var(--grid-color) 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, var(--grid-color) 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,t}(t):void 0;return e.appendChild(n),e.appendChild(r),e.appendChild(o),a&&e.appendChild(a),{horizontalRuler:n,verticalRuler:r,cornerBox:o,gridOverlay:a}}function W(e,t){const n=e/Math.max(5,Math.min(20,t/50)),r=10**Math.floor(Math.log10(n)),o=n/r;let a;return a=o<=1?1:o<=2?2:o<=5?5:10,a*r}function U(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${A}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%H===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\tleft: ${n}px;\n\t\t\tbottom: ${A+2}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function j(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${G}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%H===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\ttop: ${n-6}px;\n\t\t\tright: ${G+6}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t\ttransform: rotate(-90deg);\n\t\t\ttransform-origin: right center;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function J(e,t,n,r,o){const a=e.getBounds(),s=a.scale||1,i=a.translateX||0,l=a.translateY||0,c=a.width-o.rulerSize,u=a.height-o.rulerSize,d=-i/s,h=-l/s,m=h+u/s;!function(e,t,n,r,o,a){const s=r,i=W(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&U(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(t,d,d+c/s,c,s,o),function(e,t,n,r,o,a){const s=r,i=W(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&j(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(n,h,m,u,s,o),r&&function(e,t,n,r){let o=N*t;for(;o<q;)o*=2;for(;o>K;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,s,i,l)}function Q(e,t){const n=g(t,"rulerBackgroundColor"),r=g(t,"rulerBorderColor"),o=g(t,"rulerTextColor"),a=g(t,"rulerTickColor"),s=g(t,"gridColor");e.horizontalRuler&&(e.horizontalRuler.style.setProperty("--ruler-background-color",n),e.horizontalRuler.style.setProperty("--ruler-border-color",r),e.horizontalRuler.style.setProperty("--ruler-text-color",o),e.horizontalRuler.style.setProperty("--ruler-tick-color",a)),e.verticalRuler&&(e.verticalRuler.style.setProperty("--ruler-background-color",n),e.verticalRuler.style.setProperty("--ruler-border-color",r),e.verticalRuler.style.setProperty("--ruler-text-color",o),e.verticalRuler.style.setProperty("--ruler-tick-color",a)),e.cornerBox&&(e.cornerBox.style.setProperty("--ruler-background-color",n),e.cornerBox.style.setProperty("--ruler-border-color",r),e.cornerBox.style.setProperty("--ruler-text-color",o)),e.gridOverlay&&e.gridOverlay.style.setProperty("--grid-color",s)}function ee(e,t){if(!e?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const a=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&J(e,n.horizontalRuler,n.verticalRuler,n.gridOverlay,t)};try{return n=V(e.container,t),r=function(e,t){const n=d(t),r=e.updateTransform;e.updateTransform=function(e){const t=r.call(this,e);return n(),t};const o=d(t);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),e.updateTransform=r,n.cleanup(),o.cleanup()}}(e,a),Q(n,t),a(),t.showRulers||(n.horizontalRuler.style.display="none",n.verticalRuler.style.display="none",n.cornerBox.style.display="none"),!t.showGrid&&n.gridOverlay&&(n.gridOverlay.style.display="none"),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:a,updateTheme:e=>{o||Q(n,e)},show:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="block"),n.verticalRuler&&(n.verticalRuler.style.display="block"),n.cornerBox&&(n.cornerBox.style.display="flex"),n.gridOverlay&&(n.gridOverlay.style.display="block")},hide:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="none"),n.verticalRuler&&(n.verticalRuler.style.display="none"),n.cornerBox&&(n.cornerBox.style.display="none"),n.gridOverlay&&(n.gridOverlay.style.display="none")},toggleGrid:()=>{if(n.gridOverlay){const e="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=e?"none":"block"}},destroy:()=>{o=!0,r&&r(),n.horizontalRuler?.parentNode&&n.horizontalRuler.parentNode.removeChild(n.horizontalRuler),n.verticalRuler?.parentNode&&n.verticalRuler.parentNode.removeChild(n.verticalRuler),n.cornerBox?.parentNode&&n.cornerBox.parentNode.removeChild(n.cornerBox),n.gridOverlay?.parentNode&&n.gridOverlay.parentNode.removeChild(n.gridOverlay)}}}catch(e){return console.error("Failed to create rulers:",e),null}}return class{constructor(e,t={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new k,!e)throw new Error("Container element is required");this.config=C(t);const n=x(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(console.log("bindToWindow:","true"),this.listen.setEmitInterceptor((e,t)=>{this.broadcastEvent(e,t)}),this.setupGlobalBinding()),this.setupEventHandlers(),this._isReady=!0,this.listen.emit("ready",this)}setupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;console.log(t),t[e]=this,console.log(t[e]),t.__markupCanvasInstances||(t.__markupCanvasInstances=new Map),t.__markupCanvasInstances.set(e,this)}cleanupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;delete t[e],t.__markupCanvasInstances&&t.__markupCanvasInstances.delete(e)}broadcastEvent(e,t){if("undefined"==typeof window)return;let n=t;"ready"===e&&(n={ready:!0}),window.postMessage({source:"markup-canvas",event:e,data:n,timestamp:Date.now(),canvasName:this.config.name},"*")}setupEventHandlers(){try{u(this.config,"enableZoom",()=>{const e=I(this,this.config);this.cleanupFunctions.push(e)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=R(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),u(this.config,"enableKeyboard",()=>{const e=z(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=$(this);this.cleanupFunctions.push(e)}),u(this.config,"enableRulers",()=>{this.rulers=ee(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(e){throw console.error("Failed to set up event handlers:",e),this.cleanup(),e}}get container(){return this.baseCanvas.container}get transformLayer(){return this.baseCanvas.transformLayer}get contentLayer(){return this.baseCanvas.contentLayer}get transform(){return this.baseCanvas.transform}get isReady(){return this._isReady}get isTransforming(){return this.dragSetup?.isEnabled()||!1}get visibleBounds(){return this.getVisibleArea()}getBounds(){return this.baseCanvas.getBounds()}updateTransform(e){const t=this.baseCanvas.updateTransform(e);return t&&this.emitTransformEvents(),t}emitTransformEvents(){const e=this.baseCanvas.transform;this.listen.emit("transform",e),this.listen.emit("zoom",e.scale),this.listen.emit("pan",{x:e.translateX,y:e.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(e){return this.baseCanvas.setZoom(e)}canvasToContent(e,t){return this.baseCanvas.canvasToContent(e,t)}zoomToPoint(e,t,n){return b(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(e,t,n);return r&&this.emitTransformEvents(),r})}resetView(){return b(this.transformLayer,this.config,()=>{const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e})}zoomToFitContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}panLeft(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+t};return this.updateTransform(n)}panRight(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-t};return this.updateTransform(n)}panUp(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+t};return this.updateTransform(n)}panDown(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-t};return this.updateTransform(n)}zoomIn(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1+e))};return this.updateTransform(n)}))}zoomOut(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1-e))};return this.updateTransform(n)}))}resetZoom(){return this.resetView()}enableMouseDrag(){return this.dragSetup?.enable()??!1}disableMouseDrag(){return this.dragSetup?.disable()??!1}isMouseDragEnabled(){return this.dragSetup?.isEnabled()??!1}toggleGrid(){return!!this.rulers?.toggleGrid&&(this.rulers.toggleGrid(),!0)}showGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="block",!0)}hideGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="none",!0)}isGridVisible(){return!!this.rulers?.gridOverlay&&"none"!==this.rulers.gridOverlay.style.display}toggleRulers(){if(this.rulers){return this.areRulersVisible()?this.rulers.hide():this.rulers.show(),!0}return!1}showRulers(){return!!this.rulers&&(this.rulers.show(),!0)}hideRulers(){return!!this.rulers&&(this.rulers.hide(),!0)}areRulersVisible(){return!!this.rulers?.horizontalRuler&&"none"!==this.rulers.horizontalRuler.style.display}centerContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.getBounds(),t=(e.width-e.contentWidth*this.baseCanvas.transform.scale)/2,n=(e.height-e.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:t,translateY:n})})}fitToScreen(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(e,t){const n=this.getVisibleArea();return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}scrollToPoint(e,t){return b(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,a=r-e*this.baseCanvas.transform.scale,s=o-t*this.baseCanvas.transform.scale;return this.updateTransform({translateX:a,translateY:s})})}getConfig(){return{...this.config}}updateConfig(e){this.config=C({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=C(t);const n=g(this.config,"canvasBackgroundColor");this.baseCanvas.container.style.backgroundColor=n,this.rulers&&this.rulers.updateTheme(this.config)}cleanup(){this.cleanupGlobalBinding(),this.cleanupFunctions.forEach(e=>{try{e()}catch(e){console.warn("Error during cleanup:",e)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(e,t){this.listen.on(e,t)}off(e,t){this.listen.off(e,t)}emit(e,t){this.listen.emit(e,t)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas=t()}(this,function(){"use strict";const e="canvas-container",t="transform-layer",n="content-layer";function r(e,r){const o=Array.from(e.children);let s=e.querySelector(`.${t}`);s||(s=document.createElement("div"),s.className=t,e.appendChild(s)),function(e,t){e.style.position="absolute";const n=t.rulerSize;e.style.top=`${n}px`,e.style.left=`${n}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`,e.style.transformOrigin="0 0"}(s,r);let a=s.querySelector(`.${n}`);return a||(a=document.createElement("div"),a.className=n,s.appendChild(a),function(e,n,r){e.forEach(e=>{e===r||e.classList.contains(t)||n.appendChild(e)})}(o,a,s)),function(e){e.style.position="relative",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}(a),{transformLayer:s,contentLayer:a}}function o(e,t,n){if(!n?.inverse)return{x:e,y:t};try{const r=n.inverse(),o=new DOMPoint(e,t).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:e,y:t}}}function s(e,t){return Math.max(t.minZoom,Math.min(t.maxZoom,e))}function a(e,t,n){return new DOMMatrix([e,0,0,e,t,n])}function i(e,t,n,r,o){const a=o.enableRulers?-o.rulerSize:0,i=n||{scale:1,translateX:a,translateY:a},{scale:l,translateX:c,translateY:u}=i,d=s(l*r,o);if(Math.abs(d-l)<.001)return{scale:l,translateX:c,translateY:u};return{scale:d,translateX:e-(e-c)/l*d,translateY:t-(t-u)/l*d}}function l(e,t){return t(t=>s(t,e))}const c=new Map;function u(e,t,n){return e[t]?n():null}function d(e){let t=null,n=null;const r=(...r)=>{n=r,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},r}function h(e,t,n){return n(null!==e.container.querySelector(".canvas-ruler")?t:0)}function m(e,t,n,r,o){const s=null!==e.container.querySelector(".canvas-ruler");return o(s?t-r:t,s?n-r:n)}function f(e,t){if("dark"===e.themeMode){return e[`${t}Dark`]}return e[t]}const g={width:8e3,height:8e3,enableAcceleration:!0,bindToWindow:!1,name:"markupCanvas",enablePostMessageAPI:!1,enableZoom:!0,enablePan:!0,enableTouch:!0,enableKeyboard:!0,bindKeyboardEventsTo:"canvas",zoomSpeed:1.5,minZoom:.05,maxZoom:80,enableTransition:!0,transitionDuration:.2,enableAdaptiveSpeed:!0,enableLeftDrag:!0,enableMiddleDrag:!0,requireSpaceForMouseDrag:!1,keyboardPanStep:50,keyboardFastMultiplier:20,keyboardZoomStep:.2,enableClickToZoom:!0,clickZoomLevel:1,requireOptionForClickZoom:!1,enableRulers:!0,enableGrid:!1,showRulers:!0,showGrid:!1,rulerFontSize:9,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",rulerSize:20,canvasBackgroundColor:"rgba(250, 250, 250, 1)",canvasBackgroundColorDark:"rgba(40, 40, 40, 1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"rgba(240, 240, 240, 1)",rulerTextColor:"rgba(102, 102, 102, 1)",rulerTickColor:"rgba(204, 204, 204, 1)",gridColor:"rgba(232, 86, 193, 0.5)",rulerBackgroundColorDark:"rgba(30, 30, 30, 0.95)",rulerBorderColorDark:"rgba(68, 68, 68, 1)",rulerTextColorDark:"rgba(170, 170, 170, 1)",rulerTickColorDark:"rgba(104, 104, 104, 1)",gridColorDark:"rgba(232, 86, 193, 0.5)",themeMode:"light",onTransformUpdate:()=>{}};function p(e){try{const t=e.container,n=e.config,r=e.transform||{scale:1,translateX:0,translateY:0},s=t.getBoundingClientRect(),i=s.width||t.clientWidth||0,l=s.height||t.clientHeight||0,c=h({container:t},n.rulerSize,e=>Math.max(0,i-e)),u=h({container:t},n.rulerSize,e=>Math.max(0,l-e)),d=n.width||g.width,m=n.height||g.height,f=function(e,t,n,r,s){const i=o(0,0,a(s.scale,s.translateX,s.translateY)),l=o(e,t,a(s.scale,s.translateX,s.translateY));return{x:Math.max(0,Math.min(n,i.x)),y:Math.max(0,Math.min(r,i.y)),width:Math.max(0,Math.min(n-i.x,l.x-i.x)),height:Math.max(0,Math.min(r-i.y,l.y-i.y))}}(c,u,d,m,r);return{width:c,height:u,contentWidth:d,contentHeight:m,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:f,scaledContentWidth:d*r.scale,scaledContentHeight:m*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+d*r.scale>c,canPanUp:r.translateY<0,canPanDown:r.translateY+m*r.scale>u,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(e){return console.error("Failed to calculate canvas bounds:",e),{width:0,height:0,contentWidth:0,contentHeight:0,scale:1,translateX:0,translateY:0,visibleArea:{x:0,y:0,width:0,height:0},scaledContentWidth:0,scaledContentHeight:0,canPanLeft:!1,canPanRight:!1,canPanUp:!1,canPanDown:!1,canZoomIn:!1,canZoomOut:!1}}}function v(e,t){if(!e?.style||!t)return!1;try{return e.style.transform=function(e){return`matrix3d(${e.m11}, ${e.m12}, ${e.m13}, ${e.m14}, ${e.m21}, ${e.m22}, ${e.m23}, ${e.m24}, ${e.m31}, ${e.m32}, ${e.m33}, ${e.m34}, ${e.m41}, ${e.m42}, ${e.m43}, ${e.m44})`}(t),!0}catch(e){return console.warn("Transform application failed:",e),!1}}function y(e,t){try{if(t.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(e,t,n){const r=c.get(e);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),c.delete(e)},t);c.set(e,o)}("disableTransition",1e3*(t.transitionDuration??.2),()=>{e.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(e){return console.error("Failed to disable transitions:",e),!0}}function b(e,t,n){!function(e,t){try{return!!t.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),e.style.transition=`transform ${t.transitionDuration}s linear`,!0)}catch(e){return console.error("Failed to enable transitions:",e),!1}}(e,t);try{return n()}finally{y(e,t)}}function w(t,n){if("static"===getComputedStyle(t).position&&(t.style.position="relative"),t.style.overflow="hidden",t.style.cursor="grab",t.style.overscrollBehavior="none",n){const e=f(n,"canvasBackgroundColor");t.style.backgroundColor=e}t.hasAttribute("tabindex")||t.setAttribute("tabindex","0"),function(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e);0===t.height&&"auto"===n.height&&console.error("MarkupCanvas: Container height is 0. Please set a height on your container element using CSS.","Examples: height: 100vh, height: 500px, or use flexbox/grid layout.",e),0===t.width&&"auto"===n.width&&console.error("MarkupCanvas: Container width is 0. Please set a width on your container element using CSS.","Examples: width: 100vw, width: 800px, or use flexbox/grid layout.",e)}(t),t.classList.contains(e)||t.classList.add(e)}function C(e,t){if(!e?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{w(e,t);const{transformLayer:n,contentLayer:s}=r(e,t);t.enableAcceleration&&function(e){try{return e.style.transform=e.style.transform||"translateZ(0)",e.style.backfaceVisibility="hidden",!0}catch(e){return console.error("Failed to enable hardware acceleration:",e),!1}}(n);const c=t.enableRulers?-t.rulerSize:0,d={scale:1,translateX:c,translateY:c};v(n,a(d.scale,d.translateX,d.translateY));return{container:e,transformLayer:n,contentLayer:s,config:t,transform:d,getBounds:function(){return p(this)},updateTransform:function(e){this.transform={...this.transform,...e};const t=a(this.transform.scale,this.transform.translateX,this.transform.translateY),n=v(this.transformLayer,t);return u(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return!0},setZoom:function(e){const t=l(this.config,t=>t(e));return this.updateTransform({scale:t})},canvasToContent:function(e,t){return o(e,t,a(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(e,t,n){return b(this.transformLayer,this.config,()=>{const r=i(e,t,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return b(this.transformLayer,this.config,()=>h(this,this.config.rulerSize,e=>{const t={scale:1,translateX:-1*e,translateY:-1*e};return this.updateTransform(t)}))},zoomToFitContent:function(){return b(this.transformLayer,this.config,()=>{const e=this.getBounds(),t=e.width/this.config.width,n=e.height/this.config.height,r=l(this.config,e=>e(.9*Math.min(t,n))),o=this.config.width*r,s=this.config.height*r,a=(e.width-o)/2,i=(e.height-s)/2;return this.updateTransform({scale:r,translateX:a,translateY:i})})}}}catch(e){return console.error("Failed to create canvas:",e),null}}function x(e={}){const t={...g,...e};return("number"!=typeof t.width||t.width<=0)&&(console.warn("Invalid width, using default"),t.width=g.width),("number"!=typeof t.height||t.height<=0)&&(console.warn("Invalid height, using default"),t.height=g.height),("number"!=typeof t.zoomSpeed||t.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),t.zoomSpeed=g.zoomSpeed),("number"!=typeof t.minZoom||t.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),t.minZoom=g.minZoom),("number"!=typeof t.maxZoom||t.maxZoom<=t.minZoom)&&(console.warn("Invalid maxZoom, using default"),t.maxZoom=g.maxZoom),("number"!=typeof t.keyboardPanStep||t.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),t.keyboardPanStep=g.keyboardPanStep),("number"!=typeof t.keyboardFastMultiplier||t.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),t.keyboardFastMultiplier=g.keyboardFastMultiplier),("number"!=typeof t.clickZoomLevel||t.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),t.clickZoomLevel=g.clickZoomLevel),("number"!=typeof t.rulerFontSize||t.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),t.rulerFontSize=g.rulerFontSize),("number"!=typeof t.rulerSize||t.rulerSize<=0)&&(console.warn("Invalid rulerSize, using default"),t.rulerSize=g.rulerSize),t}class k{constructor(){this.listeners=new Map}setEmitInterceptor(e){this.emitInterceptor=e}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,t){this.emitInterceptor?.(e,t);const n=this.listeners.get(e);n&&n.forEach(n=>{try{n(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(){this.listeners.clear()}}const T=300,S=5;function D(e,t){if(!e?.getBounds)return t;try{const n=e.getBounds(),r=n.width*n.height;return t*(r/2073600)**1}catch(e){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",e),t}}function M(e,t){let n=0,r=0;function o(o){const s=e.container.getBoundingClientRect(),a=o.clientX-s.left,i=o.clientY-s.top;!function(e,t,n,r,o){h(e,t,e=>o(n-e,r-e))}(e,t.rulerSize,a,i,(e,t)=>{n=e,r=t})}function a(o){if(!(o instanceof KeyboardEvent))return;if("canvas"===t.bindKeyboardEventsTo&&document.activeElement!==e.container)return;const a=o.shiftKey,l=t.keyboardPanStep*(a?t.keyboardFastMultiplier:1);let c=!1;const u={};switch(o.key){case"ArrowLeft":u.translateX=e.transform.translateX+l,c=!0;break;case"ArrowRight":u.translateX=e.transform.translateX-l,c=!0;break;case"ArrowUp":u.translateY=e.transform.translateY+l,c=!0;break;case"ArrowDown":u.translateY=e.transform.translateY-l,c=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=s(e.transform.scale*(1+n),t),c=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=s(e.transform.scale*(1-n),t),c=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/e.transform.scale,s=i(n,r,e.transform,o,t);Object.assign(u,s),c=!0}break;case"g":case"G":o.shiftKey&&e.toggleGrid&&e.toggleGrid(),c=o.shiftKey;break;case"r":case"R":!o.shiftKey||o.metaKey||o.ctrlKey||o.altKey||!e.toggleRulers||(e.toggleRulers(),c=!0)}c&&(o.preventDefault(),Object.keys(u).length>0&&e.updateTransform(u))}const l="canvas"===t.bindKeyboardEventsTo?e.container:document;return l.addEventListener("keydown",a),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",a),e.container.removeEventListener("mousemove",o)}}function z(e,t,n,r,o){n?t.requireSpaceForMouseDrag?e.container.style.cursor=r?"grab":"default":e.container.style.cursor=o?"grabbing":"grab":e.container.style.cursor="default"}function L(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),z(e,t,n,r,!1)}function R(e,t,n,r,o,s,a,i,l,c){s&&e.button===a&&L(t,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===e.button&&n.enableClickToZoom&&i>0&&function(e,t,n,r,o,s){const a=Date.now()-r,i=e.altKey,l=!n.requireOptionForClickZoom||i;if(a<T&&!o&&!s&&l){e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,s=e.clientY-r.top,{clickX:a,clickY:i}=m(t,o,s,n.rulerSize,(e,t)=>({clickX:e,clickY:t})),l=t.canvasToContent(a,i),c=r.width/2,u=r.height/2,d=n.clickZoomLevel,h={scale:d,translateX:c-l.x*d,translateY:u-l.y*d};b(t.transformLayer,t.config,()=>{t.updateTransform(h)})}}(e,t,n,i,l,s),0===e.button&&function(e){e.setMouseDownTime(0),e.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function E(e,t,n=!0){let r=!0,o=!1,s=0,a=0,i=-1,l=!1,c=0,u=0,h=0,m=!1;const f={setIsDragging:e=>{o=e},setDragButton:e=>{i=e},setIsSpacePressed:e=>{l=e},setMouseDownTime:e=>{c=e},setMouseDownX:e=>{u=e},setMouseDownY:e=>{h=e},setHasDragged:e=>{m=e},setLastMouseX:e=>{s=e},setLastMouseY:e=>{a=e}},g=n=>{!function(e,t,n,r,o,s){n.requireSpaceForMouseDrag&&" "===e.key&&(s.setIsSpacePressed(!0),z(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:f.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,s){n.requireSpaceForMouseDrag&&" "===e.key&&(s.setIsSpacePressed(!1),z(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:s.setIsDragging,setDragButton:s.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:f.setIsSpacePressed,setIsDragging:f.setIsDragging,setDragButton:f.setDragButton})},v=n=>{!function(e,t,n,r,o,s){const a=0===e.button,i=1===e.button;if(a&&(s.setMouseDownTime(Date.now()),s.setMouseDownX(e.clientX),s.setMouseDownY(e.clientY),s.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(a&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(e.preventDefault(),s.setDragButton(e.button),s.setLastMouseX(e.clientX),s.setLastMouseY(e.clientY),z(t,n,r,o,!1))}(n,e,t,r,l,f)},y=t=>{!function(e,t,n,r,o,s,a,i,l,c){if(o>0){const t=Math.abs(e.clientX-s),o=Math.abs(e.clientY-a);(t>S||o>S)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;e.preventDefault(),d((...e)=>{const o=e[0];if(!r||!n)return;const s=o.clientX-i,a=o.clientY-l,u={translateX:t.transform.translateX+s,translateY:t.transform.translateY+a};t.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(e)}(t,e,r,o,c,u,h,s,a,{setHasDragged:f.setHasDragged,setIsDragging:f.setIsDragging,setLastMouseX:f.setLastMouseX,setLastMouseY:f.setLastMouseY})},b=n=>{R(n,e,t,r,l,o,i,c,m,{setIsDragging:f.setIsDragging,setDragButton:f.setDragButton,setMouseDownTime:f.setMouseDownTime,setHasDragged:f.setHasDragged})},w=()=>{!function(e,t,n,r,o,s){o&&L(e,t,n,r,s)}(e,t,r,l,o,{setIsDragging:f.setIsDragging,setDragButton:f.setDragButton})};e.container.addEventListener("mousedown",v),document.addEventListener("mousemove",y),document.addEventListener("mouseup",b),e.container.addEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.addEventListener("keydown",g),document.addEventListener("keyup",p)),z(e,t,r,l,o);const C=()=>{e.container.removeEventListener("mousedown",v),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",b),e.container.removeEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",g),document.removeEventListener("keyup",p))};return n?{cleanup:C,enable:()=>(r=!0,z(e,t,r,l,o),!0),disable:()=>(r=!1,o&&L(e,t,r,l,{setIsDragging:f.setIsDragging,setDragButton:f.setDragButton}),z(e,t,r,l,o),!0),isEnabled:()=>r}:C}function Y(e){const t=t=>{const n=t.data;if("markup-canvas"!==n.source)return;const r=e.config.name||"markupCanvas";if(n.canvasName!==r)return;const o=n.action,s=n.args||[];try{if("zoomIn"===o)e.zoomIn(s[0]);else if("zoomOut"===o)e.zoomOut(s[0]);else if("setZoom"===o){const t=s[0];if("number"!=typeof t||t<=0)throw new Error(`Invalid zoom level: ${t}. Must be a positive number.`);e.setZoom(t)}else if("resetZoom"===o)e.resetZoom();else if("panLeft"===o)e.panLeft(s[0]);else if("panRight"===o)e.panRight(s[0]);else if("panUp"===o)e.panUp(s[0]);else if("panDown"===o)e.panDown(s[0]);else if("fitToScreen"===o)e.fitToScreen();else if("centerContent"===o)e.centerContent();else if("scrollToPoint"===o)e.scrollToPoint(s[0],s[1]);else if("resetView"===o)e.resetView();else if("toggleRulers"===o)e.toggleRulers();else if("showRulers"===o)e.showRulers();else if("hideRulers"===o)e.hideRulers();else if("toggleGrid"===o)e.toggleGrid();else if("showGrid"===o)e.showGrid();else if("hideGrid"===o)e.hideGrid();else if("updateThemeMode"===o){const t=s[0];if("light"!==t&&"dark"!==t)throw new Error(`Invalid theme mode: ${t}`);e.updateThemeMode(t)}else{if("toggleThemeMode"!==o)throw new Error(`Unknown action: ${o}`);{const t="light"===e.getConfig().themeMode?"dark":"light";e.updateThemeMode(t)}}}catch(e){!function(e,t,n){window.postMessage({source:"markup-canvas-error",canvasName:e,action:t,error:n,timestamp:Date.now()},"*")}(r,o,e instanceof Error?e.message:String(e))}};return"undefined"!=typeof window&&window.addEventListener("message",t),()=>{"undefined"!=typeof window&&window.removeEventListener("message",t)}}function X(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}function B(e,t){const n=e.clientX-t.clientX,r=e.clientY-t.clientY;return Math.sqrt(n*n+r*r)}function F(e,t,n,r){const o=i(n,r,e.transform,t,e.config);return e.updateTransform(o)}function $(e,t,n){e.preventDefault();const r=Array.from(e.touches);d((...e)=>{const r=e[0];if(1===r.length){if(1===n.touches.length){const e=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,s={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(s)}}else if(2===r.length){const e=B(r[0],r[1]),o=X(r[0],r[1]);if(n.lastDistance>0){const r=e/n.lastDistance,s=t.container.getBoundingClientRect();let a=o.x-s.left,i=o.y-s.top;const l=function(e,t,n,r){return h(e,t,e=>{const t={...n,x:n.x-e,y:n.y-e};return r(t)})}(t,t.config.rulerSize,{x:a,y:i},e=>e);a=l.x,i=l.y,F(t,r,a,i)}n.lastDistance=e,n.lastCenter=o}n.touches=r})(r)}function P(e){const t={touches:[],lastDistance:0,lastCenter:{}},n=e=>{!function(e,t){e.preventDefault(),t.touches=Array.from(e.touches),2===t.touches.length&&(t.lastDistance=B(t.touches[0],t.touches[1]),t.lastCenter=X(t.touches[0],t.touches[1]))}(e,t)},r=n=>{$(n,e,t)},o=e=>{!function(e,t){t.touches=Array.from(e.touches),t.touches.length<2&&(t.lastDistance=0)}(e,t)};return e.container.addEventListener("touchstart",n,{passive:!1}),e.container.addEventListener("touchmove",r,{passive:!1}),e.container.addEventListener("touchend",o,{passive:!1}),()=>{e.container.removeEventListener("touchstart",n),e.container.removeEventListener("touchmove",r),e.container.removeEventListener("touchend",o)}}function I(e){const t=e.ctrlKey||e.metaKey,n=[0===e.deltaMode,Math.abs(e.deltaY)<50,e.deltaY%1!=0,Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!t,isTrackpadPinch:n&&t,isZoomGesture:t}}function Z(e,t){const n=(e=>d((...t)=>{const n=t[0];if(!n||!e?.updateTransform)return!1;try{const t=e.transform,r=1,o=n.deltaX*r,s=n.deltaY*r,a={scale:t.scale,translateX:t.translateX-o,translateY:t.translateY-s};return y(e.transformLayer,e.config),e.updateTransform(a)}catch(e){return console.error("Error handling trackpad pan:",e),!1}}))(e),r=r=>I(r).isTrackpadScroll?n(r):function(e,t,n){if(!e||"number"!=typeof e.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!t?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,s=e.clientY-r.top,{mouseX:a,mouseY:i}=m(t,o,s,n.rulerSize,(e,t)=>({mouseX:e,mouseY:t})),l=n.zoomSpeed,c=I(e);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?D(t,l):l;if(c.isTrackpadPinch){const e=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?D(t,e):e}return F(t,(e.deltaY<0?1:-1)>0?1+u:1/(1+u),a,i)}catch(e){return console.error("Error handling wheel event:",e),!1}}(r,e,t);return e.container.addEventListener("wheel",r,{passive:!1}),()=>{e.container.removeEventListener("wheel",r)}}const O=100,A=1e3,_=1001,G=4,H=4,N=100,K=100,q=20,V=200;function U(e,t){const n=function(e){const t=document.createElement("div");return t.className="canvas-ruler horizontal-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: ${e.rulerSize}px;\n\tright: 0;\n\theight: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tz-index: ${A};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),r=function(e){const t=document.createElement("div");return t.className="canvas-ruler vertical-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: ${e.rulerSize}px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tz-index: ${A};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),o=function(e){const t=document.createElement("div");return t.className="canvas-ruler corner-box",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: ${e.rulerSize}px;\n\t\theight: ${e.rulerSize}px;\n\t\tbackground: var(--ruler-background-color);\n\t\tborder-right: 1px solid var(--ruler-border-color);\n\t\tborder-bottom: 1px solid var(--ruler-border-color);\n\t\tz-index: ${_};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${e.rulerFontFamily};\n\t\tfont-size: ${e.rulerFontSize-2}px;\n\t\tcolor: var(--ruler-text-color);\n\t\tpointer-events: none;\n\t`,t.textContent=e.rulerUnits,t}(t),s=t.enableGrid?function(e){const t=document.createElement("div");return t.className="canvas-ruler grid-overlay",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${e.rulerSize}px;\n\t\tleft: ${e.rulerSize}px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${O};\n\t\tbackground-image: \n\t\t\tlinear-gradient(var(--grid-color) 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, var(--grid-color) 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,t}(t):void 0;return e.appendChild(n),e.appendChild(r),e.appendChild(o),s&&e.appendChild(s),{horizontalRuler:n,verticalRuler:r,cornerBox:o,gridOverlay:s}}function W(e,t){const n=e/Math.max(5,Math.min(20,t/50)),r=10**Math.floor(Math.log10(n)),o=n/r;let s;return s=o<=1?1:o<=2?2:o<=5?5:10,s*r}function j(e,t,n,r,o){const s=document.createElement("div");s.className="tick",s.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${G}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(s);if(t%N===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\tleft: ${n}px;\n\t\t\tbottom: ${G+2}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function J(e,t,n,r,o){const s=document.createElement("div");s.className="tick",s.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${H}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(s);if(t%N===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\ttop: ${n-6}px;\n\t\t\tright: ${H+6}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t\ttransform: rotate(-90deg);\n\t\t\ttransform-origin: right center;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function Q(e,t,n,r,o){const s=e.getBounds(),a=s.scale||1,i=s.translateX||0,l=s.translateY||0,c=s.width-o.rulerSize,u=s.height-o.rulerSize,d=-i/a,h=-l/a,m=h+u/a;!function(e,t,n,r,o,s){const a=r,i=W(n-t,a),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=a+50&&j(l,e,n,0,s)}e.innerHTML="",e.appendChild(l)}(t,d,d+c/a,c,a,o),function(e,t,n,r,o,s){const a=r,i=W(n-t,a),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=a+50&&J(l,e,n,0,s)}e.innerHTML="",e.appendChild(l)}(n,h,m,u,a,o),r&&function(e,t,n,r){let o=K*t;for(;o<q;)o*=2;for(;o>V;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,a,i,l)}function ee(e,t){const n=f(t,"rulerBackgroundColor"),r=f(t,"rulerBorderColor"),o=f(t,"rulerTextColor"),s=f(t,"rulerTickColor"),a=f(t,"gridColor");e.horizontalRuler&&(e.horizontalRuler.style.setProperty("--ruler-background-color",n),e.horizontalRuler.style.setProperty("--ruler-border-color",r),e.horizontalRuler.style.setProperty("--ruler-text-color",o),e.horizontalRuler.style.setProperty("--ruler-tick-color",s)),e.verticalRuler&&(e.verticalRuler.style.setProperty("--ruler-background-color",n),e.verticalRuler.style.setProperty("--ruler-border-color",r),e.verticalRuler.style.setProperty("--ruler-text-color",o),e.verticalRuler.style.setProperty("--ruler-tick-color",s)),e.cornerBox&&(e.cornerBox.style.setProperty("--ruler-background-color",n),e.cornerBox.style.setProperty("--ruler-border-color",r),e.cornerBox.style.setProperty("--ruler-text-color",o)),e.gridOverlay&&e.gridOverlay.style.setProperty("--grid-color",a)}function te(e,t){if(!e?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const s=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&Q(e,n.horizontalRuler,n.verticalRuler,n.gridOverlay,t)};try{return n=U(e.container,t),r=function(e,t){const n=d(t),r=e.updateTransform;e.updateTransform=function(e){const t=r.call(this,e);return n(),t};const o=d(t);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),e.updateTransform=r,n.cleanup(),o.cleanup()}}(e,s),ee(n,t),s(),t.showRulers||(n.horizontalRuler.style.display="none",n.verticalRuler.style.display="none",n.cornerBox.style.display="none"),!t.showGrid&&n.gridOverlay&&(n.gridOverlay.style.display="none"),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:s,updateTheme:e=>{o||ee(n,e)},show:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="block"),n.verticalRuler&&(n.verticalRuler.style.display="block"),n.cornerBox&&(n.cornerBox.style.display="flex"),n.gridOverlay&&(n.gridOverlay.style.display="block")},hide:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="none"),n.verticalRuler&&(n.verticalRuler.style.display="none"),n.cornerBox&&(n.cornerBox.style.display="none"),n.gridOverlay&&(n.gridOverlay.style.display="none")},toggleGrid:()=>{if(n.gridOverlay){const e="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=e?"none":"block"}},destroy:()=>{o=!0,r&&r(),n.horizontalRuler?.parentNode&&n.horizontalRuler.parentNode.removeChild(n.horizontalRuler),n.verticalRuler?.parentNode&&n.verticalRuler.parentNode.removeChild(n.verticalRuler),n.cornerBox?.parentNode&&n.cornerBox.parentNode.removeChild(n.cornerBox),n.gridOverlay?.parentNode&&n.gridOverlay.parentNode.removeChild(n.gridOverlay)}}}catch(e){return console.error("Failed to create rulers:",e),null}}return class{constructor(e,t={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new k,this.postMessageCleanup=null,!e)throw new Error("Container element is required");this.config=x(t);const n=C(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(console.log("bindToWindow:","true"),this.listen.setEmitInterceptor((e,t)=>{this.broadcastEvent(e,t)}),this.setupGlobalBinding(),this.config.enablePostMessageAPI&&(this.postMessageCleanup=Y(this))),this.setupEventHandlers(),this._isReady=!0,this.listen.emit("ready",this)}setupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;console.log(t),t[e]=this,console.log(t[e]),t.__markupCanvasInstances||(t.__markupCanvasInstances=new Map),t.__markupCanvasInstances.set(e,this)}cleanupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;delete t[e],t.__markupCanvasInstances&&t.__markupCanvasInstances.delete(e)}broadcastEvent(e,t){if("undefined"==typeof window)return;let n=t;"ready"===e&&(n={ready:!0}),window.postMessage({source:"markup-canvas",event:e,data:n,timestamp:Date.now(),canvasName:this.config.name},"*")}setupEventHandlers(){try{u(this.config,"enableZoom",()=>{const e=Z(this,this.config);this.cleanupFunctions.push(e)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=E(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),u(this.config,"enableKeyboard",()=>{const e=M(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=P(this);this.cleanupFunctions.push(e)}),u(this.config,"enableRulers",()=>{this.rulers=te(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(e){throw console.error("Failed to set up event handlers:",e),this.cleanup(),e}}get container(){return this.baseCanvas.container}get transformLayer(){return this.baseCanvas.transformLayer}get contentLayer(){return this.baseCanvas.contentLayer}get transform(){return this.baseCanvas.transform}get isReady(){return this._isReady}get isTransforming(){return this.dragSetup?.isEnabled()||!1}get visibleBounds(){return this.getVisibleArea()}getBounds(){return this.baseCanvas.getBounds()}updateTransform(e){const t=this.baseCanvas.updateTransform(e);return t&&this.emitTransformEvents(),t}emitTransformEvents(){const e=this.baseCanvas.transform;this.listen.emit("transform",e),this.listen.emit("zoom",e.scale),this.listen.emit("pan",{x:e.translateX,y:e.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(e){return this.baseCanvas.setZoom(e)}canvasToContent(e,t){return this.baseCanvas.canvasToContent(e,t)}zoomToPoint(e,t,n){return b(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(e,t,n);return r&&this.emitTransformEvents(),r})}resetView(){return b(this.transformLayer,this.config,()=>{const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e})}zoomToFitContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}panLeft(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+t};return this.updateTransform(n)}panRight(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-t};return this.updateTransform(n)}panUp(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+t};return this.updateTransform(n)}panDown(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-t};return this.updateTransform(n)}zoomIn(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1+e))};return this.updateTransform(n)}))}zoomOut(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1-e))};return this.updateTransform(n)}))}resetZoom(){return this.resetView()}enableMouseDrag(){return this.dragSetup?.enable()??!1}disableMouseDrag(){return this.dragSetup?.disable()??!1}isMouseDragEnabled(){return this.dragSetup?.isEnabled()??!1}toggleGrid(){return!!this.rulers?.toggleGrid&&(this.rulers.toggleGrid(),!0)}showGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="block",!0)}hideGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="none",!0)}isGridVisible(){return!!this.rulers?.gridOverlay&&"none"!==this.rulers.gridOverlay.style.display}toggleRulers(){if(this.rulers){return this.areRulersVisible()?this.rulers.hide():this.rulers.show(),!0}return!1}showRulers(){return!!this.rulers&&(this.rulers.show(),!0)}hideRulers(){return!!this.rulers&&(this.rulers.hide(),!0)}areRulersVisible(){return!!this.rulers?.horizontalRuler&&"none"!==this.rulers.horizontalRuler.style.display}centerContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.getBounds(),t=(e.width-e.contentWidth*this.baseCanvas.transform.scale)/2,n=(e.height-e.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:t,translateY:n})})}fitToScreen(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(e,t){const n=this.getVisibleArea();return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}scrollToPoint(e,t){return b(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,s=r-e*this.baseCanvas.transform.scale,a=o-t*this.baseCanvas.transform.scale;return this.updateTransform({translateX:s,translateY:a})})}getConfig(){return{...this.config}}updateConfig(e){this.config=x({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=x(t);const n=f(this.config,"canvasBackgroundColor");this.baseCanvas.container.style.backgroundColor=n,this.rulers&&this.rulers.updateTheme(this.config)}cleanup(){this.cleanupGlobalBinding(),this.postMessageCleanup&&(this.postMessageCleanup(),this.postMessageCleanup=null),this.cleanupFunctions.forEach(e=>{try{e()}catch(e){console.warn("Error during cleanup:",e)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(e,t){this.listen.on(e,t)}off(e,t){this.listen.off(e,t)}emit(e,t){this.listen.emit(e,t)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
@@ -5,6 +5,7 @@ export interface MarkupCanvasConfig {
5
5
  enableAcceleration?: boolean;
6
6
  bindToWindow?: boolean;
7
7
  name?: string;
8
+ enablePostMessageAPI?: boolean;
8
9
  enableZoom?: boolean;
9
10
  enablePan?: boolean;
10
11
  enableTouch?: boolean;
@@ -31,3 +31,10 @@ export interface MouseDragControls {
31
31
  disable: () => boolean;
32
32
  isEnabled: () => boolean;
33
33
  }
34
+ export type PostMessageAction = "zoomIn" | "zoomOut" | "setZoom" | "resetZoom" | "panLeft" | "panRight" | "panUp" | "panDown" | "fitToScreen" | "centerContent" | "scrollToPoint" | "resetView" | "toggleRulers" | "showRulers" | "hideRulers" | "toggleGrid" | "showGrid" | "hideGrid" | "updateThemeMode" | "toggleThemeMode";
35
+ export interface PostMessageRequest {
36
+ source: "markup-canvas";
37
+ canvasName: string;
38
+ action: PostMessageAction;
39
+ args?: unknown[];
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markup-canvas/core",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "High-performance canvas-like container with pan and zoom capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/markup-canvas.cjs.js",
@@ -1,7 +1,7 @@
1
1
  import { createCanvas } from "@/lib/canvas/index.js";
2
2
  import { createMarkupCanvasConfig } from "@/lib/config/createMarkupCanvasConfig.js";
3
3
  import { EventEmitter } from "@/lib/events/EventEmitter.js";
4
- import { setupKeyboardEvents, setupMouseEvents, setupTouchEvents, setupWheelEvents } from "@/lib/events/index.js";
4
+ import { setupKeyboardEvents, setupMouseEvents, setupPostMessageEvents, setupTouchEvents, setupWheelEvents } from "@/lib/events/index.js";
5
5
  import { getThemeValue, withClampedZoom, withFeatureEnabled } from "@/lib/helpers/index.js";
6
6
  import { createRulers } from "@/lib/rulers/index.js";
7
7
  import { withTransition } from "@/lib/transition/withTransition.js";
@@ -29,6 +29,7 @@ export class MarkupCanvas implements Canvas {
29
29
  public config: Required<MarkupCanvasConfig>;
30
30
  private _isReady = false;
31
31
  private listen = new EventEmitter<MarkupCanvasEvents>();
32
+ private postMessageCleanup: (() => void) | null = null;
32
33
 
33
34
  constructor(container: HTMLElement, options: MarkupCanvasConfig = {}) {
34
35
  if (!container) {
@@ -50,6 +51,11 @@ export class MarkupCanvas implements Canvas {
50
51
  this.broadcastEvent(event as string, data);
51
52
  });
52
53
  this.setupGlobalBinding();
54
+
55
+ // Set up postMessage listener
56
+ if (this.config.enablePostMessageAPI) {
57
+ this.postMessageCleanup = setupPostMessageEvents(this);
58
+ }
53
59
  }
54
60
 
55
61
  this.setupEventHandlers();
@@ -482,6 +488,13 @@ export class MarkupCanvas implements Canvas {
482
488
  // Cleanup method
483
489
  cleanup(): void {
484
490
  this.cleanupGlobalBinding();
491
+
492
+ // Cleanup postMessage listener
493
+ if (this.postMessageCleanup) {
494
+ this.postMessageCleanup();
495
+ this.postMessageCleanup = null;
496
+ }
497
+
485
498
  this.cleanupFunctions.forEach((cleanup) => {
486
499
  try {
487
500
  cleanup();
@@ -6,6 +6,11 @@ export const DEFAULT_CONFIG: Required<MarkupCanvasConfig> = {
6
6
  height: 8000,
7
7
  enableAcceleration: true,
8
8
 
9
+ // Global Binding & Instance Access
10
+ bindToWindow: false,
11
+ name: "markupCanvas",
12
+ enablePostMessageAPI: false,
13
+
9
14
  // Interaction controls
10
15
  enableZoom: true,
11
16
  enablePan: true,
@@ -67,10 +72,6 @@ export const DEFAULT_CONFIG: Required<MarkupCanvasConfig> = {
67
72
  // Theme
68
73
  themeMode: "light",
69
74
 
70
- // Global Binding & Instance Access
71
- bindToWindow: false,
72
- name: "markupCanvas",
73
-
74
75
  // Callbacks
75
76
  onTransformUpdate: () => {},
76
77
  };
@@ -9,6 +9,7 @@ export const EDITOR_PRESET: Required<MarkupCanvasConfig> = {
9
9
  // Global Binding & Instance Access
10
10
  bindToWindow: true,
11
11
  name: "canvas",
12
+ enablePostMessageAPI: true,
12
13
 
13
14
  // Interaction controls
14
15
  enableZoom: true,
@@ -1,5 +1,6 @@
1
1
  export { setupKeyboardEvents } from "./keyboard/setupKeyboardEvents.js";
2
2
  export { setupMouseEvents } from "./mouse/setupMouseEvents.js";
3
+ export { setupPostMessageEvents } from "./postMessage/setupPostMessageEvents.js";
3
4
  export { setupTouchEvents } from "./touch/setupTouchEvents.js";
4
5
  export { detectTrackpadGesture } from "./trackpad/detectTrackpadGesture.js";
5
6
  export { getAdaptiveZoomSpeed } from "./utils/getAdaptiveZoomSpeed.js";
@@ -80,14 +80,14 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
80
80
  break;
81
81
  case "g":
82
82
  case "G":
83
- if (canvas.toggleGrid) {
83
+ if (event.shiftKey && canvas.toggleGrid) {
84
84
  canvas.toggleGrid();
85
85
  }
86
- handled = true;
86
+ handled = event.shiftKey;
87
87
  break;
88
88
  case "r":
89
89
  case "R":
90
- if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
90
+ if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
91
91
  canvas.toggleRulers();
92
92
  handled = true;
93
93
  }
@@ -81,14 +81,14 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
81
81
  break;
82
82
  case "g":
83
83
  case "G":
84
- if (canvas.toggleGrid) {
84
+ if (event.shiftKey && canvas.toggleGrid) {
85
85
  canvas.toggleGrid();
86
86
  }
87
- handled = true;
87
+ handled = event.shiftKey;
88
88
  break;
89
89
  case "r":
90
90
  case "R":
91
- if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
91
+ if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
92
92
  canvas.toggleRulers();
93
93
  handled = true;
94
94
  }
@@ -0,0 +1,12 @@
1
+ export function sendPostMessageError(canvasName: string, action: string, error: string): void {
2
+ window.postMessage(
3
+ {
4
+ source: "markup-canvas-error",
5
+ canvasName,
6
+ action,
7
+ error,
8
+ timestamp: Date.now(),
9
+ },
10
+ "*"
11
+ );
12
+ }
@@ -0,0 +1,96 @@
1
+ import type { MarkupCanvas } from "@/lib/MarkupCanvas.js";
2
+ import type { PostMessageAction, PostMessageRequest } from "@/types/events";
3
+ import { sendPostMessageError } from "./sendError";
4
+
5
+ export function setupPostMessageEvents(canvas: MarkupCanvas): () => void {
6
+ const handleMessage = (event: MessageEvent): void => {
7
+ const data = event.data as PostMessageRequest;
8
+
9
+ // Validate message structure
10
+ if (data.source !== "markup-canvas") {
11
+ return;
12
+ }
13
+
14
+ const canvasName = canvas.config.name || "markupCanvas";
15
+ if (data.canvasName !== canvasName) {
16
+ return;
17
+ }
18
+
19
+ const action = data.action as PostMessageAction;
20
+ const args = data.args || [];
21
+
22
+ try {
23
+ // View methods
24
+ if (action === "zoomIn") {
25
+ canvas.zoomIn(args[0] as number | undefined);
26
+ } else if (action === "zoomOut") {
27
+ canvas.zoomOut(args[0] as number | undefined);
28
+ } else if (action === "setZoom") {
29
+ const zoomLevel = args[0] as number;
30
+ if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
31
+ throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
32
+ }
33
+ canvas.setZoom(zoomLevel);
34
+ } else if (action === "resetZoom") {
35
+ canvas.resetZoom();
36
+ } else if (action === "panLeft") {
37
+ canvas.panLeft(args[0] as number | undefined);
38
+ } else if (action === "panRight") {
39
+ canvas.panRight(args[0] as number | undefined);
40
+ } else if (action === "panUp") {
41
+ canvas.panUp(args[0] as number | undefined);
42
+ } else if (action === "panDown") {
43
+ canvas.panDown(args[0] as number | undefined);
44
+ } else if (action === "fitToScreen") {
45
+ canvas.fitToScreen();
46
+ } else if (action === "centerContent") {
47
+ canvas.centerContent();
48
+ } else if (action === "scrollToPoint") {
49
+ canvas.scrollToPoint(args[0] as number, args[1] as number);
50
+ } else if (action === "resetView") {
51
+ canvas.resetView();
52
+ }
53
+ // Ruler/Grid methods
54
+ else if (action === "toggleRulers") {
55
+ canvas.toggleRulers();
56
+ } else if (action === "showRulers") {
57
+ canvas.showRulers();
58
+ } else if (action === "hideRulers") {
59
+ canvas.hideRulers();
60
+ } else if (action === "toggleGrid") {
61
+ canvas.toggleGrid();
62
+ } else if (action === "showGrid") {
63
+ canvas.showGrid();
64
+ } else if (action === "hideGrid") {
65
+ canvas.hideGrid();
66
+ }
67
+ // Config methods
68
+ else if (action === "updateThemeMode") {
69
+ const mode = args[0] as "light" | "dark";
70
+ if (mode !== "light" && mode !== "dark") {
71
+ throw new Error(`Invalid theme mode: ${mode}`);
72
+ }
73
+ canvas.updateThemeMode(mode);
74
+ } else if (action === "toggleThemeMode") {
75
+ const currentConfig = canvas.getConfig();
76
+ const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
77
+ canvas.updateThemeMode(newMode);
78
+ } else {
79
+ throw new Error(`Unknown action: ${action}`);
80
+ }
81
+ } catch (error) {
82
+ const errorMessage = error instanceof Error ? error.message : String(error);
83
+ sendPostMessageError(canvasName, action, errorMessage);
84
+ }
85
+ };
86
+
87
+ if (typeof window !== "undefined") {
88
+ window.addEventListener("message", handleMessage);
89
+ }
90
+
91
+ return () => {
92
+ if (typeof window !== "undefined") {
93
+ window.removeEventListener("message", handleMessage);
94
+ }
95
+ };
96
+ }
@@ -10,6 +10,9 @@ export interface MarkupCanvasConfig {
10
10
  bindToWindow?: boolean;
11
11
  name?: string;
12
12
 
13
+ // Post Message API
14
+ enablePostMessageAPI?: boolean;
15
+
13
16
  // Interaction controls
14
17
  enableZoom?: boolean;
15
18
  enablePan?: boolean;
@@ -29,3 +29,32 @@ export interface MouseDragControls {
29
29
  disable: () => boolean;
30
30
  isEnabled: () => boolean;
31
31
  }
32
+
33
+ export type PostMessageAction =
34
+ | "zoomIn"
35
+ | "zoomOut"
36
+ | "setZoom"
37
+ | "resetZoom"
38
+ | "panLeft"
39
+ | "panRight"
40
+ | "panUp"
41
+ | "panDown"
42
+ | "fitToScreen"
43
+ | "centerContent"
44
+ | "scrollToPoint"
45
+ | "resetView"
46
+ | "toggleRulers"
47
+ | "showRulers"
48
+ | "hideRulers"
49
+ | "toggleGrid"
50
+ | "showGrid"
51
+ | "hideGrid"
52
+ | "updateThemeMode"
53
+ | "toggleThemeMode";
54
+
55
+ export interface PostMessageRequest {
56
+ source: "markup-canvas";
57
+ canvasName: string;
58
+ action: PostMessageAction;
59
+ args?: unknown[];
60
+ }