canvu-react 0.3.37 → 0.3.39
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/{asset-hydration-DrTOgDdd.d.ts → asset-hydration-DdFLdlqX.d.ts} +1 -1
- package/dist/{asset-hydration-EtEuBwb7.d.cts → asset-hydration-DowNdaOJ.d.cts} +1 -1
- package/dist/chatbot.d.cts +2 -2
- package/dist/chatbot.d.ts +2 -2
- package/dist/index.cjs +275 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +270 -1
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +213 -17
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -6
- package/dist/react.d.ts +6 -6
- package/dist/react.js +213 -17
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +3 -3
- package/dist/realtime.d.ts +3 -3
- package/dist/realtime.js.map +1 -1
- package/dist/{shape-builders-CsSXKCcs.d.ts → shape-builders-C7bxJBGR.d.ts} +40 -2
- package/dist/{shape-builders-CsbSRZnQ.d.cts → shape-builders-Dedcl6tw.d.cts} +40 -2
- package/dist/tldraw.cjs +134 -65
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.js +134 -65
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-B6PAYKzx.d.ts → types-BBb8KoyW.d.ts} +10 -1
- package/dist/{types-DWGk2_GZ.d.cts → types-DUW61Tjy.d.cts} +10 -1
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -4970,6 +4970,78 @@ var Camera2D = class {
|
|
|
4970
4970
|
}
|
|
4971
4971
|
};
|
|
4972
4972
|
|
|
4973
|
+
// src/input/pan-momentum.ts
|
|
4974
|
+
var VELOCITY_SAMPLE_WINDOW = 80;
|
|
4975
|
+
var FRICTION = 0.94;
|
|
4976
|
+
var MIN_VELOCITY = 0.3;
|
|
4977
|
+
function createPanMomentumController(camera, onUpdate, sensitivity) {
|
|
4978
|
+
const samples = [];
|
|
4979
|
+
let animationFrameId = null;
|
|
4980
|
+
const cancel = () => {
|
|
4981
|
+
if (animationFrameId !== null) {
|
|
4982
|
+
cancelAnimationFrame(animationFrameId);
|
|
4983
|
+
animationFrameId = null;
|
|
4984
|
+
}
|
|
4985
|
+
};
|
|
4986
|
+
const reset = () => {
|
|
4987
|
+
cancel();
|
|
4988
|
+
samples.length = 0;
|
|
4989
|
+
};
|
|
4990
|
+
const recordMove = (dx, dy) => {
|
|
4991
|
+
const now = performance.now();
|
|
4992
|
+
samples.push({ dx, dy, time: now });
|
|
4993
|
+
const cutoff = now - VELOCITY_SAMPLE_WINDOW;
|
|
4994
|
+
let oldestSample = samples[0];
|
|
4995
|
+
while (oldestSample && oldestSample.time < cutoff) {
|
|
4996
|
+
samples.shift();
|
|
4997
|
+
oldestSample = samples[0];
|
|
4998
|
+
}
|
|
4999
|
+
};
|
|
5000
|
+
const computeReleaseVelocity = () => {
|
|
5001
|
+
if (samples.length < 2) return { vx: 0, vy: 0 };
|
|
5002
|
+
const first = samples[0];
|
|
5003
|
+
const last = samples[samples.length - 1];
|
|
5004
|
+
if (!first || !last) return { vx: 0, vy: 0 };
|
|
5005
|
+
const elapsed = last.time - first.time;
|
|
5006
|
+
if (elapsed < 4) return { vx: 0, vy: 0 };
|
|
5007
|
+
let totalDx = 0;
|
|
5008
|
+
let totalDy = 0;
|
|
5009
|
+
for (const sample of samples) {
|
|
5010
|
+
totalDx += sample.dx;
|
|
5011
|
+
totalDy += sample.dy;
|
|
5012
|
+
}
|
|
5013
|
+
const msPerFrame = 1e3 / 60;
|
|
5014
|
+
return {
|
|
5015
|
+
vx: totalDx / elapsed * msPerFrame * sensitivity,
|
|
5016
|
+
vy: totalDy / elapsed * msPerFrame * sensitivity
|
|
5017
|
+
};
|
|
5018
|
+
};
|
|
5019
|
+
const startMomentum = () => {
|
|
5020
|
+
cancel();
|
|
5021
|
+
const { vx, vy } = computeReleaseVelocity();
|
|
5022
|
+
samples.length = 0;
|
|
5023
|
+
if (Math.abs(vx) < MIN_VELOCITY && Math.abs(vy) < MIN_VELOCITY) {
|
|
5024
|
+
return;
|
|
5025
|
+
}
|
|
5026
|
+
let currentVx = vx;
|
|
5027
|
+
let currentVy = vy;
|
|
5028
|
+
const animate = () => {
|
|
5029
|
+
currentVx *= FRICTION;
|
|
5030
|
+
currentVy *= FRICTION;
|
|
5031
|
+
if (Math.abs(currentVx) < MIN_VELOCITY && Math.abs(currentVy) < MIN_VELOCITY) {
|
|
5032
|
+
animationFrameId = null;
|
|
5033
|
+
return;
|
|
5034
|
+
}
|
|
5035
|
+
camera.x += currentVx;
|
|
5036
|
+
camera.y += currentVy;
|
|
5037
|
+
onUpdate();
|
|
5038
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
5039
|
+
};
|
|
5040
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
5041
|
+
};
|
|
5042
|
+
return { recordMove, startMomentum, cancel, reset };
|
|
5043
|
+
}
|
|
5044
|
+
|
|
4973
5045
|
// src/input/apple-pencil-navigation.ts
|
|
4974
5046
|
var DRAWING_LIKE_TOOLS = /* @__PURE__ */ new Set([
|
|
4975
5047
|
"draw",
|
|
@@ -5000,6 +5072,7 @@ function attachApplePencilNavigation(options) {
|
|
|
5000
5072
|
let pinchStartDist = 0;
|
|
5001
5073
|
let pinchStartZoom = 1;
|
|
5002
5074
|
let panLast = null;
|
|
5075
|
+
let touchMomentum = null;
|
|
5003
5076
|
const shouldIntercept = (e) => {
|
|
5004
5077
|
if (e.pointerType !== "touch") return false;
|
|
5005
5078
|
const tool = getCurrentToolId();
|
|
@@ -5018,6 +5091,10 @@ function attachApplePencilNavigation(options) {
|
|
|
5018
5091
|
pointers.clear();
|
|
5019
5092
|
mode = "idle";
|
|
5020
5093
|
panLast = null;
|
|
5094
|
+
if (touchMomentum) {
|
|
5095
|
+
touchMomentum.cancel();
|
|
5096
|
+
touchMomentum = null;
|
|
5097
|
+
}
|
|
5021
5098
|
return;
|
|
5022
5099
|
}
|
|
5023
5100
|
if (e.pointerType === "touch" && activePenPointerIds.size > 0) {
|
|
@@ -5027,10 +5104,18 @@ function attachApplePencilNavigation(options) {
|
|
|
5027
5104
|
return;
|
|
5028
5105
|
}
|
|
5029
5106
|
if (!shouldIntercept(e)) return;
|
|
5107
|
+
if (touchMomentum) {
|
|
5108
|
+
touchMomentum.cancel();
|
|
5109
|
+
}
|
|
5030
5110
|
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
5031
5111
|
if (pointers.size === 1) {
|
|
5032
5112
|
mode = "pan";
|
|
5033
5113
|
panLast = { x: e.clientX, y: e.clientY };
|
|
5114
|
+
touchMomentum = createPanMomentumController(
|
|
5115
|
+
camera,
|
|
5116
|
+
onUpdate,
|
|
5117
|
+
touchPanSensitivity
|
|
5118
|
+
);
|
|
5034
5119
|
} else if (pointers.size === 2) {
|
|
5035
5120
|
const vals = Array.from(pointers.values());
|
|
5036
5121
|
const a = vals[0];
|
|
@@ -5058,6 +5143,7 @@ function attachApplePencilNavigation(options) {
|
|
|
5058
5143
|
panLast = { x: e.clientX, y: e.clientY };
|
|
5059
5144
|
camera.x += dx * touchPanSensitivity;
|
|
5060
5145
|
camera.y += dy * touchPanSensitivity;
|
|
5146
|
+
touchMomentum?.recordMove(dx, dy);
|
|
5061
5147
|
onUpdate();
|
|
5062
5148
|
e.preventDefault();
|
|
5063
5149
|
e.stopImmediatePropagation();
|
|
@@ -5104,12 +5190,20 @@ function attachApplePencilNavigation(options) {
|
|
|
5104
5190
|
if (!pointers.has(e.pointerId)) return;
|
|
5105
5191
|
pointers.delete(e.pointerId);
|
|
5106
5192
|
if (pointers.size === 0) {
|
|
5193
|
+
const wasPanning = mode === "pan";
|
|
5107
5194
|
mode = "idle";
|
|
5108
5195
|
panLast = null;
|
|
5196
|
+
if (wasPanning && touchMomentum) {
|
|
5197
|
+
touchMomentum.startMomentum();
|
|
5198
|
+
touchMomentum = null;
|
|
5199
|
+
}
|
|
5109
5200
|
} else if (pointers.size === 1 && mode === "pinch") {
|
|
5110
5201
|
mode = "pan";
|
|
5111
5202
|
const r = Array.from(pointers.values())[0];
|
|
5112
5203
|
panLast = r ?? null;
|
|
5204
|
+
if (touchMomentum) {
|
|
5205
|
+
touchMomentum.reset();
|
|
5206
|
+
}
|
|
5113
5207
|
}
|
|
5114
5208
|
e.preventDefault();
|
|
5115
5209
|
e.stopImmediatePropagation();
|
|
@@ -5119,6 +5213,10 @@ function attachApplePencilNavigation(options) {
|
|
|
5119
5213
|
element.addEventListener("pointerup", onPointerUp, { capture: true });
|
|
5120
5214
|
element.addEventListener("pointercancel", onPointerUp, { capture: true });
|
|
5121
5215
|
return () => {
|
|
5216
|
+
if (touchMomentum) {
|
|
5217
|
+
touchMomentum.cancel();
|
|
5218
|
+
touchMomentum = null;
|
|
5219
|
+
}
|
|
5122
5220
|
element.removeEventListener("pointerdown", onPointerDown, { capture: true });
|
|
5123
5221
|
element.removeEventListener("pointermove", onPointerMove, { capture: true });
|
|
5124
5222
|
element.removeEventListener("pointerup", onPointerUp, { capture: true });
|
|
@@ -5171,6 +5269,7 @@ function attachViewportInput(options) {
|
|
|
5171
5269
|
let pinchStartZoom = 1;
|
|
5172
5270
|
let panLast = null;
|
|
5173
5271
|
let mousePanButton = null;
|
|
5272
|
+
let touchMomentum = null;
|
|
5174
5273
|
const onWheel = (e) => {
|
|
5175
5274
|
if (e.ctrlKey || e.metaKey) {
|
|
5176
5275
|
e.preventDefault();
|
|
@@ -5197,6 +5296,9 @@ function attachViewportInput(options) {
|
|
|
5197
5296
|
if (touchHandledElsewhere && e.pointerType === "touch") {
|
|
5198
5297
|
return;
|
|
5199
5298
|
}
|
|
5299
|
+
if (touchMomentum) {
|
|
5300
|
+
touchMomentum.cancel();
|
|
5301
|
+
}
|
|
5200
5302
|
const panOk = allowPrimaryPointerPan();
|
|
5201
5303
|
if (e.pointerType === "mouse" && e.button === 0) {
|
|
5202
5304
|
if (!panOk) {
|
|
@@ -5222,6 +5324,11 @@ function attachViewportInput(options) {
|
|
|
5222
5324
|
if (panOk) {
|
|
5223
5325
|
mode = "pan";
|
|
5224
5326
|
panLast = { x: e.clientX, y: e.clientY };
|
|
5327
|
+
touchMomentum = createPanMomentumController(
|
|
5328
|
+
camera,
|
|
5329
|
+
onUpdate,
|
|
5330
|
+
touchPanSensitivity
|
|
5331
|
+
);
|
|
5225
5332
|
e.preventDefault();
|
|
5226
5333
|
}
|
|
5227
5334
|
} else if (pointers.size === 2) {
|
|
@@ -5258,6 +5365,7 @@ function attachViewportInput(options) {
|
|
|
5258
5365
|
panLast = { x: e.clientX, y: e.clientY };
|
|
5259
5366
|
camera.x += dx * touchPanSensitivity;
|
|
5260
5367
|
camera.y += dy * touchPanSensitivity;
|
|
5368
|
+
touchMomentum?.recordMove(dx, dy);
|
|
5261
5369
|
onUpdate();
|
|
5262
5370
|
e.preventDefault();
|
|
5263
5371
|
return;
|
|
@@ -5309,15 +5417,27 @@ function attachViewportInput(options) {
|
|
|
5309
5417
|
}
|
|
5310
5418
|
pointers.delete(e.pointerId);
|
|
5311
5419
|
if (pointers.size === 0) {
|
|
5420
|
+
const wasPanning = mode === "pan";
|
|
5312
5421
|
mode = "idle";
|
|
5313
5422
|
panLast = null;
|
|
5423
|
+
if (wasPanning && touchMomentum) {
|
|
5424
|
+
touchMomentum.startMomentum();
|
|
5425
|
+
touchMomentum = null;
|
|
5426
|
+
}
|
|
5314
5427
|
} else if (pointers.size === 1 && mode === "pinch") {
|
|
5315
5428
|
mode = "pan";
|
|
5316
5429
|
const remaining = Array.from(pointers.values())[0];
|
|
5317
5430
|
panLast = remaining ?? null;
|
|
5431
|
+
if (touchMomentum) {
|
|
5432
|
+
touchMomentum.reset();
|
|
5433
|
+
}
|
|
5318
5434
|
}
|
|
5319
5435
|
};
|
|
5320
5436
|
const onPointerCancel = (e) => {
|
|
5437
|
+
if (touchMomentum) {
|
|
5438
|
+
touchMomentum.cancel();
|
|
5439
|
+
touchMomentum = null;
|
|
5440
|
+
}
|
|
5321
5441
|
onPointerUp(e);
|
|
5322
5442
|
};
|
|
5323
5443
|
wheelTarget.addEventListener("wheel", onWheel, { passive: false });
|
|
@@ -5326,6 +5446,10 @@ function attachViewportInput(options) {
|
|
|
5326
5446
|
element.addEventListener("pointerup", onPointerUp);
|
|
5327
5447
|
element.addEventListener("pointercancel", onPointerCancel);
|
|
5328
5448
|
return () => {
|
|
5449
|
+
if (touchMomentum) {
|
|
5450
|
+
touchMomentum.cancel();
|
|
5451
|
+
touchMomentum = null;
|
|
5452
|
+
}
|
|
5329
5453
|
wheelTarget.removeEventListener("wheel", onWheel);
|
|
5330
5454
|
element.removeEventListener("pointerdown", onPointerDown);
|
|
5331
5455
|
element.removeEventListener("pointermove", onPointerMove);
|
|
@@ -6207,6 +6331,7 @@ var SvgVectorRenderer = class {
|
|
|
6207
6331
|
svg;
|
|
6208
6332
|
rootG;
|
|
6209
6333
|
itemNodeCache = /* @__PURE__ */ new Map();
|
|
6334
|
+
liveOverlay = null;
|
|
6210
6335
|
resizeObserver;
|
|
6211
6336
|
constructor(options) {
|
|
6212
6337
|
this.container = options.container;
|
|
@@ -6241,6 +6366,50 @@ var SvgVectorRenderer = class {
|
|
|
6241
6366
|
const items = cullItemsByViewport(this.scene.getItems(), visible);
|
|
6242
6367
|
this.syncVisibleItems(items);
|
|
6243
6368
|
this.rootG.setAttribute("transform", formatCameraTransform(this.camera));
|
|
6369
|
+
this.keepLiveOverlayOnTop();
|
|
6370
|
+
}
|
|
6371
|
+
/**
|
|
6372
|
+
* Updates only the in-progress (live) stroke node without re-culling or
|
|
6373
|
+
* re-syncing the committed scene items. Drawing tools call this on every
|
|
6374
|
+
* pointer move, so it must stay O(1) regardless of how many items the
|
|
6375
|
+
* board already contains.
|
|
6376
|
+
*
|
|
6377
|
+
* Pass `null` to remove the live overlay (e.g. when the stroke is committed).
|
|
6378
|
+
*/
|
|
6379
|
+
renderLiveItem(item) {
|
|
6380
|
+
if (!item) {
|
|
6381
|
+
if (this.liveOverlay) {
|
|
6382
|
+
this.liveOverlay.g.remove();
|
|
6383
|
+
this.liveOverlay = null;
|
|
6384
|
+
}
|
|
6385
|
+
return;
|
|
6386
|
+
}
|
|
6387
|
+
if (!this.liveOverlay) {
|
|
6388
|
+
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
6389
|
+
g.setAttribute("data-live-overlay", "true");
|
|
6390
|
+
this.liveOverlay = {
|
|
6391
|
+
g,
|
|
6392
|
+
lastChildrenSvg: "",
|
|
6393
|
+
lastTransform: ""
|
|
6394
|
+
};
|
|
6395
|
+
this.rootG.appendChild(g);
|
|
6396
|
+
}
|
|
6397
|
+
const cached = this.liveOverlay;
|
|
6398
|
+
const t = formatItemPlacementTransform(item);
|
|
6399
|
+
if (cached.lastTransform !== t) {
|
|
6400
|
+
cached.g.setAttribute("transform", t);
|
|
6401
|
+
cached.lastTransform = t;
|
|
6402
|
+
}
|
|
6403
|
+
if (cached.lastChildrenSvg !== item.childrenSvg) {
|
|
6404
|
+
cached.g.innerHTML = item.childrenSvg;
|
|
6405
|
+
cached.lastChildrenSvg = item.childrenSvg;
|
|
6406
|
+
}
|
|
6407
|
+
this.keepLiveOverlayOnTop();
|
|
6408
|
+
}
|
|
6409
|
+
keepLiveOverlayOnTop() {
|
|
6410
|
+
if (this.liveOverlay && this.rootG.lastChild !== this.liveOverlay.g) {
|
|
6411
|
+
this.rootG.appendChild(this.liveOverlay.g);
|
|
6412
|
+
}
|
|
6244
6413
|
}
|
|
6245
6414
|
syncVisibleItems(items) {
|
|
6246
6415
|
const visibleIds = /* @__PURE__ */ new Set();
|
|
@@ -6285,6 +6454,7 @@ var SvgVectorRenderer = class {
|
|
|
6285
6454
|
destroy() {
|
|
6286
6455
|
this.resizeObserver.disconnect();
|
|
6287
6456
|
this.itemNodeCache.clear();
|
|
6457
|
+
this.liveOverlay = null;
|
|
6288
6458
|
this.svg.remove();
|
|
6289
6459
|
}
|
|
6290
6460
|
/** Toggle whether the scene SVG receives pointer events (vs overlay handling them). */
|
|
@@ -6293,6 +6463,18 @@ var SvgVectorRenderer = class {
|
|
|
6293
6463
|
}
|
|
6294
6464
|
};
|
|
6295
6465
|
|
|
6466
|
+
// src/scene/link-item.ts
|
|
6467
|
+
var LINK_PLUGIN_KEY = "canvuLink";
|
|
6468
|
+
var isCanvuLinkData = (value) => {
|
|
6469
|
+
if (!value || typeof value !== "object") return false;
|
|
6470
|
+
const candidate = value;
|
|
6471
|
+
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
6472
|
+
};
|
|
6473
|
+
function getLinkData(item) {
|
|
6474
|
+
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
6475
|
+
return isCanvuLinkData(entry) ? entry : null;
|
|
6476
|
+
}
|
|
6477
|
+
|
|
6296
6478
|
// src/scene/scene.ts
|
|
6297
6479
|
var VectorScene = class {
|
|
6298
6480
|
items = [];
|
|
@@ -7469,6 +7651,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7469
7651
|
selectedIds: selectedIdsProp,
|
|
7470
7652
|
onSelectionChange,
|
|
7471
7653
|
onItemsChange: consumerOnItemsChange,
|
|
7654
|
+
onActivateLink,
|
|
7472
7655
|
onWorldPointerDown: consumerOnWorldPointerDown,
|
|
7473
7656
|
toolbar,
|
|
7474
7657
|
navMenu,
|
|
@@ -7649,6 +7832,8 @@ var VectorViewport = react.forwardRef(
|
|
|
7649
7832
|
const itemsRef = react.useRef(items);
|
|
7650
7833
|
const onWorldPointerDownRef = react.useRef(onWorldPointerDown);
|
|
7651
7834
|
const onItemsChangeRef = react.useRef(onItemsChange);
|
|
7835
|
+
const onActivateLinkRef = react.useRef(onActivateLink);
|
|
7836
|
+
onActivateLinkRef.current = onActivateLink;
|
|
7652
7837
|
const assetStoreRef = react.useRef(assetStore);
|
|
7653
7838
|
assetStoreRef.current = assetStore;
|
|
7654
7839
|
const customPlacementRef = react.useRef(customPlacement);
|
|
@@ -7761,8 +7946,6 @@ var VectorViewport = react.forwardRef(
|
|
|
7761
7946
|
const hiddenIds = new Set(eraserPreviewIds);
|
|
7762
7947
|
return resolvedItems.filter((it) => !hiddenIds.has(it.id));
|
|
7763
7948
|
}, [eraserPreviewIds, resolvedItems]);
|
|
7764
|
-
const resolvedSceneItemsRef = react.useRef(resolvedSceneItems);
|
|
7765
|
-
resolvedSceneItemsRef.current = resolvedSceneItems;
|
|
7766
7949
|
const livePenStrokeItemRef = react.useRef(null);
|
|
7767
7950
|
const [eraserActive, setEraserActive] = react.useState(false);
|
|
7768
7951
|
const [editingTextId, setEditingTextId] = react.useState(null);
|
|
@@ -7983,12 +8166,9 @@ var VectorViewport = react.forwardRef(
|
|
|
7983
8166
|
const renderSceneWithLivePenStroke = react.useCallback(
|
|
7984
8167
|
(item) => {
|
|
7985
8168
|
livePenStrokeItemRef.current = item;
|
|
7986
|
-
const scene = sceneRef.current;
|
|
7987
8169
|
const renderer = rendererRef.current;
|
|
7988
|
-
if (!
|
|
7989
|
-
|
|
7990
|
-
scene.setItems(item ? [...base2, item] : base2);
|
|
7991
|
-
renderer.render();
|
|
8170
|
+
if (!renderer) return;
|
|
8171
|
+
renderer.renderLiveItem(item);
|
|
7992
8172
|
},
|
|
7993
8173
|
[]
|
|
7994
8174
|
);
|
|
@@ -8219,16 +8399,15 @@ var VectorViewport = react.forwardRef(
|
|
|
8219
8399
|
}, []);
|
|
8220
8400
|
react.useEffect(() => {
|
|
8221
8401
|
const scene = sceneRef.current;
|
|
8402
|
+
const renderer = rendererRef.current;
|
|
8222
8403
|
if (scene) {
|
|
8223
8404
|
const live = livePenStrokeItemRef.current;
|
|
8224
8405
|
if (live && resolvedSceneItems.some((it) => it.id === live.id)) {
|
|
8225
8406
|
livePenStrokeItemRef.current = null;
|
|
8226
8407
|
}
|
|
8227
|
-
|
|
8228
|
-
scene.setItems(
|
|
8229
|
-
currentLive ? [...resolvedSceneItems, currentLive] : resolvedSceneItems
|
|
8230
|
-
);
|
|
8408
|
+
scene.setItems(resolvedSceneItems);
|
|
8231
8409
|
renderFrame();
|
|
8410
|
+
renderer?.renderLiveItem(livePenStrokeItemRef.current);
|
|
8232
8411
|
}
|
|
8233
8412
|
}, [resolvedSceneItems, renderFrame]);
|
|
8234
8413
|
react.useEffect(() => {
|
|
@@ -8804,11 +8983,12 @@ var VectorViewport = react.forwardRef(
|
|
|
8804
8983
|
setEditingTextId(null);
|
|
8805
8984
|
}, []);
|
|
8806
8985
|
const placeImageFilesAtWorld = react.useCallback(
|
|
8807
|
-
async (files, worldX, worldY) => {
|
|
8986
|
+
async (files, worldX, worldY, options) => {
|
|
8808
8987
|
const change = onItemsChangeRef.current;
|
|
8809
8988
|
if (!change || files.length === 0) return;
|
|
8810
8989
|
const store = imageStoreRef.current;
|
|
8811
8990
|
if (!store) return;
|
|
8991
|
+
const stackBelowExistingItems = options?.stackBelowExistingItems ?? true;
|
|
8812
8992
|
try {
|
|
8813
8993
|
const pdfFiles = files.filter((file) => file.type === "application/pdf");
|
|
8814
8994
|
if (pdfFiles.length > 0) {
|
|
@@ -8834,7 +9014,7 @@ var VectorViewport = react.forwardRef(
|
|
|
8834
9014
|
}
|
|
8835
9015
|
const result = await ingestAssetFilesToSceneItems({
|
|
8836
9016
|
files,
|
|
8837
|
-
existingItems: itemsRef.current,
|
|
9017
|
+
existingItems: stackBelowExistingItems ? itemsRef.current : [],
|
|
8838
9018
|
worldCenter: {
|
|
8839
9019
|
x: worldX,
|
|
8840
9020
|
y: worldY
|
|
@@ -8921,19 +9101,35 @@ var VectorViewport = react.forwardRef(
|
|
|
8921
9101
|
);
|
|
8922
9102
|
if (files.length === 0) return;
|
|
8923
9103
|
const { worldX, worldY } = screenToWorld(e.clientX, e.clientY);
|
|
8924
|
-
await placeImageFilesAtWorld(files, worldX, worldY
|
|
9104
|
+
await placeImageFilesAtWorld(files, worldX, worldY, {
|
|
9105
|
+
stackBelowExistingItems: false
|
|
9106
|
+
});
|
|
8925
9107
|
},
|
|
8926
9108
|
[screenToWorld, placeImageFilesAtWorld]
|
|
8927
9109
|
);
|
|
8928
9110
|
const handleOverlayDoubleClick = react.useCallback(
|
|
8929
9111
|
(e) => {
|
|
8930
|
-
if (!interactiveRef.current || !onItemsChangeRef.current) return;
|
|
8931
|
-
if (toolIdRef.current !== "select") return;
|
|
8932
|
-
e.preventDefault();
|
|
8933
9112
|
const cam = cameraRef.current;
|
|
8934
9113
|
if (!cam) return;
|
|
8935
9114
|
const { worldX, worldY } = screenToWorld(e.clientX, e.clientY);
|
|
8936
9115
|
const lineHitWorld = 10 / cam.zoom;
|
|
9116
|
+
const linkHit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
|
|
9117
|
+
lineHitWorld,
|
|
9118
|
+
ignoreLocked: false
|
|
9119
|
+
});
|
|
9120
|
+
const link = linkHit ? getLinkData(linkHit) : null;
|
|
9121
|
+
if (linkHit && link) {
|
|
9122
|
+
e.preventDefault();
|
|
9123
|
+
if (onActivateLinkRef.current) {
|
|
9124
|
+
onActivateLinkRef.current({ link, item: linkHit });
|
|
9125
|
+
} else if (typeof window !== "undefined" && typeof window.open === "function") {
|
|
9126
|
+
window.open(link.href, "_blank", "noopener,noreferrer");
|
|
9127
|
+
}
|
|
9128
|
+
return;
|
|
9129
|
+
}
|
|
9130
|
+
if (!interactiveRef.current || !onItemsChangeRef.current) return;
|
|
9131
|
+
if (toolIdRef.current !== "select") return;
|
|
9132
|
+
e.preventDefault();
|
|
8937
9133
|
const hit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
|
|
8938
9134
|
lineHitWorld,
|
|
8939
9135
|
ignoreLocked: true
|