canvu-react 0.3.14 → 0.3.16
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 +5 -0
- package/dist/react.cjs +16 -37
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +16 -37
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -308,6 +308,11 @@ canvu can now stream PDF pages into the canvas progressively through
|
|
|
308
308
|
`onItemsReady(...)`, skip PDF thumbnails during ingest, and use a lower initial
|
|
309
309
|
raster scale by default to improve time-to-first-render.
|
|
310
310
|
|
|
311
|
+
The native file tool now accepts multiple images and PDFs from the picker and
|
|
312
|
+
stacks every imported asset vertically. When the board already has uploaded
|
|
313
|
+
images or PDFs, new imports are inserted immediately after the existing stack
|
|
314
|
+
instead of overlapping previous content.
|
|
315
|
+
|
|
311
316
|
This helper is the same ingestion layer used internally by the native file
|
|
312
317
|
tool, so external imports do not need to reimplement PDF rasterization, local
|
|
313
318
|
blob persistence, or `pluginData` attachment.
|
package/dist/react.cjs
CHANGED
|
@@ -1365,12 +1365,12 @@ async function uploadAssetIfNeeded(assetStore, file, kind) {
|
|
|
1365
1365
|
async function ingestAssetFilesToSceneItems(options) {
|
|
1366
1366
|
const {
|
|
1367
1367
|
files,
|
|
1368
|
+
existingItems = [],
|
|
1368
1369
|
worldCenter,
|
|
1369
1370
|
assetStore,
|
|
1370
1371
|
imageStore = new IndexedDbImageStore(),
|
|
1371
1372
|
createId = createShapeId,
|
|
1372
1373
|
gapWorld = 16,
|
|
1373
|
-
stepWorld = 48,
|
|
1374
1374
|
pdfScale = 1.15,
|
|
1375
1375
|
pdfPageConcurrency = 2,
|
|
1376
1376
|
decorateItem,
|
|
@@ -1379,10 +1379,7 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1379
1379
|
} = options;
|
|
1380
1380
|
const items = [];
|
|
1381
1381
|
const errors = [];
|
|
1382
|
-
let
|
|
1383
|
-
let occupiedBottomY = null;
|
|
1384
|
-
let imageYOffsetAdjustment = 0;
|
|
1385
|
-
let hasImagePlacementBase = false;
|
|
1382
|
+
let occupiedBottomY = existingItems.length > 0 ? Math.max(...existingItems.map((item) => item.bounds.y + item.bounds.height)) : null;
|
|
1386
1383
|
for (const file of files) {
|
|
1387
1384
|
const kind = getAssetKindForFile(file);
|
|
1388
1385
|
if (!kind) {
|
|
@@ -1405,10 +1402,10 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1405
1402
|
const uploadResult2 = await uploadResultPromise;
|
|
1406
1403
|
const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
|
|
1407
1404
|
const naturalTopY2 = worldCenter.y - page.height / 2;
|
|
1408
|
-
const
|
|
1405
|
+
const stackedTopY2 = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
|
|
1409
1406
|
const bounds2 = {
|
|
1410
1407
|
x: worldCenter.x - page.width / 2,
|
|
1411
|
-
y: Math.max(naturalTopY2,
|
|
1408
|
+
y: Math.max(naturalTopY2, stackedTopY2),
|
|
1412
1409
|
width: page.width,
|
|
1413
1410
|
height: page.height
|
|
1414
1411
|
};
|
|
@@ -1446,9 +1443,6 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1446
1443
|
onItemsReady?.([item2], { file, kind });
|
|
1447
1444
|
}
|
|
1448
1445
|
});
|
|
1449
|
-
hasImagePlacementBase = false;
|
|
1450
|
-
imagePlacementIndex = 0;
|
|
1451
|
-
imageYOffsetAdjustment = 0;
|
|
1452
1446
|
continue;
|
|
1453
1447
|
}
|
|
1454
1448
|
const [uploadResult, storedImage] = await Promise.all([
|
|
@@ -1459,17 +1453,11 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1459
1453
|
const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
|
|
1460
1454
|
const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
|
|
1461
1455
|
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
1462
|
-
const
|
|
1463
|
-
const
|
|
1464
|
-
const naturalTopY = worldCenter.y - height / 2 + oy;
|
|
1465
|
-
if (!hasImagePlacementBase) {
|
|
1466
|
-
const minimumTopY = occupiedBottomY == null ? naturalTopY : occupiedBottomY + gapWorld;
|
|
1467
|
-
imageYOffsetAdjustment = Math.max(0, minimumTopY - naturalTopY);
|
|
1468
|
-
hasImagePlacementBase = true;
|
|
1469
|
-
}
|
|
1456
|
+
const naturalTopY = worldCenter.y - height / 2;
|
|
1457
|
+
const stackedTopY = occupiedBottomY == null ? naturalTopY : occupiedBottomY + gapWorld;
|
|
1470
1458
|
const bounds = {
|
|
1471
|
-
x: worldCenter.x - width / 2
|
|
1472
|
-
y: naturalTopY
|
|
1459
|
+
x: worldCenter.x - width / 2,
|
|
1460
|
+
y: Math.max(naturalTopY, stackedTopY),
|
|
1473
1461
|
width,
|
|
1474
1462
|
height
|
|
1475
1463
|
};
|
|
@@ -1499,7 +1487,6 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1499
1487
|
);
|
|
1500
1488
|
items.push(item);
|
|
1501
1489
|
onItemsReady?.([item], { file, kind });
|
|
1502
|
-
imagePlacementIndex++;
|
|
1503
1490
|
occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
|
|
1504
1491
|
} catch (error) {
|
|
1505
1492
|
const fileError = {
|
|
@@ -6010,7 +5997,7 @@ body[data-canvu-pen-active="true"] [data-slot="shape-context-menu"] * {
|
|
|
6010
5997
|
}
|
|
6011
5998
|
`;
|
|
6012
5999
|
function debugApplePencilPointer(phase, detail) {
|
|
6013
|
-
|
|
6000
|
+
return;
|
|
6014
6001
|
}
|
|
6015
6002
|
var MARKER_TOOL_STYLE = {
|
|
6016
6003
|
stroke: "#fde047",
|
|
@@ -6610,7 +6597,6 @@ var VectorViewport = react.forwardRef(
|
|
|
6610
6597
|
const captureInteractionPointer = react.useCallback(
|
|
6611
6598
|
(target, pointerId, options) => {
|
|
6612
6599
|
const captureNativePointer = options?.captureNativePointer ?? true;
|
|
6613
|
-
debugApplePencilPointer("capture", { pointerId, captureNativePointer });
|
|
6614
6600
|
activeInteractionPointerIdRef.current = pointerId;
|
|
6615
6601
|
activeInteractionPointerTargetRef.current = captureNativePointer ? target : null;
|
|
6616
6602
|
if (captureNativePointer) {
|
|
@@ -6622,7 +6608,6 @@ var VectorViewport = react.forwardRef(
|
|
|
6622
6608
|
const releaseInteractionPointer = react.useCallback(() => {
|
|
6623
6609
|
const pointerId = activeInteractionPointerIdRef.current;
|
|
6624
6610
|
const target = activeInteractionPointerTargetRef.current;
|
|
6625
|
-
debugApplePencilPointer("release", { pointerId });
|
|
6626
6611
|
if (pointerId != null && target) {
|
|
6627
6612
|
try {
|
|
6628
6613
|
if (target.hasPointerCapture(pointerId)) {
|
|
@@ -6655,8 +6640,6 @@ var VectorViewport = react.forwardRef(
|
|
|
6655
6640
|
const itemId = strokeState.itemId;
|
|
6656
6641
|
const style = { ...strokeStyleRef.current };
|
|
6657
6642
|
debugApplePencilPointer("finalize-stroke", {
|
|
6658
|
-
pointerType,
|
|
6659
|
-
tool,
|
|
6660
6643
|
points: pts.length
|
|
6661
6644
|
});
|
|
6662
6645
|
dragStateRef.current = { kind: "idle" };
|
|
@@ -7406,6 +7389,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7406
7389
|
}
|
|
7407
7390
|
const result = await ingestAssetFilesToSceneItems({
|
|
7408
7391
|
files,
|
|
7392
|
+
existingItems: itemsRef.current,
|
|
7409
7393
|
worldCenter: {
|
|
7410
7394
|
x: worldX,
|
|
7411
7395
|
y: worldY
|
|
@@ -7433,12 +7417,12 @@ var VectorViewport = react.forwardRef(
|
|
|
7433
7417
|
);
|
|
7434
7418
|
const handleImageFileChange = react.useCallback(
|
|
7435
7419
|
async (e) => {
|
|
7436
|
-
const
|
|
7420
|
+
const files = e.target.files ? Array.from(e.target.files) : [];
|
|
7437
7421
|
e.target.value = "";
|
|
7438
7422
|
const pending = pendingImagePlacementRef.current;
|
|
7439
7423
|
pendingImagePlacementRef.current = null;
|
|
7440
|
-
if (
|
|
7441
|
-
await placeImageFilesAtWorld(
|
|
7424
|
+
if (files.length === 0 || !pending) return;
|
|
7425
|
+
await placeImageFilesAtWorld(files, pending.worldX, pending.worldY);
|
|
7442
7426
|
},
|
|
7443
7427
|
[placeImageFilesAtWorld]
|
|
7444
7428
|
);
|
|
@@ -7534,14 +7518,12 @@ var VectorViewport = react.forwardRef(
|
|
|
7534
7518
|
pointerType: e.pointerType,
|
|
7535
7519
|
pointerId: e.pointerId,
|
|
7536
7520
|
pressure: e.pointerType === "pen" ? e.pressure : void 0,
|
|
7537
|
-
activePointerId,
|
|
7538
7521
|
dragKind: currentDragState.kind
|
|
7539
7522
|
});
|
|
7540
7523
|
}
|
|
7541
7524
|
if (e.pointerType === "pen" && currentDragState.kind === "stroke" && currentDragState.pointerType === "pen") {
|
|
7542
7525
|
debugApplePencilPointer("pen-reentry", {
|
|
7543
7526
|
pointerId: e.pointerId,
|
|
7544
|
-
previousPointerId: activePointerId,
|
|
7545
7527
|
previousPoints: currentDragState.points.length
|
|
7546
7528
|
});
|
|
7547
7529
|
finalizeStrokeDragState(currentDragState);
|
|
@@ -7887,9 +7869,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7887
7869
|
debugApplePencilPointer("native-pointerdown", {
|
|
7888
7870
|
pointerType: e.pointerType,
|
|
7889
7871
|
pointerId: e.pointerId,
|
|
7890
|
-
pressure: e.pressure
|
|
7891
|
-
tool
|
|
7892
|
-
});
|
|
7872
|
+
pressure: e.pressure});
|
|
7893
7873
|
e.preventDefault();
|
|
7894
7874
|
e.stopImmediatePropagation();
|
|
7895
7875
|
};
|
|
@@ -7989,9 +7969,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7989
7969
|
debugApplePencilPointer("touchstart-stroke", {
|
|
7990
7970
|
touchId: touch.identifier,
|
|
7991
7971
|
touchType: touchKind(touch),
|
|
7992
|
-
force: touchPressure(touch)
|
|
7993
|
-
tool
|
|
7994
|
-
});
|
|
7972
|
+
force: touchPressure(touch)});
|
|
7995
7973
|
stopTouchEvent(ev);
|
|
7996
7974
|
};
|
|
7997
7975
|
const onTouchMove = (ev) => {
|
|
@@ -8713,6 +8691,7 @@ var VectorViewport = react.forwardRef(
|
|
|
8713
8691
|
{
|
|
8714
8692
|
ref: imageInputRef,
|
|
8715
8693
|
type: "file",
|
|
8694
|
+
multiple: true,
|
|
8716
8695
|
accept: "image/*,application/pdf",
|
|
8717
8696
|
style: { display: "none" },
|
|
8718
8697
|
"aria-label": "Select image or PDF to place on canvas",
|