canvu-react 0.4.66 → 0.4.68

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/dist/react.cjs CHANGED
@@ -6884,6 +6884,152 @@ function cullItemsByViewport(items, visibleWorld) {
6884
6884
  return cullItemsByViewportSpatial(items, visibleWorld, SPATIAL_CELL_SIZE);
6885
6885
  }
6886
6886
 
6887
+ // src/renderer/raster-image-canvas.ts
6888
+ init_rect();
6889
+ var DEFAULT_PIXEL_HEADROOM = 1.5;
6890
+ var DEFAULT_MAX_PIXEL_COUNT = 12e6;
6891
+ var DEFAULT_MAX_DIMENSION = 4096;
6892
+ var DEFAULT_UPSCALE_REDRAW_RATIO = 1.15;
6893
+ function resolveRasterImageCanvasRenderingOptions(options) {
6894
+ if (!options) return null;
6895
+ const fallbackDevicePixelRatio = typeof window === "undefined" ? 1 : window.devicePixelRatio;
6896
+ return {
6897
+ resolveRenderTarget: options.resolveRenderTarget,
6898
+ devicePixelRatio: toPositiveFiniteNumber(
6899
+ options.devicePixelRatio,
6900
+ fallbackDevicePixelRatio
6901
+ ),
6902
+ pixelHeadroom: toPositiveFiniteNumber(
6903
+ options.pixelHeadroom,
6904
+ DEFAULT_PIXEL_HEADROOM
6905
+ ),
6906
+ maxPixelCount: toPositiveFiniteNumber(
6907
+ options.maxPixelCount,
6908
+ DEFAULT_MAX_PIXEL_COUNT
6909
+ ),
6910
+ maxDimension: toPositiveFiniteNumber(
6911
+ options.maxDimension,
6912
+ DEFAULT_MAX_DIMENSION
6913
+ ),
6914
+ upscaleRedrawRatio: toPositiveFiniteNumber(
6915
+ options.upscaleRedrawRatio,
6916
+ DEFAULT_UPSCALE_REDRAW_RATIO
6917
+ )
6918
+ };
6919
+ }
6920
+ function getRasterImageContentRect(item) {
6921
+ if (item.toolKind !== "image" || !item.imageIntrinsicSize) return null;
6922
+ const bounds = normalizeRect(item.bounds);
6923
+ const intrinsicWidth = Math.max(1e-6, item.imageIntrinsicSize.width);
6924
+ const intrinsicHeight = Math.max(1e-6, item.imageIntrinsicSize.height);
6925
+ const boundsAspectRatio = bounds.width / Math.max(1e-9, bounds.height);
6926
+ const imageAspectRatio = intrinsicWidth / intrinsicHeight;
6927
+ if (Math.abs(boundsAspectRatio - imageAspectRatio) < 1e-3) {
6928
+ return { x: 0, y: 0, width: bounds.width, height: bounds.height };
6929
+ }
6930
+ if (boundsAspectRatio > imageAspectRatio) {
6931
+ const height2 = bounds.height;
6932
+ const width2 = height2 * imageAspectRatio;
6933
+ return { x: (bounds.width - width2) / 2, y: 0, width: width2, height: height2 };
6934
+ }
6935
+ const width = bounds.width;
6936
+ const height = width / imageAspectRatio;
6937
+ return { x: 0, y: (bounds.height - height) / 2, width, height };
6938
+ }
6939
+ function getRasterImageCanvasTargetSize({
6940
+ intrinsicSize,
6941
+ contentRect,
6942
+ cameraZoom,
6943
+ devicePixelRatio,
6944
+ pixelHeadroom,
6945
+ maxPixelCount,
6946
+ maxDimension
6947
+ }) {
6948
+ const intrinsicWidth = Math.max(1, Math.round(intrinsicSize.width));
6949
+ const intrinsicHeight = Math.max(1, Math.round(intrinsicSize.height));
6950
+ const targetCssWidth = Math.max(1, contentRect.width * cameraZoom);
6951
+ const targetCssHeight = Math.max(1, contentRect.height * cameraZoom);
6952
+ const desiredWidth = targetCssWidth * toPositiveFiniteNumber(devicePixelRatio, 1) * pixelHeadroom;
6953
+ const desiredHeight = targetCssHeight * toPositiveFiniteNumber(devicePixelRatio, 1) * pixelHeadroom;
6954
+ const dimensionScale = Math.min(
6955
+ 1,
6956
+ toPositiveFiniteNumber(maxDimension, intrinsicWidth) / Math.max(intrinsicWidth, intrinsicHeight)
6957
+ );
6958
+ const pixelScale = Math.min(
6959
+ 1,
6960
+ Math.sqrt(
6961
+ toPositiveFiniteNumber(maxPixelCount, intrinsicWidth * intrinsicHeight) / (intrinsicWidth * intrinsicHeight)
6962
+ )
6963
+ );
6964
+ const viewportScale = Math.min(
6965
+ 1,
6966
+ desiredWidth / intrinsicWidth,
6967
+ desiredHeight / intrinsicHeight
6968
+ );
6969
+ const scale = Math.min(dimensionScale, pixelScale, viewportScale);
6970
+ const width = Math.max(1, Math.round(intrinsicWidth * scale));
6971
+ const height = Math.max(1, Math.round(width * (intrinsicHeight / intrinsicWidth)));
6972
+ return { width, height };
6973
+ }
6974
+ function resolveRasterImageCanvasRenderTarget({
6975
+ item,
6976
+ href,
6977
+ intrinsicSize,
6978
+ contentRect,
6979
+ targetSize,
6980
+ viewportSize,
6981
+ cameraZoom,
6982
+ options
6983
+ }) {
6984
+ const request = {
6985
+ item,
6986
+ href,
6987
+ intrinsicSize,
6988
+ contentRect,
6989
+ targetSize,
6990
+ viewportSize,
6991
+ cameraZoom,
6992
+ devicePixelRatio: options.devicePixelRatio
6993
+ };
6994
+ const resolved = options.resolveRenderTarget?.(request);
6995
+ if (typeof resolved === "string") {
6996
+ return { href: resolved, sourceKey: resolved, targetSize, contentRect };
6997
+ }
6998
+ if (resolved?.href) {
6999
+ return {
7000
+ href: resolved.href,
7001
+ sourceKey: resolved.sourceKey ?? resolved.href,
7002
+ targetSize,
7003
+ contentRect
7004
+ };
7005
+ }
7006
+ return { href, sourceKey: href, targetSize, contentRect };
7007
+ }
7008
+ function shouldRedrawRasterImageCanvas({
7009
+ currentSourceKey,
7010
+ currentWidth,
7011
+ currentHeight,
7012
+ nextSourceKey,
7013
+ nextWidth,
7014
+ nextHeight,
7015
+ upscaleRedrawRatio
7016
+ }) {
7017
+ const safeCurrentWidth = Math.max(0, Math.round(currentWidth));
7018
+ const safeCurrentHeight = Math.max(0, Math.round(currentHeight));
7019
+ const safeNextWidth = Math.max(1, Math.round(nextWidth));
7020
+ const safeNextHeight = Math.max(1, Math.round(nextHeight));
7021
+ if (!currentSourceKey || safeCurrentWidth <= 1 || safeCurrentHeight <= 1) {
7022
+ return true;
7023
+ }
7024
+ if (currentSourceKey !== nextSourceKey && safeCurrentWidth === safeNextWidth && safeCurrentHeight === safeNextHeight) {
7025
+ return true;
7026
+ }
7027
+ return safeNextWidth > safeCurrentWidth * upscaleRedrawRatio || safeNextHeight > safeCurrentHeight * upscaleRedrawRatio;
7028
+ }
7029
+ function toPositiveFiniteNumber(value, fallback) {
7030
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
7031
+ }
7032
+
6887
7033
  // src/renderer/svg-vector-renderer.ts
6888
7034
  function formatCameraTransform(camera) {
6889
7035
  const z = camera.zoom;
@@ -6908,6 +7054,87 @@ function itemClassName(item) {
6908
7054
  }
6909
7055
  return classes.join(" ");
6910
7056
  }
7057
+ async function decodeRasterImage(href, width, height, signal) {
7058
+ if (typeof createImageBitmap === "function") {
7059
+ try {
7060
+ const response = await fetch(href, { signal, credentials: "same-origin" });
7061
+ if (!response.ok) {
7062
+ throw new Error(`Failed to fetch raster image: ${response.status}`);
7063
+ }
7064
+ const blob = await response.blob();
7065
+ if (signal.aborted) throw new DOMException("Aborted", "AbortError");
7066
+ const bitmap = await createImageBitmap(blob, {
7067
+ resizeWidth: width,
7068
+ resizeHeight: height,
7069
+ resizeQuality: "high"
7070
+ });
7071
+ return {
7072
+ width: bitmap.width,
7073
+ height: bitmap.height,
7074
+ draw: (context) => {
7075
+ context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
7076
+ },
7077
+ close: () => bitmap.close()
7078
+ };
7079
+ } catch (error) {
7080
+ if (signal.aborted) throw error;
7081
+ }
7082
+ }
7083
+ const image = await loadImageElement(href, signal);
7084
+ return {
7085
+ width,
7086
+ height,
7087
+ draw: (context) => {
7088
+ context.drawImage(image, 0, 0, width, height);
7089
+ },
7090
+ close: () => {
7091
+ }
7092
+ };
7093
+ }
7094
+ function loadImageElement(href, signal) {
7095
+ return new Promise((resolve, reject) => {
7096
+ if (signal.aborted) {
7097
+ reject(new DOMException("Aborted", "AbortError"));
7098
+ return;
7099
+ }
7100
+ const image = new Image();
7101
+ const cleanup = () => {
7102
+ signal.removeEventListener("abort", abort);
7103
+ image.onload = null;
7104
+ image.onerror = null;
7105
+ };
7106
+ const abort = () => {
7107
+ cleanup();
7108
+ image.removeAttribute("src");
7109
+ reject(new DOMException("Aborted", "AbortError"));
7110
+ };
7111
+ image.decoding = "async";
7112
+ image.onload = () => {
7113
+ const decodePromise = image.decode?.();
7114
+ if (!decodePromise) {
7115
+ cleanup();
7116
+ resolve(image);
7117
+ return;
7118
+ }
7119
+ decodePromise.then(
7120
+ () => {
7121
+ cleanup();
7122
+ resolve(image);
7123
+ },
7124
+ () => {
7125
+ cleanup();
7126
+ resolve(image);
7127
+ }
7128
+ );
7129
+ };
7130
+ image.onerror = () => {
7131
+ cleanup();
7132
+ reject(new Error("Failed to load raster image"));
7133
+ };
7134
+ signal.addEventListener("abort", abort, { once: true });
7135
+ image.src = href;
7136
+ });
7137
+ }
6911
7138
  var SvgVectorRenderer = class {
6912
7139
  container;
6913
7140
  scene;
@@ -6919,10 +7146,14 @@ var SvgVectorRenderer = class {
6919
7146
  hoveredItemId = null;
6920
7147
  liveOverlay = null;
6921
7148
  resizeObserver;
7149
+ rasterImageCanvasRendering;
6922
7150
  constructor(options) {
6923
7151
  this.container = options.container;
6924
7152
  this.scene = options.scene;
6925
7153
  this.camera = options.camera;
7154
+ this.rasterImageCanvasRendering = resolveRasterImageCanvasRenderingOptions(
7155
+ options.rasterImageCanvas
7156
+ );
6926
7157
  this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
6927
7158
  this.svg.setAttribute("width", "100%");
6928
7159
  this.svg.setAttribute("height", "100%");
@@ -6951,6 +7182,15 @@ var SvgVectorRenderer = class {
6951
7182
  this.applyInteractionAttributes(cached.g, id);
6952
7183
  }
6953
7184
  }
7185
+ setRasterImageCanvasRendering(options) {
7186
+ this.rasterImageCanvasRendering = resolveRasterImageCanvasRenderingOptions(options);
7187
+ if (!this.rasterImageCanvasRendering) {
7188
+ for (const cached of this.itemNodeCache.values()) {
7189
+ this.releaseRasterImageCanvas(cached);
7190
+ }
7191
+ }
7192
+ this.render();
7193
+ }
6954
7194
  /**
6955
7195
  * Reads container size, culls items, and updates the SVG (incrementally when possible).
6956
7196
  */
@@ -7024,6 +7264,7 @@ var SvgVectorRenderer = class {
7024
7264
  }
7025
7265
  for (const [id, cached] of this.itemNodeCache) {
7026
7266
  if (!visibleIds.has(id)) {
7267
+ this.releaseRasterImageCanvas(cached);
7027
7268
  cached.g.remove();
7028
7269
  }
7029
7270
  }
@@ -7056,6 +7297,7 @@ var SvgVectorRenderer = class {
7056
7297
  g.innerHTML = item.childrenSvg;
7057
7298
  cached.lastChildrenSvg = item.childrenSvg;
7058
7299
  }
7300
+ this.syncRasterImageCanvas(cached, item);
7059
7301
  const expectedPosition = previousNode ? previousNode.nextSibling : this.rootG.firstChild;
7060
7302
  if (expectedPosition !== g) {
7061
7303
  this.rootG.insertBefore(g, expectedPosition);
@@ -7063,6 +7305,228 @@ var SvgVectorRenderer = class {
7063
7305
  previousNode = g;
7064
7306
  }
7065
7307
  }
7308
+ syncRasterImageCanvas(cached, item) {
7309
+ const options = this.rasterImageCanvasRendering;
7310
+ if (!options || item.toolKind !== "image" || !item.imageRasterHref || !item.imageIntrinsicSize) {
7311
+ this.releaseRasterImageCanvas(cached);
7312
+ return;
7313
+ }
7314
+ const contentRect = getRasterImageContentRect(item);
7315
+ const viewportSize = {
7316
+ width: this.container.clientWidth,
7317
+ height: this.container.clientHeight
7318
+ };
7319
+ if (!contentRect || viewportSize.width <= 0 || viewportSize.height <= 0) {
7320
+ this.releaseRasterImageCanvas(cached);
7321
+ return;
7322
+ }
7323
+ const targetSize = getRasterImageCanvasTargetSize({
7324
+ intrinsicSize: item.imageIntrinsicSize,
7325
+ contentRect,
7326
+ cameraZoom: this.camera.zoom,
7327
+ devicePixelRatio: options.devicePixelRatio,
7328
+ pixelHeadroom: options.pixelHeadroom,
7329
+ maxPixelCount: options.maxPixelCount,
7330
+ maxDimension: options.maxDimension
7331
+ });
7332
+ const target = resolveRasterImageCanvasRenderTarget({
7333
+ item,
7334
+ href: item.imageRasterHref,
7335
+ intrinsicSize: item.imageIntrinsicSize,
7336
+ contentRect,
7337
+ targetSize,
7338
+ viewportSize,
7339
+ cameraZoom: this.camera.zoom,
7340
+ options
7341
+ });
7342
+ const rasterCanvas = this.ensureRasterImageCanvas(cached);
7343
+ this.positionRasterImageCanvas(rasterCanvas, target.contentRect);
7344
+ if (rasterCanvas.itemHref !== null && rasterCanvas.itemHref !== item.imageRasterHref) {
7345
+ this.clearRasterImageCanvasBitmap(rasterCanvas);
7346
+ }
7347
+ if (!shouldRedrawRasterImageCanvas({
7348
+ currentSourceKey: rasterCanvas.sourceKey,
7349
+ currentWidth: rasterCanvas.width,
7350
+ currentHeight: rasterCanvas.height,
7351
+ nextSourceKey: target.sourceKey,
7352
+ nextWidth: target.targetSize.width,
7353
+ nextHeight: target.targetSize.height,
7354
+ upscaleRedrawRatio: options.upscaleRedrawRatio
7355
+ })) {
7356
+ return;
7357
+ }
7358
+ const request = {
7359
+ itemHref: item.imageRasterHref,
7360
+ target
7361
+ };
7362
+ if (rasterCanvas.abortController) {
7363
+ if (rasterCanvas.loadingItemHref !== item.imageRasterHref) {
7364
+ rasterCanvas.abortController.abort();
7365
+ rasterCanvas.abortController = null;
7366
+ rasterCanvas.loadingItemHref = null;
7367
+ rasterCanvas.loadingSourceKey = null;
7368
+ rasterCanvas.loadingWidth = 0;
7369
+ rasterCanvas.loadingHeight = 0;
7370
+ rasterCanvas.queuedTarget = null;
7371
+ this.drawRasterImageCanvas(rasterCanvas, request);
7372
+ return;
7373
+ }
7374
+ if (rasterCanvas.loadingSourceKey === target.sourceKey && rasterCanvas.loadingWidth === target.targetSize.width && rasterCanvas.loadingHeight === target.targetSize.height) {
7375
+ return;
7376
+ }
7377
+ if (shouldRedrawRasterImageCanvas({
7378
+ currentSourceKey: rasterCanvas.loadingSourceKey,
7379
+ currentWidth: rasterCanvas.loadingWidth,
7380
+ currentHeight: rasterCanvas.loadingHeight,
7381
+ nextSourceKey: target.sourceKey,
7382
+ nextWidth: target.targetSize.width,
7383
+ nextHeight: target.targetSize.height,
7384
+ upscaleRedrawRatio: options.upscaleRedrawRatio
7385
+ })) {
7386
+ rasterCanvas.queuedTarget = request;
7387
+ }
7388
+ return;
7389
+ }
7390
+ this.drawRasterImageCanvas(rasterCanvas, request);
7391
+ }
7392
+ ensureRasterImageCanvas(cached) {
7393
+ if (cached.rasterCanvas) {
7394
+ if (!cached.rasterCanvas.foreignObject.isConnected) {
7395
+ cached.g.appendChild(cached.rasterCanvas.foreignObject);
7396
+ }
7397
+ return cached.rasterCanvas;
7398
+ }
7399
+ const foreignObject = document.createElementNS(
7400
+ "http://www.w3.org/2000/svg",
7401
+ "foreignObject"
7402
+ );
7403
+ foreignObject.setAttribute("data-canvu-raster-canvas", "true");
7404
+ foreignObject.style.pointerEvents = "none";
7405
+ const canvas = document.createElement("canvas");
7406
+ canvas.width = 1;
7407
+ canvas.height = 1;
7408
+ canvas.style.display = "block";
7409
+ canvas.style.width = "100%";
7410
+ canvas.style.height = "100%";
7411
+ foreignObject.appendChild(canvas);
7412
+ cached.g.appendChild(foreignObject);
7413
+ cached.rasterCanvas = {
7414
+ foreignObject,
7415
+ canvas,
7416
+ itemHref: null,
7417
+ sourceKey: null,
7418
+ width: 0,
7419
+ height: 0,
7420
+ loadSequence: 0,
7421
+ abortController: null,
7422
+ loadingItemHref: null,
7423
+ loadingSourceKey: null,
7424
+ loadingWidth: 0,
7425
+ loadingHeight: 0,
7426
+ queuedTarget: null
7427
+ };
7428
+ return cached.rasterCanvas;
7429
+ }
7430
+ positionRasterImageCanvas(rasterCanvas, contentRect) {
7431
+ rasterCanvas.foreignObject.setAttribute("x", String(contentRect.x));
7432
+ rasterCanvas.foreignObject.setAttribute("y", String(contentRect.y));
7433
+ rasterCanvas.foreignObject.setAttribute("width", String(contentRect.width));
7434
+ rasterCanvas.foreignObject.setAttribute("height", String(contentRect.height));
7435
+ }
7436
+ drawRasterImageCanvas(rasterCanvas, request) {
7437
+ const { target } = request;
7438
+ const width = Math.max(1, Math.round(target.targetSize.width));
7439
+ const height = Math.max(1, Math.round(target.targetSize.height));
7440
+ const sequence = rasterCanvas.loadSequence + 1;
7441
+ rasterCanvas.loadSequence = sequence;
7442
+ rasterCanvas.abortController?.abort();
7443
+ const abortController = new AbortController();
7444
+ rasterCanvas.abortController = abortController;
7445
+ rasterCanvas.loadingItemHref = request.itemHref;
7446
+ rasterCanvas.loadingSourceKey = target.sourceKey;
7447
+ rasterCanvas.loadingWidth = width;
7448
+ rasterCanvas.loadingHeight = height;
7449
+ rasterCanvas.queuedTarget = null;
7450
+ decodeRasterImage(target.href, width, height, abortController.signal).then((decoded) => {
7451
+ if (abortController.signal.aborted || rasterCanvas.loadSequence !== sequence) {
7452
+ decoded.close();
7453
+ return;
7454
+ }
7455
+ const context = rasterCanvas.canvas.getContext("2d");
7456
+ if (!context) {
7457
+ decoded.close();
7458
+ rasterCanvas.abortController = null;
7459
+ rasterCanvas.loadingItemHref = null;
7460
+ rasterCanvas.loadingSourceKey = null;
7461
+ rasterCanvas.loadingWidth = 0;
7462
+ rasterCanvas.loadingHeight = 0;
7463
+ this.drawQueuedRasterImageCanvasTarget(rasterCanvas);
7464
+ return;
7465
+ }
7466
+ rasterCanvas.canvas.width = decoded.width;
7467
+ rasterCanvas.canvas.height = decoded.height;
7468
+ context.clearRect(0, 0, decoded.width, decoded.height);
7469
+ decoded.draw(context);
7470
+ decoded.close();
7471
+ rasterCanvas.itemHref = request.itemHref;
7472
+ rasterCanvas.sourceKey = target.sourceKey;
7473
+ rasterCanvas.width = decoded.width;
7474
+ rasterCanvas.height = decoded.height;
7475
+ rasterCanvas.abortController = null;
7476
+ rasterCanvas.loadingItemHref = null;
7477
+ rasterCanvas.loadingSourceKey = null;
7478
+ rasterCanvas.loadingWidth = 0;
7479
+ rasterCanvas.loadingHeight = 0;
7480
+ this.drawQueuedRasterImageCanvasTarget(rasterCanvas);
7481
+ }).catch((error) => {
7482
+ if (abortController.signal.aborted) return;
7483
+ if (error instanceof Error && error.name === "AbortError") return;
7484
+ if (rasterCanvas.loadSequence === sequence) {
7485
+ rasterCanvas.abortController = null;
7486
+ rasterCanvas.loadingItemHref = null;
7487
+ rasterCanvas.loadingSourceKey = null;
7488
+ rasterCanvas.loadingWidth = 0;
7489
+ rasterCanvas.loadingHeight = 0;
7490
+ this.drawQueuedRasterImageCanvasTarget(rasterCanvas);
7491
+ }
7492
+ });
7493
+ }
7494
+ drawQueuedRasterImageCanvasTarget(rasterCanvas) {
7495
+ const queuedTarget = rasterCanvas.queuedTarget;
7496
+ if (!queuedTarget) return;
7497
+ rasterCanvas.queuedTarget = null;
7498
+ if (!shouldRedrawRasterImageCanvas({
7499
+ currentSourceKey: rasterCanvas.sourceKey,
7500
+ currentWidth: rasterCanvas.width,
7501
+ currentHeight: rasterCanvas.height,
7502
+ nextSourceKey: queuedTarget.target.sourceKey,
7503
+ nextWidth: queuedTarget.target.targetSize.width,
7504
+ nextHeight: queuedTarget.target.targetSize.height,
7505
+ upscaleRedrawRatio: this.rasterImageCanvasRendering?.upscaleRedrawRatio ?? 1
7506
+ })) {
7507
+ return;
7508
+ }
7509
+ this.drawRasterImageCanvas(rasterCanvas, queuedTarget);
7510
+ }
7511
+ clearRasterImageCanvasBitmap(rasterCanvas) {
7512
+ const context = rasterCanvas.canvas.getContext("2d");
7513
+ context?.clearRect(0, 0, rasterCanvas.canvas.width, rasterCanvas.canvas.height);
7514
+ rasterCanvas.canvas.width = 1;
7515
+ rasterCanvas.canvas.height = 1;
7516
+ rasterCanvas.itemHref = null;
7517
+ rasterCanvas.sourceKey = null;
7518
+ rasterCanvas.width = 0;
7519
+ rasterCanvas.height = 0;
7520
+ }
7521
+ releaseRasterImageCanvas(cached) {
7522
+ const rasterCanvas = cached.rasterCanvas;
7523
+ if (!rasterCanvas) return;
7524
+ rasterCanvas.abortController?.abort();
7525
+ rasterCanvas.loadSequence += 1;
7526
+ rasterCanvas.queuedTarget = null;
7527
+ rasterCanvas.foreignObject.remove();
7528
+ cached.rasterCanvas = void 0;
7529
+ }
7066
7530
  applyInteractionAttributes(g, itemId) {
7067
7531
  g.setAttribute(
7068
7532
  "data-canvu-selected",
@@ -7075,6 +7539,9 @@ var SvgVectorRenderer = class {
7075
7539
  }
7076
7540
  destroy() {
7077
7541
  this.resizeObserver.disconnect();
7542
+ for (const cached of this.itemNodeCache.values()) {
7543
+ this.releaseRasterImageCanvas(cached);
7544
+ }
7078
7545
  this.itemNodeCache.clear();
7079
7546
  this.liveOverlay = null;
7080
7547
  this.svg.remove();
@@ -8364,6 +8831,7 @@ var VectorViewport = react.forwardRef(
8364
8831
  customPlacement: consumerCustomPlacement,
8365
8832
  customPlacements: consumerCustomPlacements,
8366
8833
  assetStore,
8834
+ imageCanvasRendering,
8367
8835
  toolLocked = false,
8368
8836
  autoResetToolTo = "select",
8369
8837
  onToolChangeRequest,
@@ -8570,6 +9038,8 @@ var VectorViewport = react.forwardRef(
8570
9038
  onActivateLinkRef.current = onActivateLink;
8571
9039
  const assetStoreRef = react.useRef(assetStore);
8572
9040
  assetStoreRef.current = assetStore;
9041
+ const imageCanvasRenderingRef = react.useRef(imageCanvasRendering);
9042
+ imageCanvasRenderingRef.current = imageCanvasRendering;
8573
9043
  const customPlacementRef = react.useRef(customPlacement);
8574
9044
  customPlacementRef.current = customPlacement;
8575
9045
  const allCustomPlacementsRef = react.useRef(allCustomPlacements);
@@ -9190,7 +9660,8 @@ var VectorViewport = react.forwardRef(
9190
9660
  container: el,
9191
9661
  scene,
9192
9662
  camera,
9193
- pointerEventsNone: interactiveRef.current
9663
+ pointerEventsNone: interactiveRef.current,
9664
+ rasterImageCanvas: imageCanvasRenderingRef.current
9194
9665
  });
9195
9666
  rendererRef.current = renderer;
9196
9667
  renderer.setInteractionState({
@@ -9242,6 +9713,9 @@ var VectorViewport = react.forwardRef(
9242
9713
  hoveredItemId: hoveredItemIdRef.current
9243
9714
  });
9244
9715
  }, [effectiveSelectedIds]);
9716
+ react.useEffect(() => {
9717
+ rendererRef.current?.setRasterImageCanvasRendering(imageCanvasRendering);
9718
+ }, [imageCanvasRendering]);
9245
9719
  react.useEffect(() => {
9246
9720
  const r = rendererRef.current;
9247
9721
  if (!r) return;
@@ -9781,15 +10255,26 @@ var VectorViewport = react.forwardRef(
9781
10255
  if (!cam) return;
9782
10256
  const { worldX, worldY } = screenToWorld(e.clientX, e.clientY);
9783
10257
  const lineHitWorld = 10 / cam.zoom;
10258
+ const cur = effectiveSelectedIdsRef.current;
9784
10259
  const hit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
9785
10260
  lineHitWorld,
9786
10261
  ignoreLocked: true
9787
10262
  });
9788
10263
  if (!hit) {
10264
+ const selectedUnlockedItems = cur.map((id) => resolvedItemsRef.current.find((item) => item.id === id)).filter((item) => item != null && !item.locked);
10265
+ if (selectedUnlockedItems.some(
10266
+ (item) => pointInSelectedItemBounds(item, worldX, worldY)
10267
+ )) {
10268
+ setContextMenuState({
10269
+ x: e.clientX,
10270
+ y: e.clientY,
10271
+ itemIds: [...cur]
10272
+ });
10273
+ return;
10274
+ }
9789
10275
  setContextMenuState(null);
9790
10276
  return;
9791
10277
  }
9792
- const cur = effectiveSelectedIdsRef.current;
9793
10278
  let nextIds;
9794
10279
  if (!cur.includes(hit.id)) {
9795
10280
  nextIds = e.shiftKey ? [...cur, hit.id] : [hit.id];