canvu-react 0.4.45 → 0.4.46

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/native.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import getStroke from 'perfect-freehand';
2
2
  import { Group, Canvas, Rect, Circle, Path, RoundedRect, Oval, DashPathEffect, Line, vec, matchFont, Text as Text$1, Image } from '@shopify/react-native-skia';
3
- import { memo, forwardRef, useState, useRef, useCallback, useEffect, useMemo, useImperativeHandle } from 'react';
3
+ import { memo, forwardRef, useState, useRef, useEffect, useCallback, useMemo, useImperativeHandle } from 'react';
4
4
  import { StyleSheet, PanResponder, View, Modal, Text, TextInput, Pressable, ScrollView, Image as Image$1 } from 'react-native';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
@@ -1863,6 +1863,63 @@ function smoothFreehandPointsToPathD(points) {
1863
1863
  return d;
1864
1864
  }
1865
1865
 
1866
+ // src/native/native-freehand-payload-cache.ts
1867
+ var MAX_NATIVE_FREEHAND_PAYLOAD_CACHE_SIZE = 800;
1868
+ var pathSignatureCache = /* @__PURE__ */ new WeakMap();
1869
+ var payloadCache = /* @__PURE__ */ new Map();
1870
+ function hashNumber(hash, value) {
1871
+ const normalizedValue = Math.round(value * 100);
1872
+ return Math.imul(hash ^ normalizedValue, 16777619) >>> 0;
1873
+ }
1874
+ function getPathPointsSignature(pathPointsLocal) {
1875
+ const cachedSignature = pathSignatureCache.get(pathPointsLocal);
1876
+ if (cachedSignature) return cachedSignature;
1877
+ const hash = pathPointsLocal.reduce((currentHash, point) => {
1878
+ const withX = hashNumber(currentHash, point.x);
1879
+ const withY = hashNumber(withX, point.y);
1880
+ return hashNumber(withY, point.pressure ?? -1);
1881
+ }, 2166136261);
1882
+ const signature = `${pathPointsLocal.length}:${hash}`;
1883
+ pathSignatureCache.set(pathPointsLocal, signature);
1884
+ return signature;
1885
+ }
1886
+ function getStyleSignature(style) {
1887
+ return [
1888
+ style.stroke,
1889
+ style.strokeWidth,
1890
+ style.strokeOpacity ?? "",
1891
+ style.strokeDash ?? ""
1892
+ ].join(":");
1893
+ }
1894
+ function getPayloadCacheKey(input) {
1895
+ return [
1896
+ input.itemId,
1897
+ input.toolKind,
1898
+ getStyleSignature(input.style),
1899
+ getPathPointsSignature(input.pathPointsLocal)
1900
+ ].join("|");
1901
+ }
1902
+ function storePayload(key, payload) {
1903
+ payloadCache.set(key, payload);
1904
+ if (payloadCache.size > MAX_NATIVE_FREEHAND_PAYLOAD_CACHE_SIZE) {
1905
+ const oldestKey = payloadCache.keys().next().value;
1906
+ if (oldestKey !== void 0) {
1907
+ payloadCache.delete(oldestKey);
1908
+ }
1909
+ }
1910
+ return payload;
1911
+ }
1912
+ function getNativeFreehandSvgPayload(input) {
1913
+ const key = getPayloadCacheKey(input);
1914
+ if (payloadCache.has(key)) {
1915
+ return payloadCache.get(key) ?? null;
1916
+ }
1917
+ return storePayload(
1918
+ key,
1919
+ computeFreehandSvgPayload(input.pathPointsLocal, input.style, input.toolKind)
1920
+ );
1921
+ }
1922
+
1866
1923
  // src/native/native-link-card.ts
1867
1924
  function linkHostname(href) {
1868
1925
  try {
@@ -2865,7 +2922,12 @@ function NativeShapeRenderer({ item }) {
2865
2922
  )) });
2866
2923
  }
2867
2924
  if ((k === "draw" || k === "pencil" || k === "brush" || k === "marker") && item.pathPointsLocal && item.pathPointsLocal.length > 0) {
2868
- const payload = computeFreehandSvgPayload(item.pathPointsLocal, style, k);
2925
+ const payload = getNativeFreehandSvgPayload({
2926
+ itemId: item.id,
2927
+ pathPointsLocal: item.pathPointsLocal,
2928
+ style,
2929
+ toolKind: k
2930
+ });
2869
2931
  if (!payload) return null;
2870
2932
  const color = rgba(style.stroke, style.strokeOpacity);
2871
2933
  if (payload.kind === "circle") {
@@ -5169,6 +5231,36 @@ function hitTestNativeRemotePresence(peers, camera, point) {
5169
5231
  }
5170
5232
  return null;
5171
5233
  }
5234
+
5235
+ // src/native/native-transient-items.ts
5236
+ function moveNativeTransientItems({
5237
+ items,
5238
+ snapshots,
5239
+ dx,
5240
+ dy
5241
+ }) {
5242
+ return items.map((item) => {
5243
+ const snapshot = snapshots[item.id];
5244
+ if (!snapshot) return item;
5245
+ return {
5246
+ ...snapshot,
5247
+ x: snapshot.x + dx,
5248
+ y: snapshot.y + dy,
5249
+ bounds: {
5250
+ ...snapshot.bounds,
5251
+ x: snapshot.bounds.x + dx,
5252
+ y: snapshot.bounds.y + dy
5253
+ }
5254
+ };
5255
+ });
5256
+ }
5257
+ function replaceNativeTransientItem({
5258
+ items,
5259
+ itemId,
5260
+ item: replacement
5261
+ }) {
5262
+ return items.map((item) => item.id === itemId ? replacement : item);
5263
+ }
5172
5264
  var DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS = {
5173
5265
  title: "Add link",
5174
5266
  description: "Paste the link you want to add to the board.",
@@ -5315,13 +5407,41 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5315
5407
  customPlacementsRef.current = customPlacements;
5316
5408
  const onSelectionChangeRef = useRef(onSelectionChange);
5317
5409
  onSelectionChangeRef.current = onSelectionChange;
5318
- const itemsRef = useRef(items);
5319
- itemsRef.current = items;
5410
+ const [transientItems, setTransientItems] = useState(
5411
+ null
5412
+ );
5413
+ const transientItemsRef = useRef(null);
5414
+ const activeItems = transientItems ?? items;
5415
+ const itemsRef = useRef(activeItems);
5416
+ itemsRef.current = activeItems;
5320
5417
  const selectedIdsRef = useRef(selectedIds);
5321
5418
  selectedIdsRef.current = selectedIds;
5322
5419
  const remotePresenceRef = useRef(remotePresence);
5323
5420
  remotePresenceRef.current = remotePresence;
5324
5421
  const dragStateRef = useRef({ kind: "idle" });
5422
+ useEffect(() => {
5423
+ const committedItems = items;
5424
+ if (transientItemsRef.current === null && itemsRef.current === committedItems) {
5425
+ return;
5426
+ }
5427
+ transientItemsRef.current = null;
5428
+ setTransientItems(null);
5429
+ }, [items]);
5430
+ const setTransientItemsPreview = useCallback((nextItems) => {
5431
+ transientItemsRef.current = nextItems;
5432
+ setTransientItems(nextItems);
5433
+ }, []);
5434
+ const clearTransientItemsPreview = useCallback(() => {
5435
+ transientItemsRef.current = null;
5436
+ setTransientItems(null);
5437
+ }, []);
5438
+ const commitTransientItemsPreview = useCallback(() => {
5439
+ const nextItems = transientItemsRef.current;
5440
+ const change = onItemsChangeRef.current;
5441
+ if (!nextItems || !change) return false;
5442
+ change(nextItems);
5443
+ return true;
5444
+ }, []);
5325
5445
  const [placementPreview, setPlacementPreviewState] = useState(null);
5326
5446
  const setRealtimePlacementPreview = useCallback(
5327
5447
  (nextPreview) => {
@@ -5462,8 +5582,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5462
5582
  const hideToolCursor = useCallback(() => {
5463
5583
  }, []);
5464
5584
  const selectedItems = useMemo(
5465
- () => items.filter((it) => selectedIds.includes(it.id)),
5466
- [items, selectedIds]
5585
+ () => activeItems.filter((item) => selectedIds.includes(item.id)),
5586
+ [activeItems, selectedIds]
5467
5587
  );
5468
5588
  const selectedStyleInspectorState = useMemo(() => {
5469
5589
  const styleableItems = selectedItems.filter(
@@ -5479,10 +5599,10 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5479
5599
  };
5480
5600
  }, [selectedItems]);
5481
5601
  const sceneItems = useMemo(() => {
5482
- if (eraserPreviewIds.length === 0) return items;
5602
+ if (eraserPreviewIds.length === 0) return activeItems;
5483
5603
  const hidden = new Set(eraserPreviewIds);
5484
- return items.filter((it) => !hidden.has(it.id));
5485
- }, [items, eraserPreviewIds]);
5604
+ return activeItems.filter((item) => !hidden.has(item.id));
5605
+ }, [activeItems, eraserPreviewIds]);
5486
5606
  const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked && supportsNativeResizeHandles(selectedItems[0]);
5487
5607
  const patchSelectedItemsStrokeStyle = useCallback(
5488
5608
  (patch) => {
@@ -5780,21 +5900,13 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5780
5900
  const dy = worldY - st.startWorld.y;
5781
5901
  const change = onItemsChangeRef.current;
5782
5902
  if (!change) return;
5783
- const nextList = itemsRef.current.map((it) => {
5784
- const snap = st.snapshots[it.id];
5785
- if (!snap) return it;
5786
- return {
5787
- ...snap,
5788
- x: snap.x + dx,
5789
- y: snap.y + dy,
5790
- bounds: {
5791
- ...snap.bounds,
5792
- x: snap.bounds.x + dx,
5793
- y: snap.bounds.y + dy
5794
- }
5795
- };
5903
+ const nextItems = moveNativeTransientItems({
5904
+ items: itemsRef.current,
5905
+ snapshots: st.snapshots,
5906
+ dx,
5907
+ dy
5796
5908
  });
5797
- change(nextList);
5909
+ setTransientItemsPreview(nextItems);
5798
5910
  return;
5799
5911
  }
5800
5912
  if (st.kind === "rotate") {
@@ -5807,7 +5919,13 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5807
5919
  st.startPointerAngleRad,
5808
5920
  angle
5809
5921
  );
5810
- change(itemsRef.current.map((item) => item.id === st.id ? next : item));
5922
+ setTransientItemsPreview(
5923
+ replaceNativeTransientItem({
5924
+ items: itemsRef.current,
5925
+ itemId: st.id,
5926
+ item: next
5927
+ })
5928
+ );
5811
5929
  return;
5812
5930
  }
5813
5931
  if (st.kind === "resize") {
@@ -5817,7 +5935,13 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5817
5935
  x: worldX,
5818
5936
  y: worldY
5819
5937
  });
5820
- change(itemsRef.current.map((item) => item.id === st.id ? next : item));
5938
+ setTransientItemsPreview(
5939
+ replaceNativeTransientItem({
5940
+ items: itemsRef.current,
5941
+ itemId: st.id,
5942
+ item: next
5943
+ })
5944
+ );
5821
5945
  return;
5822
5946
  }
5823
5947
  if (st.kind === "marquee") {
@@ -5867,6 +5991,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5867
5991
  requestRender,
5868
5992
  screenToWorld,
5869
5993
  setRealtimePlacementPreview,
5994
+ setTransientItemsPreview,
5870
5995
  updateToolCursorPoint
5871
5996
  ]
5872
5997
  );
@@ -5908,10 +6033,12 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5908
6033
  }
5909
6034
  if (st.kind === "move") {
5910
6035
  dragStateRef.current = { kind: "idle" };
6036
+ commitTransientItemsPreview();
5911
6037
  return;
5912
6038
  }
5913
6039
  if (st.kind === "resize" || st.kind === "rotate") {
5914
6040
  dragStateRef.current = { kind: "idle" };
6041
+ commitTransientItemsPreview();
5915
6042
  return;
5916
6043
  }
5917
6044
  if (st.kind === "marquee") {
@@ -6121,6 +6248,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6121
6248
  requestSelectToolAfterUse,
6122
6249
  screenToWorld,
6123
6250
  setRealtimePlacementPreview,
6251
+ commitTransientItemsPreview,
6124
6252
  updateToolCursorPoint
6125
6253
  ]
6126
6254
  );
@@ -6211,6 +6339,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6211
6339
  notifyWorldPointerLeave();
6212
6340
  dragStateRef.current = { kind: "idle" };
6213
6341
  setRealtimePlacementPreview(null);
6342
+ clearTransientItemsPreview();
6214
6343
  setLaserTrail([]);
6215
6344
  setEraserTrail([]);
6216
6345
  setEraserPreviewIds([]);
@@ -6224,7 +6353,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6224
6353
  requestRender,
6225
6354
  hideToolCursor,
6226
6355
  notifyWorldPointerLeave,
6227
- setRealtimePlacementPreview
6356
+ setRealtimePlacementPreview,
6357
+ clearTransientItemsPreview
6228
6358
  ]
6229
6359
  );
6230
6360
  useImperativeHandle(
@@ -6294,8 +6424,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6294
6424
  placementPreview,
6295
6425
  laserTrail,
6296
6426
  eraserTrail,
6297
- eraserPreviewItems: items.filter(
6298
- (it) => eraserPreviewIds.includes(it.id)
6427
+ eraserPreviewItems: activeItems.filter(
6428
+ (item) => eraserPreviewIds.includes(item.id)
6299
6429
  ),
6300
6430
  previewStrokeStyle: strokeStyleState,
6301
6431
  remotePresence