canvu-react 0.3.24 → 0.3.25

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.js CHANGED
@@ -711,6 +711,47 @@ var init_shape_builders = __esm({
711
711
  }
712
712
  });
713
713
 
714
+ // src/image/canvas-encode.ts
715
+ var DEFAULT_FALLBACK_MIME_TYPES = ["image/png"];
716
+ var tryCanvasToBlob = (canvas, mimeType, quality) => new Promise((resolve) => {
717
+ canvas.toBlob((blob) => resolve(blob), mimeType, quality);
718
+ });
719
+ var blobFromDataUrl = async (dataUrl) => {
720
+ const response = await fetch(dataUrl);
721
+ const blob = await response.blob();
722
+ if (blob.size === 0) {
723
+ throw new Error("Failed to encode canvas to blob");
724
+ }
725
+ return blob;
726
+ };
727
+ async function encodeCanvasToBlob(canvas, options) {
728
+ const primaryMimeType = options?.mimeType ?? "image/png";
729
+ const quality = options?.quality;
730
+ const mimeTypes = [
731
+ primaryMimeType,
732
+ ...options?.fallbackMimeTypes ?? DEFAULT_FALLBACK_MIME_TYPES
733
+ ].filter(
734
+ (mimeType, index, mimeTypeList) => mimeTypeList.indexOf(mimeType) === index
735
+ );
736
+ for (const mimeType of mimeTypes) {
737
+ const blob = await tryCanvasToBlob(
738
+ canvas,
739
+ mimeType,
740
+ mimeType === primaryMimeType ? quality : void 0
741
+ );
742
+ if (blob) {
743
+ return blob;
744
+ }
745
+ }
746
+ for (const mimeType of mimeTypes) {
747
+ const dataUrl = canvas.toDataURL(mimeType, quality);
748
+ if (dataUrl && dataUrl !== "data:,") {
749
+ return blobFromDataUrl(dataUrl);
750
+ }
751
+ }
752
+ throw new Error("Failed to encode canvas to blob");
753
+ }
754
+
714
755
  // src/image/indexed-db-image-store.ts
715
756
  var DB_NAME = "canvu-image-store";
716
757
  var DB_VERSION = 1;
@@ -829,19 +870,7 @@ function decodeImageToCanvas(blob, maxDimension) {
829
870
  });
830
871
  }
831
872
  function canvasToBlob(canvas, mime, quality) {
832
- return new Promise((resolve, reject) => {
833
- canvas.toBlob(
834
- (blob) => {
835
- if (!blob) {
836
- reject(new Error("Could not encode blob"));
837
- return;
838
- }
839
- resolve(blob);
840
- },
841
- mime,
842
- quality
843
- );
844
- });
873
+ return encodeCanvasToBlob(canvas, { mimeType: mime, quality });
845
874
  }
846
875
  async function loadImageToStore(file, store) {
847
876
  const originalBlob = file;
@@ -909,21 +938,6 @@ async function renderPageToCanvas(page, scale) {
909
938
  await page.render({ canvas, viewport }).promise;
910
939
  return { canvas, width: w, height: h };
911
940
  }
912
- function canvasToBlob2(canvas, mime, quality) {
913
- return new Promise((resolve, reject) => {
914
- canvas.toBlob(
915
- (blob) => {
916
- if (!blob) {
917
- reject(new Error("Could not encode blob"));
918
- return;
919
- }
920
- resolve(blob);
921
- },
922
- mime,
923
- quality
924
- );
925
- });
926
- }
927
941
  function normalizePdfPageNumbers(pageNumbers, pageCount) {
928
942
  if (!pageNumbers || pageNumbers.length === 0) {
929
943
  return Array.from({ length: pageCount }, (_, index) => index + 1);
@@ -985,7 +999,7 @@ async function loadPdfToStore(file, store, options) {
985
999
  const page = await pdf.getPage(pageNumber);
986
1000
  const { canvas, width, height } = await renderPageToCanvas(page, scale);
987
1001
  const mime = "image/png";
988
- const pageBlob = await canvasToBlob2(canvas, mime);
1002
+ const pageBlob = await encodeCanvasToBlob(canvas, { mimeType: mime });
989
1003
  const blobId = await store.storeOriginal(pageBlob);
990
1004
  const thumbnailBlobId = storeThumbnails ? await (async () => {
991
1005
  const thumbScale = Math.min(1, 256 / Math.max(width, height));
@@ -1000,7 +1014,9 @@ async function loadPdfToStore(file, store, options) {
1000
1014
  tCtx.imageSmoothingQuality = "high";
1001
1015
  tCtx.drawImage(canvas, 0, 0, tw, th);
1002
1016
  }
1003
- const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
1017
+ const thumbBlob = await encodeCanvasToBlob(thumbCanvas, {
1018
+ mimeType: mime
1019
+ });
1004
1020
  return await store.storeThumbnail(thumbBlob);
1005
1021
  })() : "";
1006
1022
  const pageResult = {
@@ -7398,6 +7414,40 @@ var VectorViewport = forwardRef(
7398
7414
  onWorldPointerLeaveRef.current = onWorldPointerLeave;
7399
7415
  const onPlacementPreviewChangeRef = useRef(onPlacementPreviewChange);
7400
7416
  onPlacementPreviewChangeRef.current = onPlacementPreviewChange;
7417
+ const directRemoteStrokePreviewRef = useRef(false);
7418
+ const remoteStrokePreviewFrameRef = useRef(null);
7419
+ const pendingRemoteStrokePreviewRef = useRef(null);
7420
+ const flushRemoteStrokePreview = useCallback(() => {
7421
+ remoteStrokePreviewFrameRef.current = null;
7422
+ const pending = pendingRemoteStrokePreviewRef.current;
7423
+ if (!pending) return;
7424
+ onPlacementPreviewChangeRef.current?.({
7425
+ kind: "stroke",
7426
+ tool: pending.tool,
7427
+ points: pending.points
7428
+ });
7429
+ }, []);
7430
+ const emitRemoteStrokePreview = useCallback(
7431
+ (tool, points) => {
7432
+ if (tool === "laser") return;
7433
+ directRemoteStrokePreviewRef.current = true;
7434
+ pendingRemoteStrokePreviewRef.current = { tool, points };
7435
+ if (remoteStrokePreviewFrameRef.current != null) return;
7436
+ remoteStrokePreviewFrameRef.current = requestAnimationFrame(
7437
+ flushRemoteStrokePreview
7438
+ );
7439
+ },
7440
+ [flushRemoteStrokePreview]
7441
+ );
7442
+ const emitRemoteStrokePreviewClear = useCallback(() => {
7443
+ if (remoteStrokePreviewFrameRef.current != null) {
7444
+ cancelAnimationFrame(remoteStrokePreviewFrameRef.current);
7445
+ remoteStrokePreviewFrameRef.current = null;
7446
+ }
7447
+ pendingRemoteStrokePreviewRef.current = null;
7448
+ directRemoteStrokePreviewRef.current = false;
7449
+ onPlacementPreviewChangeRef.current?.(null);
7450
+ }, []);
7401
7451
  const overlayCameraTickRef = useRef(false);
7402
7452
  overlayCameraTickRef.current = interactive || remotePresence != null && remotePresence.length > 0 || presenceOverlay != null;
7403
7453
  const pruneEraserTrail = useCallback(
@@ -7516,6 +7566,7 @@ var VectorViewport = forwardRef(
7516
7566
  if (itemId) {
7517
7567
  renderSceneWithLivePenStroke(null);
7518
7568
  }
7569
+ emitRemoteStrokePreviewClear();
7519
7570
  setPlacementPreview(null);
7520
7571
  commitCompletedStroke({
7521
7572
  tool,
@@ -7527,6 +7578,7 @@ var VectorViewport = forwardRef(
7527
7578
  },
7528
7579
  [
7529
7580
  commitCompletedStroke,
7581
+ emitRemoteStrokePreviewClear,
7530
7582
  releaseInteractionPointer,
7531
7583
  renderSceneWithLivePenStroke
7532
7584
  ]
@@ -7638,8 +7690,18 @@ var VectorViewport = forwardRef(
7638
7690
  };
7639
7691
  }, [onWorldPointerMove]);
7640
7692
  useEffect(() => {
7693
+ if (directRemoteStrokePreviewRef.current && placementPreview === null) {
7694
+ return;
7695
+ }
7641
7696
  onPlacementPreviewChangeRef.current?.(placementPreview);
7642
7697
  }, [placementPreview]);
7698
+ useEffect(() => {
7699
+ return () => {
7700
+ if (remoteStrokePreviewFrameRef.current != null) {
7701
+ cancelAnimationFrame(remoteStrokePreviewFrameRef.current);
7702
+ }
7703
+ };
7704
+ }, []);
7643
7705
  useEffect(() => {
7644
7706
  const scene = sceneRef.current;
7645
7707
  if (scene) {
@@ -8633,6 +8695,7 @@ var VectorViewport = forwardRef(
8633
8695
  setPlacementPreview(null);
8634
8696
  } else if (directPenStroke) {
8635
8697
  setPlacementPreview(null);
8698
+ emitRemoteStrokePreview(tool, [startPoint]);
8636
8699
  } else {
8637
8700
  setPlacementPreview({
8638
8701
  kind: "stroke",
@@ -8674,6 +8737,7 @@ var VectorViewport = forwardRef(
8674
8737
  [
8675
8738
  applePencilNav,
8676
8739
  captureInteractionPointer,
8740
+ emitRemoteStrokePreview,
8677
8741
  finalizeStrokeDragState,
8678
8742
  renderSceneWithLivePenStroke,
8679
8743
  screenToWorld
@@ -8732,6 +8796,7 @@ var VectorViewport = forwardRef(
8732
8796
  activeInteractionPointerIdRef.current = e.pointerId;
8733
8797
  activeInteractionPointerTargetRef.current = null;
8734
8798
  setPlacementPreview(null);
8799
+ emitRemoteStrokePreview(tool, [startPoint]);
8735
8800
  debugApplePencilPointer("native-pointerdown", {
8736
8801
  pointerType: e.pointerType,
8737
8802
  pointerId: e.pointerId,
@@ -8749,6 +8814,7 @@ var VectorViewport = forwardRef(
8749
8814
  };
8750
8815
  }, [
8751
8816
  applePencilNav,
8817
+ emitRemoteStrokePreview,
8752
8818
  finalizeStrokeDragState,
8753
8819
  interactive,
8754
8820
  renderSceneWithLivePenStroke,
@@ -8832,6 +8898,7 @@ var VectorViewport = forwardRef(
8832
8898
  activeInteractionPointerTargetRef.current = null;
8833
8899
  activeInteractionTouchIdRef.current = touch.identifier;
8834
8900
  setPlacementPreview(null);
8901
+ emitRemoteStrokePreview(tool, [startPoint]);
8835
8902
  debugApplePencilPointer("touchstart-stroke", {
8836
8903
  touchId: touch.identifier,
8837
8904
  touchType: touchKind(touch),
@@ -8863,6 +8930,7 @@ var VectorViewport = forwardRef(
8863
8930
  renderSceneWithLivePenStroke(item);
8864
8931
  }
8865
8932
  setPlacementPreview(null);
8933
+ emitRemoteStrokePreview(st.tool, interpolated);
8866
8934
  }
8867
8935
  debugApplePencilPointer("touchmove-stroke", {
8868
8936
  touchId: touch.identifier,
@@ -8925,6 +8993,7 @@ var VectorViewport = forwardRef(
8925
8993
  };
8926
8994
  }, [
8927
8995
  applePencilNav,
8996
+ emitRemoteStrokePreview,
8928
8997
  finalizeStrokeDragState,
8929
8998
  interactive,
8930
8999
  renderSceneWithLivePenStroke,
@@ -8979,6 +9048,7 @@ var VectorViewport = forwardRef(
8979
9048
  renderSceneWithLivePenStroke(item);
8980
9049
  }
8981
9050
  setPlacementPreview(null);
9051
+ emitRemoteStrokePreview(st.tool, interpolated);
8982
9052
  return;
8983
9053
  }
8984
9054
  if (st.tool === "laser") {
@@ -9120,6 +9190,9 @@ var VectorViewport = forwardRef(
9120
9190
  }
9121
9191
  const cam = cameraRef.current;
9122
9192
  if (!cam) {
9193
+ if (st.kind === "stroke") {
9194
+ emitRemoteStrokePreviewClear();
9195
+ }
9123
9196
  if (st.kind === "erase") {
9124
9197
  eraserPreviewIdsRef.current.clear();
9125
9198
  setEraserPreviewIds([]);
@@ -9351,6 +9424,8 @@ var VectorViewport = forwardRef(
9351
9424
  document.removeEventListener("pointercancel", onUp);
9352
9425
  };
9353
9426
  }, [
9427
+ emitRemoteStrokePreview,
9428
+ emitRemoteStrokePreviewClear,
9354
9429
  interactive,
9355
9430
  pruneEraserTrail,
9356
9431
  pruneLaserTrail,