canvu-react 0.4.52 → 0.4.54

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
@@ -1193,6 +1193,9 @@ function cloneVectorSceneItemWithNewId(item) {
1193
1193
  }
1194
1194
  return next;
1195
1195
  }
1196
+ function cloneVectorSceneItemsWithNewIds(items) {
1197
+ return items.map(cloneVectorSceneItemWithNewId);
1198
+ }
1196
1199
 
1197
1200
  // src/scene/managed-images.ts
1198
1201
  var MANAGED_KEY = "managed";
@@ -1256,8 +1259,68 @@ function rotateManagedImage(items, id) {
1256
1259
  function deleteManagedImage(items, id) {
1257
1260
  return restackManagedImages(items.filter((i) => i.id !== id));
1258
1261
  }
1262
+ function reorderManagedImages(items, orderedManagedIds) {
1263
+ const managedSlots = [];
1264
+ for (let i = 0; i < items.length; i++) {
1265
+ const item = items[i];
1266
+ if (item && isManagedImage(item)) managedSlots.push(i);
1267
+ }
1268
+ if (managedSlots.length !== orderedManagedIds.length) {
1269
+ return [...items];
1270
+ }
1271
+ const byId = new Map(items.map((i) => [i.id, i]));
1272
+ const next = [...items];
1273
+ managedSlots.forEach((slot, k) => {
1274
+ const orderedId = orderedManagedIds[k];
1275
+ if (orderedId === void 0) return;
1276
+ const replacement = byId.get(orderedId);
1277
+ if (replacement) next[slot] = replacement;
1278
+ });
1279
+ return restackManagedImages(next);
1280
+ }
1281
+
1282
+ // src/native/native-images-menu-reorder.ts
1283
+ function moveNativeManagedImageId(managedIds, fromIndex, toIndex) {
1284
+ const next = [...managedIds];
1285
+ if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= next.length || toIndex >= next.length) {
1286
+ return next;
1287
+ }
1288
+ const [item] = next.splice(fromIndex, 1);
1289
+ if (item === void 0) return next;
1290
+ next.splice(toIndex, 0, item);
1291
+ return next;
1292
+ }
1293
+ function getNativeImagesMenuReorderIndex({
1294
+ activeId,
1295
+ managedIds,
1296
+ rowLayouts,
1297
+ dragDeltaY
1298
+ }) {
1299
+ const currentIndex = managedIds.indexOf(activeId);
1300
+ if (currentIndex < 0) return -1;
1301
+ const layoutById = new Map(rowLayouts.map((layout) => [layout.id, layout]));
1302
+ const activeLayout = layoutById.get(activeId);
1303
+ if (!activeLayout) return currentIndex;
1304
+ const activeCenterY = activeLayout.y + activeLayout.height / 2 + dragDeltaY;
1305
+ let nextIndex = currentIndex;
1306
+ let closestDistance = Infinity;
1307
+ for (let index = 0; index < managedIds.length; index++) {
1308
+ const id = managedIds[index];
1309
+ if (id === void 0) continue;
1310
+ const layout = layoutById.get(id);
1311
+ if (!layout) continue;
1312
+ const centerY = layout.y + layout.height / 2;
1313
+ const distance = Math.abs(activeCenterY - centerY);
1314
+ if (distance < closestDistance) {
1315
+ closestDistance = distance;
1316
+ nextIndex = index;
1317
+ }
1318
+ }
1319
+ return nextIndex;
1320
+ }
1259
1321
  var defaultLabels = {
1260
1322
  title: "Images",
1323
+ dragHandle: "Drag to reorder",
1261
1324
  focus: "Focus on canvas",
1262
1325
  duplicate: "Duplicate",
1263
1326
  rotate: "Rotate",
@@ -1329,61 +1392,110 @@ function NativeImagesMenuRow({
1329
1392
  item,
1330
1393
  items,
1331
1394
  labels,
1395
+ isDragging,
1396
+ isDropTarget,
1397
+ dragDeltaY,
1332
1398
  onItemsChange,
1333
1399
  onFocusItem,
1400
+ onDragStart,
1401
+ onDragMove,
1402
+ onDragEnd,
1403
+ onRowLayout,
1334
1404
  getImageUri,
1335
1405
  renderThumbnail,
1336
1406
  rowStyle,
1337
1407
  actionButtonStyle
1338
1408
  }) {
1339
1409
  const imageUri = getImageUri(item);
1340
- return /* @__PURE__ */ jsxs(View, { style: [styles.row, rowStyle], children: [
1341
- /* @__PURE__ */ jsx(View, { style: styles.handle, children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "grip", color: "#94a3b8" }) }),
1342
- /* @__PURE__ */ jsx(
1343
- Pressable,
1344
- {
1345
- accessibilityRole: "button",
1346
- accessibilityLabel: labels.focus,
1347
- disabled: !onFocusItem,
1348
- onPress: () => onFocusItem?.(item),
1349
- style: styles.thumbnailButton,
1350
- children: /* @__PURE__ */ jsxs(View, { style: styles.thumbnailBox, children: [
1351
- renderThumbnail ? renderThumbnail(item) : null,
1352
- !renderThumbnail && imageUri ? /* @__PURE__ */ jsx(Image$1, { source: { uri: imageUri }, style: styles.thumbnailImage }) : null
1410
+ const panResponder = useMemo(
1411
+ () => PanResponder.create({
1412
+ onStartShouldSetPanResponder: () => true,
1413
+ onMoveShouldSetPanResponder: () => true,
1414
+ onPanResponderGrant: () => onDragStart(item.id),
1415
+ onPanResponderMove: (_event, gestureState) => onDragMove(item.id, gestureState.dy),
1416
+ onPanResponderRelease: (_event, gestureState) => onDragEnd(item.id, gestureState.dy),
1417
+ onPanResponderTerminate: (_event, gestureState) => onDragEnd(item.id, gestureState.dy),
1418
+ onPanResponderTerminationRequest: () => false,
1419
+ onShouldBlockNativeResponder: () => true
1420
+ }),
1421
+ [item.id, onDragEnd, onDragMove, onDragStart]
1422
+ );
1423
+ const onLayout = useCallback(
1424
+ (event) => {
1425
+ const { height, y } = event.nativeEvent.layout;
1426
+ onRowLayout({ id: item.id, y, height });
1427
+ },
1428
+ [item.id, onRowLayout]
1429
+ );
1430
+ return /* @__PURE__ */ jsxs(
1431
+ View,
1432
+ {
1433
+ onLayout,
1434
+ style: [
1435
+ styles.row,
1436
+ rowStyle,
1437
+ isDropTarget && styles.dropTargetRow,
1438
+ isDragging && styles.draggingRow,
1439
+ isDragging && { transform: [{ translateY: dragDeltaY }] }
1440
+ ],
1441
+ children: [
1442
+ /* @__PURE__ */ jsx(
1443
+ View,
1444
+ {
1445
+ accessibilityRole: "button",
1446
+ accessibilityLabel: labels.dragHandle,
1447
+ style: [styles.handle, isDragging && styles.handleDragging],
1448
+ ...panResponder.panHandlers,
1449
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "grip", color: "#94a3b8" })
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ jsx(
1453
+ Pressable,
1454
+ {
1455
+ accessibilityRole: "button",
1456
+ accessibilityLabel: labels.focus,
1457
+ disabled: !onFocusItem,
1458
+ onPress: () => onFocusItem?.(item),
1459
+ style: styles.thumbnailButton,
1460
+ children: /* @__PURE__ */ jsxs(View, { style: styles.thumbnailBox, children: [
1461
+ renderThumbnail ? renderThumbnail(item) : null,
1462
+ !renderThumbnail && imageUri ? /* @__PURE__ */ jsx(Image$1, { source: { uri: imageUri }, style: styles.thumbnailImage }) : null
1463
+ ] })
1464
+ }
1465
+ ),
1466
+ /* @__PURE__ */ jsxs(View, { style: styles.actionsColumn, children: [
1467
+ /* @__PURE__ */ jsx(
1468
+ NativeImagesMenuAction,
1469
+ {
1470
+ label: labels.duplicate,
1471
+ onPress: () => onItemsChange(copyManagedImage(items, item.id)),
1472
+ style: actionButtonStyle,
1473
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "copy" })
1474
+ }
1475
+ ),
1476
+ /* @__PURE__ */ jsx(
1477
+ NativeImagesMenuAction,
1478
+ {
1479
+ label: labels.rotate,
1480
+ onPress: () => onItemsChange(rotateManagedImage(items, item.id)),
1481
+ style: actionButtonStyle,
1482
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "rotate" })
1483
+ }
1484
+ ),
1485
+ /* @__PURE__ */ jsx(
1486
+ NativeImagesMenuAction,
1487
+ {
1488
+ label: labels.delete,
1489
+ danger: true,
1490
+ onPress: () => onItemsChange(deleteManagedImage(items, item.id)),
1491
+ style: actionButtonStyle,
1492
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "trash", color: "#b91c1c" })
1493
+ }
1494
+ )
1353
1495
  ] })
1354
- }
1355
- ),
1356
- /* @__PURE__ */ jsxs(View, { style: styles.actionsColumn, children: [
1357
- /* @__PURE__ */ jsx(
1358
- NativeImagesMenuAction,
1359
- {
1360
- label: labels.duplicate,
1361
- onPress: () => onItemsChange(copyManagedImage(items, item.id)),
1362
- style: actionButtonStyle,
1363
- children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "copy" })
1364
- }
1365
- ),
1366
- /* @__PURE__ */ jsx(
1367
- NativeImagesMenuAction,
1368
- {
1369
- label: labels.rotate,
1370
- onPress: () => onItemsChange(rotateManagedImage(items, item.id)),
1371
- style: actionButtonStyle,
1372
- children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "rotate" })
1373
- }
1374
- ),
1375
- /* @__PURE__ */ jsx(
1376
- NativeImagesMenuAction,
1377
- {
1378
- label: labels.delete,
1379
- danger: true,
1380
- onPress: () => onItemsChange(deleteManagedImage(items, item.id)),
1381
- style: actionButtonStyle,
1382
- children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "trash", color: "#b91c1c" })
1383
- }
1384
- )
1385
- ] })
1386
- ] });
1496
+ ]
1497
+ }
1498
+ );
1387
1499
  }
1388
1500
  function NativeImagesMenu({
1389
1501
  items,
@@ -1400,7 +1512,69 @@ function NativeImagesMenu({
1400
1512
  renderThumbnail
1401
1513
  }) {
1402
1514
  const managed = useMemo(() => items.filter(isManagedImage), [items]);
1515
+ const managedIds = useMemo(() => managed.map((item) => item.id), [managed]);
1403
1516
  const [collapsed, setCollapsed] = useState(!defaultOpen);
1517
+ const [dragState, setDragState] = useState(null);
1518
+ const rowLayoutsRef = useRef(/* @__PURE__ */ new Map());
1519
+ const getRowLayouts = useCallback(
1520
+ () => managedIds.map((id) => rowLayoutsRef.current.get(id)).filter((layout) => layout != null),
1521
+ [managedIds]
1522
+ );
1523
+ const handleRowLayout = useCallback((layout) => {
1524
+ rowLayoutsRef.current.set(layout.id, layout);
1525
+ }, []);
1526
+ const handleDragStart = useCallback(
1527
+ (id) => {
1528
+ const fromIndex = managedIds.indexOf(id);
1529
+ if (fromIndex < 0) return;
1530
+ setDragState({
1531
+ activeId: id,
1532
+ fromIndex,
1533
+ currentIndex: fromIndex,
1534
+ dragDeltaY: 0
1535
+ });
1536
+ },
1537
+ [managedIds]
1538
+ );
1539
+ const handleDragMove = useCallback(
1540
+ (id, dragDeltaY) => {
1541
+ setDragState((current) => {
1542
+ if (!current || current.activeId !== id) return current;
1543
+ const currentIndex = getNativeImagesMenuReorderIndex({
1544
+ activeId: id,
1545
+ managedIds,
1546
+ rowLayouts: getRowLayouts(),
1547
+ dragDeltaY
1548
+ });
1549
+ return {
1550
+ ...current,
1551
+ currentIndex: currentIndex >= 0 ? currentIndex : current.fromIndex,
1552
+ dragDeltaY
1553
+ };
1554
+ });
1555
+ },
1556
+ [getRowLayouts, managedIds]
1557
+ );
1558
+ const handleDragEnd = useCallback(
1559
+ (id, dragDeltaY) => {
1560
+ const fromIndex = managedIds.indexOf(id);
1561
+ const currentIndex = getNativeImagesMenuReorderIndex({
1562
+ activeId: id,
1563
+ managedIds,
1564
+ rowLayouts: getRowLayouts(),
1565
+ dragDeltaY
1566
+ });
1567
+ setDragState(null);
1568
+ if (fromIndex < 0 || currentIndex < 0 || fromIndex === currentIndex) return;
1569
+ const orderedIds = moveNativeManagedImageId(
1570
+ managedIds,
1571
+ fromIndex,
1572
+ currentIndex
1573
+ );
1574
+ onItemsChange(reorderManagedImages(items, orderedIds));
1575
+ },
1576
+ [getRowLayouts, items, managedIds, onItemsChange]
1577
+ );
1404
1578
  if (managed.length === 0) return null;
1405
1579
  const resolvedLabels = resolveLabels(labels);
1406
1580
  if (collapsed) {
@@ -1445,15 +1619,23 @@ function NativeImagesMenu({
1445
1619
  {
1446
1620
  style: styles.listScroll,
1447
1621
  contentContainerStyle: styles.listContent,
1622
+ scrollEnabled: dragState == null,
1448
1623
  showsVerticalScrollIndicator: false,
1449
- children: managed.map((item) => /* @__PURE__ */ jsx(
1624
+ children: managed.map((item, index) => /* @__PURE__ */ jsx(
1450
1625
  NativeImagesMenuRow,
1451
1626
  {
1452
1627
  item,
1453
1628
  items,
1454
1629
  labels: resolvedLabels,
1630
+ isDragging: dragState?.activeId === item.id,
1631
+ isDropTarget: dragState != null && dragState.activeId !== item.id && dragState.currentIndex === index,
1632
+ dragDeltaY: dragState?.activeId === item.id ? dragState.dragDeltaY : 0,
1455
1633
  onItemsChange,
1456
1634
  onFocusItem,
1635
+ onDragStart: handleDragStart,
1636
+ onDragMove: handleDragMove,
1637
+ onDragEnd: handleDragEnd,
1638
+ onRowLayout: handleRowLayout,
1457
1639
  getImageUri,
1458
1640
  renderThumbnail,
1459
1641
  rowStyle,
@@ -1513,6 +1695,19 @@ var styles = StyleSheet.create({
1513
1695
  dangerActionButton: {
1514
1696
  backgroundColor: "#fef2f2"
1515
1697
  },
1698
+ draggingRow: {
1699
+ backgroundColor: "#eef2f7",
1700
+ elevation: 4,
1701
+ opacity: 0.85,
1702
+ zIndex: 1
1703
+ },
1704
+ dropTargetRow: {
1705
+ backgroundColor: "#f8fafc"
1706
+ },
1707
+ handleDragging: {
1708
+ backgroundColor: "#e2e8f0",
1709
+ borderRadius: 6
1710
+ },
1516
1711
  handle: {
1517
1712
  alignItems: "center",
1518
1713
  height: 128,
@@ -3072,22 +3267,6 @@ function resolveNativeStrokePreviewStyle(tool, previewStrokeStyle) {
3072
3267
  };
3073
3268
  }
3074
3269
 
3075
- // src/native/native-stroke-preview.ts
3076
- function buildNativeStrokePreviewPath(points) {
3077
- if (points.length < 2) return null;
3078
- const d = smoothFreehandPointsToPathD(points);
3079
- return d || null;
3080
- }
3081
- function buildNativeFreehandStrokePreviewPayload(points, style, tool) {
3082
- if (tool === "laser") return null;
3083
- return computeFreehandSvgPayload(
3084
- points.map((point) => ({ x: point.x, y: point.y })),
3085
- style,
3086
- tool,
3087
- true
3088
- );
3089
- }
3090
-
3091
3270
  // src/native/native-vector-interactions.ts
3092
3271
  var NATIVE_SELECTION_HANDLE_HIT_RADIUS_PX = 24;
3093
3272
  function nativeItemPlacementBounds(item) {
@@ -3155,6 +3334,26 @@ function pointInNativeSelectedItemBounds(item, worldPoint) {
3155
3334
  );
3156
3335
  return local.x >= 0 && local.x <= bounds.width && local.y >= 0 && local.y <= bounds.height;
3157
3336
  }
3337
+ function resolveNativeSelectionContextMenuRequest({
3338
+ interactive,
3339
+ toolId,
3340
+ selectedItems,
3341
+ worldPoint,
3342
+ screenPoint
3343
+ }) {
3344
+ if (!interactive || toolId !== "select") return null;
3345
+ const editableSelectedItems = selectedItems.filter((item) => !item.locked);
3346
+ if (editableSelectedItems.length === 0) return null;
3347
+ const pressedSelectedItem = editableSelectedItems.some(
3348
+ (item) => pointInNativeSelectedItemBounds(item, worldPoint)
3349
+ );
3350
+ if (!pressedSelectedItem) return null;
3351
+ return {
3352
+ itemIds: editableSelectedItems.map((item) => item.id),
3353
+ x: screenPoint.x,
3354
+ y: screenPoint.y
3355
+ };
3356
+ }
3158
3357
  function resolveNativeCustomPlacement(toolId, customPlacement, customPlacements) {
3159
3358
  if (customPlacement?.toolId === toolId) return customPlacement;
3160
3359
  return customPlacements?.find((placement) => placement.toolId === toolId) ?? null;
@@ -3459,90 +3658,58 @@ function NativeInteractionOverlay({
3459
3658
  p.tool,
3460
3659
  p.style ?? previewStrokeStyle
3461
3660
  );
3462
- const strokeColor = colorWithOpacity(
3463
- style.stroke,
3464
- isLaser ? 0.85 : style.strokeOpacity
3661
+ const payload = computeFreehandSvgPayload(
3662
+ p.points,
3663
+ style,
3664
+ isLaser ? "draw" : p.tool,
3665
+ p.points.length === 2
3465
3666
  );
3466
- if (!isLaser) {
3467
- const payload = buildNativeFreehandStrokePreviewPayload(
3468
- p.points,
3469
- style,
3470
- p.tool
3667
+ if (!payload) return null;
3668
+ if (payload.kind === "circle") {
3669
+ return /* @__PURE__ */ jsx(
3670
+ Circle,
3671
+ {
3672
+ cx: payload.cx,
3673
+ cy: payload.cy,
3674
+ r: payload.r,
3675
+ color: colorWithOpacity(payload.fill, payload.fillOpacity),
3676
+ style: "fill",
3677
+ antiAlias: true
3678
+ }
3471
3679
  );
3472
- if (payload?.kind === "circle") {
3473
- return /* @__PURE__ */ jsx(
3474
- Circle,
3475
- {
3476
- cx: payload.cx,
3477
- cy: payload.cy,
3478
- r: payload.r,
3479
- color: colorWithOpacity(payload.fill, payload.fillOpacity),
3480
- style: "fill",
3481
- antiAlias: true
3482
- }
3483
- );
3484
- }
3485
- if (payload?.kind === "fillPath") {
3486
- return /* @__PURE__ */ jsx(
3487
- Path,
3488
- {
3489
- path: payload.d,
3490
- color: colorWithOpacity(payload.fill, payload.fillOpacity),
3491
- style: "fill",
3492
- fillType: "winding",
3493
- antiAlias: true
3494
- }
3495
- );
3496
- }
3497
- if (payload?.kind === "strokePath") {
3498
- const intervals = dashIntervalsFromStrokeDasharray3(
3499
- payload.strokeDasharray
3500
- );
3501
- return /* @__PURE__ */ jsx(
3502
- Path,
3503
- {
3504
- path: payload.d,
3505
- color: colorWithOpacity(payload.stroke, payload.strokeOpacity),
3506
- style: "stroke",
3507
- strokeWidth: payload.strokeWidth,
3508
- strokeCap: "round",
3509
- strokeJoin: "round",
3510
- antiAlias: true,
3511
- children: intervals ? /* @__PURE__ */ jsx(DashPathEffect, { intervals }) : null
3512
- }
3513
- );
3514
- }
3515
- return null;
3516
3680
  }
3517
- if (p.points.length === 1) {
3518
- const point = p.points[0];
3519
- if (!point) return null;
3681
+ if (payload.kind === "fillPath") {
3520
3682
  return /* @__PURE__ */ jsx(
3521
- Circle,
3683
+ Path,
3522
3684
  {
3523
- cx: point.x,
3524
- cy: point.y,
3525
- r: Math.max(0.5, style.strokeWidth / 2),
3526
- color: strokeColor,
3685
+ path: payload.d,
3686
+ color: colorWithOpacity(payload.fill, payload.fillOpacity),
3527
3687
  style: "fill",
3688
+ fillType: "winding",
3528
3689
  antiAlias: true
3529
3690
  }
3530
3691
  );
3531
3692
  }
3532
- const path = buildNativeStrokePreviewPath(p.points);
3533
- if (!path) return null;
3534
- return /* @__PURE__ */ jsx(
3535
- Path,
3536
- {
3537
- path,
3538
- color: strokeColor,
3539
- style: "stroke",
3540
- strokeWidth: style.strokeWidth,
3541
- strokeCap: "round",
3542
- strokeJoin: "round",
3543
- antiAlias: true
3544
- }
3545
- );
3693
+ if (payload.kind === "strokePath") {
3694
+ const intervals = dashIntervalsFromStrokeDasharray3(payload.strokeDasharray);
3695
+ return /* @__PURE__ */ jsx(
3696
+ Path,
3697
+ {
3698
+ path: payload.d,
3699
+ color: colorWithOpacity(
3700
+ payload.stroke,
3701
+ isLaser ? 0.85 : payload.strokeOpacity
3702
+ ),
3703
+ style: "stroke",
3704
+ strokeWidth: payload.strokeWidth,
3705
+ strokeCap: "round",
3706
+ strokeJoin: "round",
3707
+ antiAlias: true,
3708
+ children: intervals ? /* @__PURE__ */ jsx(DashPathEffect, { intervals }) : null
3709
+ }
3710
+ );
3711
+ }
3712
+ return null;
3546
3713
  }
3547
3714
  return null;
3548
3715
  }, [placementPreview, previewStrokeStyle, overlayStrokeWorld, marqueeDashWorld]);
@@ -5106,6 +5273,18 @@ function applyRotationFromPointer(item, startRotation, startPointerAngleRad, poi
5106
5273
  }
5107
5274
  return { ...item, rotation: startRotation + delta };
5108
5275
  }
5276
+ function moveItemByDelta(item, dx, dy) {
5277
+ return {
5278
+ ...item,
5279
+ x: item.x + dx,
5280
+ y: item.y + dy,
5281
+ bounds: {
5282
+ ...item.bounds,
5283
+ x: item.bounds.x + dx,
5284
+ y: item.bounds.y + dy
5285
+ }
5286
+ };
5287
+ }
5109
5288
  function resizeItemByHandle(item, start, handle, currentWorld) {
5110
5289
  const sb = normalizeRect(start.bounds);
5111
5290
  if (!itemAllowsResizeHandle(item, handle)) {
@@ -5330,6 +5509,39 @@ function hitTestNativeRemotePresence(peers, camera, point) {
5330
5509
  return null;
5331
5510
  }
5332
5511
 
5512
+ // src/native/native-shape-clipboard.ts
5513
+ var NATIVE_SHAPE_CLIPBOARD_PASTE_OFFSET_WORLD = 24;
5514
+ function copyNativeSelectedShapeItems(items, selectedIds) {
5515
+ if (selectedIds.length === 0) return null;
5516
+ const copies = selectedIds.map((id) => items.find((item) => item.id === id)).filter((item) => item != null);
5517
+ if (copies.length === 0) return null;
5518
+ return copies.map((item) => JSON.parse(JSON.stringify(item)));
5519
+ }
5520
+ function pasteNativeShapeClipboard(input) {
5521
+ if (input.clipboard.length === 0) return null;
5522
+ const clones = cloneVectorSceneItemsWithNewIds(input.clipboard);
5523
+ const moved = clones.map(
5524
+ (item) => moveItemByDelta(
5525
+ item,
5526
+ NATIVE_SHAPE_CLIPBOARD_PASTE_OFFSET_WORLD,
5527
+ NATIVE_SHAPE_CLIPBOARD_PASTE_OFFSET_WORLD
5528
+ )
5529
+ );
5530
+ if (moved.length === 0) return null;
5531
+ return {
5532
+ items: [...input.items, ...moved],
5533
+ selectedIds: moved.map((item) => item.id)
5534
+ };
5535
+ }
5536
+ function duplicateNativeSelectedShapes(input) {
5537
+ const clipboard = copyNativeSelectedShapeItems(input.items, input.selectedIds);
5538
+ if (!clipboard) return null;
5539
+ return pasteNativeShapeClipboard({
5540
+ items: input.items,
5541
+ clipboard
5542
+ });
5543
+ }
5544
+
5333
5545
  // src/native/native-transient-items.ts
5334
5546
  function moveNativeTransientItems({
5335
5547
  items,
@@ -5378,6 +5590,11 @@ var NATIVE_VIEWPORT_OVERLAY_Z_INDEX = 40;
5378
5590
  var NATIVE_VIEWPORT_OVERLAY_ELEVATION = 40;
5379
5591
  var NATIVE_VIEWPORT_EXTERNAL_OVERLAY_Z_INDEX = 20;
5380
5592
  var NATIVE_VIEWPORT_EXTERNAL_OVERLAY_ELEVATION = 20;
5593
+ var LONG_PRESS_CONTEXT_MENU_MS = 520;
5594
+ var LONG_PRESS_CONTEXT_MENU_CANCEL_PX = 10;
5595
+ var NATIVE_CONTEXT_MENU_WIDTH = 304;
5596
+ var NATIVE_CONTEXT_MENU_HEIGHT = 52;
5597
+ var NATIVE_CONTEXT_MENU_MARGIN = 12;
5381
5598
  function isPlacementTool(toolId) {
5382
5599
  return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
5383
5600
  }
@@ -5450,6 +5667,102 @@ function fitCameraToWorldRect(camera, viewportW, viewportH, worldRect, padding)
5450
5667
  camera.x = viewportW / 2 - cx * z;
5451
5668
  camera.y = viewportH / 2 - cy * z;
5452
5669
  }
5670
+ function clampNativeContextMenuState(menu, size) {
5671
+ return {
5672
+ ...menu,
5673
+ x: Math.max(
5674
+ NATIVE_CONTEXT_MENU_MARGIN,
5675
+ Math.min(
5676
+ menu.x,
5677
+ Math.max(NATIVE_CONTEXT_MENU_MARGIN, size.width - NATIVE_CONTEXT_MENU_WIDTH)
5678
+ )
5679
+ ),
5680
+ y: Math.max(
5681
+ NATIVE_CONTEXT_MENU_MARGIN,
5682
+ Math.min(
5683
+ menu.y - NATIVE_CONTEXT_MENU_HEIGHT - NATIVE_CONTEXT_MENU_MARGIN,
5684
+ Math.max(
5685
+ NATIVE_CONTEXT_MENU_MARGIN,
5686
+ size.height - NATIVE_CONTEXT_MENU_HEIGHT - NATIVE_CONTEXT_MENU_MARGIN
5687
+ )
5688
+ )
5689
+ )
5690
+ };
5691
+ }
5692
+ function NativeSelectionContextMenu({
5693
+ x,
5694
+ y,
5695
+ canPaste,
5696
+ onCopy,
5697
+ onPaste,
5698
+ onDuplicate,
5699
+ onDelete
5700
+ }) {
5701
+ return /* @__PURE__ */ jsxs(
5702
+ View,
5703
+ {
5704
+ style: [
5705
+ styles4.nativeSelectionContextMenu,
5706
+ {
5707
+ left: x,
5708
+ top: y
5709
+ }
5710
+ ],
5711
+ children: [
5712
+ /* @__PURE__ */ jsx(NativeSelectionContextMenuButton, { label: "Copy", onPress: onCopy }),
5713
+ /* @__PURE__ */ jsx(
5714
+ NativeSelectionContextMenuButton,
5715
+ {
5716
+ label: "Paste",
5717
+ onPress: onPaste,
5718
+ disabled: !canPaste
5719
+ }
5720
+ ),
5721
+ /* @__PURE__ */ jsx(NativeSelectionContextMenuButton, { label: "Duplicate", onPress: onDuplicate }),
5722
+ /* @__PURE__ */ jsx(
5723
+ NativeSelectionContextMenuButton,
5724
+ {
5725
+ label: "Delete",
5726
+ onPress: onDelete,
5727
+ destructive: true
5728
+ }
5729
+ )
5730
+ ]
5731
+ }
5732
+ );
5733
+ }
5734
+ function NativeSelectionContextMenuButton({
5735
+ label,
5736
+ onPress,
5737
+ disabled = false,
5738
+ destructive = false
5739
+ }) {
5740
+ return /* @__PURE__ */ jsx(
5741
+ Pressable,
5742
+ {
5743
+ accessibilityRole: "button",
5744
+ accessibilityState: { disabled },
5745
+ disabled,
5746
+ onPress,
5747
+ style: ({ pressed }) => [
5748
+ styles4.nativeSelectionContextMenuButton,
5749
+ pressed && !disabled ? styles4.nativeSelectionContextMenuButtonPressed : void 0,
5750
+ disabled ? styles4.nativeSelectionContextMenuButtonDisabled : void 0
5751
+ ],
5752
+ children: /* @__PURE__ */ jsx(
5753
+ Text,
5754
+ {
5755
+ style: [
5756
+ styles4.nativeSelectionContextMenuButtonText,
5757
+ destructive ? styles4.nativeSelectionContextMenuDeleteText : void 0,
5758
+ disabled ? styles4.nativeSelectionContextMenuButtonTextDisabled : void 0
5759
+ ],
5760
+ children: label
5761
+ }
5762
+ )
5763
+ }
5764
+ );
5765
+ }
5453
5766
  var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5454
5767
  items,
5455
5768
  selectedIds = [],
@@ -5516,6 +5829,16 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5516
5829
  selectedIdsRef.current = selectedIds;
5517
5830
  const remotePresenceRef = useRef(remotePresence);
5518
5831
  remotePresenceRef.current = remotePresence;
5832
+ const shapeClipboardRef = useRef(null);
5833
+ const [selectionContextMenu, setSelectionContextMenu] = useState(null);
5834
+ const selectionContextMenuRef = useRef(
5835
+ null
5836
+ );
5837
+ selectionContextMenuRef.current = selectionContextMenu;
5838
+ const contextMenuLongPressTimerRef = useRef(
5839
+ null
5840
+ );
5841
+ const contextMenuLongPressStartRef = useRef(null);
5519
5842
  const dragStateRef = useRef({ kind: "idle" });
5520
5843
  useEffect(() => {
5521
5844
  const committedItems = items;
@@ -5533,6 +5856,16 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5533
5856
  transientItemsRef.current = null;
5534
5857
  setTransientItems(null);
5535
5858
  }, []);
5859
+ const clearNativeContextMenuLongPress = useCallback(() => {
5860
+ if (contextMenuLongPressTimerRef.current) {
5861
+ clearTimeout(contextMenuLongPressTimerRef.current);
5862
+ contextMenuLongPressTimerRef.current = null;
5863
+ }
5864
+ contextMenuLongPressStartRef.current = null;
5865
+ }, []);
5866
+ const closeNativeSelectionContextMenu = useCallback(() => {
5867
+ setSelectionContextMenu(null);
5868
+ }, []);
5536
5869
  const commitTransientItemsPreview = useCallback(() => {
5537
5870
  const nextItems = transientItemsRef.current;
5538
5871
  const change = onItemsChangeRef.current;
@@ -5565,6 +5898,9 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5565
5898
  if (laserClearTimerRef.current) {
5566
5899
  clearTimeout(laserClearTimerRef.current);
5567
5900
  }
5901
+ if (contextMenuLongPressTimerRef.current) {
5902
+ clearTimeout(contextMenuLongPressTimerRef.current);
5903
+ }
5568
5904
  },
5569
5905
  []
5570
5906
  );
@@ -5649,6 +5985,10 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5649
5985
  }
5650
5986
  const camera = cameraRef.current;
5651
5987
  const [cameraTick, setCameraTick] = useState(0);
5988
+ const selectedItems = useMemo(
5989
+ () => activeItems.filter((item) => selectedIds.includes(item.id)),
5990
+ [activeItems, selectedIds]
5991
+ );
5652
5992
  const screenToWorld = useCallback(
5653
5993
  (sx, sy) => {
5654
5994
  const cam = cameraRef.current;
@@ -5657,6 +5997,59 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5657
5997
  },
5658
5998
  []
5659
5999
  );
6000
+ const startNativeContextMenuLongPress = useCallback(
6001
+ (point) => {
6002
+ clearNativeContextMenuLongPress();
6003
+ if (toolIdRef.current !== "select") return;
6004
+ const { worldX, worldY } = screenToWorld(point.x, point.y);
6005
+ const request = resolveNativeSelectionContextMenuRequest({
6006
+ interactive,
6007
+ toolId: toolIdRef.current,
6008
+ selectedItems,
6009
+ worldPoint: { x: worldX, y: worldY },
6010
+ screenPoint: point
6011
+ });
6012
+ if (!request) return;
6013
+ contextMenuLongPressStartRef.current = point;
6014
+ contextMenuLongPressTimerRef.current = setTimeout(() => {
6015
+ contextMenuLongPressTimerRef.current = null;
6016
+ contextMenuLongPressStartRef.current = null;
6017
+ dragStateRef.current = { kind: "idle" };
6018
+ lastPanPoint.current = null;
6019
+ lastPinchDist.current = null;
6020
+ clearTransientItemsPreview();
6021
+ setRealtimePlacementPreview(null);
6022
+ setSelectionContextMenu(
6023
+ clampNativeContextMenuState(
6024
+ {
6025
+ ...request,
6026
+ canPaste: shapeClipboardRef.current !== null
6027
+ },
6028
+ size
6029
+ )
6030
+ );
6031
+ }, LONG_PRESS_CONTEXT_MENU_MS);
6032
+ },
6033
+ [
6034
+ clearNativeContextMenuLongPress,
6035
+ clearTransientItemsPreview,
6036
+ interactive,
6037
+ screenToWorld,
6038
+ selectedItems,
6039
+ setRealtimePlacementPreview,
6040
+ size
6041
+ ]
6042
+ );
6043
+ const cancelNativeContextMenuLongPressAfterMove = useCallback(
6044
+ (point) => {
6045
+ const start = contextMenuLongPressStartRef.current;
6046
+ if (!start) return;
6047
+ if (Math.hypot(point.x - start.x, point.y - start.y) > LONG_PRESS_CONTEXT_MENU_CANCEL_PX) {
6048
+ clearNativeContextMenuLongPress();
6049
+ }
6050
+ },
6051
+ [clearNativeContextMenuLongPress]
6052
+ );
5660
6053
  const notifyWorldPointerMove = useCallback(
5661
6054
  (point) => {
5662
6055
  const { worldX, worldY } = screenToWorld(point.x, point.y);
@@ -5679,10 +6072,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5679
6072
  }, []);
5680
6073
  const hideToolCursor = useCallback(() => {
5681
6074
  }, []);
5682
- const selectedItems = useMemo(
5683
- () => activeItems.filter((item) => selectedIds.includes(item.id)),
5684
- [activeItems, selectedIds]
5685
- );
5686
6075
  const selectedStyleInspectorState = useMemo(() => {
5687
6076
  const styleableItems = selectedItems.filter(
5688
6077
  (item) => !item.locked && getNativeStyleInspectorToolId(item) !== null
@@ -5727,10 +6116,77 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5727
6116
  },
5728
6117
  [patchCurrentStrokeStyle]
5729
6118
  );
6119
+ const copySelectedShapes = useCallback(() => {
6120
+ if (!interactive) return false;
6121
+ const clipboard = copyNativeSelectedShapeItems(
6122
+ itemsRef.current,
6123
+ selectedIdsRef.current
6124
+ );
6125
+ if (!clipboard) return false;
6126
+ shapeClipboardRef.current = clipboard;
6127
+ return true;
6128
+ }, [interactive]);
6129
+ const pasteCopiedShapes = useCallback(() => {
6130
+ if (!interactive) return false;
6131
+ const change = onItemsChangeRef.current;
6132
+ const clipboard = shapeClipboardRef.current;
6133
+ if (!change || !clipboard) return false;
6134
+ const pasted = pasteNativeShapeClipboard({
6135
+ items: itemsRef.current,
6136
+ clipboard
6137
+ });
6138
+ if (!pasted) return false;
6139
+ change(pasted.items);
6140
+ onSelectionChangeRef.current?.(pasted.selectedIds);
6141
+ return true;
6142
+ }, [interactive]);
6143
+ const duplicateSelectedShapes = useCallback(() => {
6144
+ if (!interactive) return false;
6145
+ const change = onItemsChangeRef.current;
6146
+ if (!change) return false;
6147
+ const duplicated = duplicateNativeSelectedShapes({
6148
+ items: itemsRef.current,
6149
+ selectedIds: selectedIdsRef.current
6150
+ });
6151
+ if (!duplicated) return false;
6152
+ change(duplicated.items);
6153
+ onSelectionChangeRef.current?.(duplicated.selectedIds);
6154
+ return true;
6155
+ }, [interactive]);
6156
+ const handleCopySelectedShapesFromMenu = useCallback(() => {
6157
+ copySelectedShapes();
6158
+ closeNativeSelectionContextMenu();
6159
+ }, [closeNativeSelectionContextMenu, copySelectedShapes]);
6160
+ const handlePasteCopiedShapesFromMenu = useCallback(() => {
6161
+ pasteCopiedShapes();
6162
+ closeNativeSelectionContextMenu();
6163
+ }, [closeNativeSelectionContextMenu, pasteCopiedShapes]);
6164
+ const handleDuplicateSelectedShapesFromMenu = useCallback(() => {
6165
+ duplicateSelectedShapes();
6166
+ closeNativeSelectionContextMenu();
6167
+ }, [closeNativeSelectionContextMenu, duplicateSelectedShapes]);
6168
+ const handleDeleteSelectedShapes = useCallback(() => {
6169
+ const menu = selectionContextMenuRef.current;
6170
+ const ids = menu?.itemIds ?? selectedIdsRef.current;
6171
+ if (ids.length === 0) return false;
6172
+ const change = onItemsChangeRef.current;
6173
+ if (!change) return false;
6174
+ const idSet = new Set(ids);
6175
+ const nextItems = itemsRef.current.filter((item) => !idSet.has(item.id));
6176
+ if (nextItems.length === itemsRef.current.length) return false;
6177
+ change(nextItems);
6178
+ onSelectionChangeRef.current?.(
6179
+ selectedIdsRef.current.filter((id) => !idSet.has(id))
6180
+ );
6181
+ closeNativeSelectionContextMenu();
6182
+ return true;
6183
+ }, [closeNativeSelectionContextMenu]);
5730
6184
  const lastPinchDist = useRef(null);
5731
6185
  const lastPanPoint = useRef(null);
5732
6186
  const beginDragAtScreenPoint = useCallback(
5733
6187
  (point) => {
6188
+ closeNativeSelectionContextMenu();
6189
+ startNativeContextMenuLongPress(point);
5734
6190
  lastPinchDist.current = null;
5735
6191
  lastPanPoint.current = null;
5736
6192
  const sx = point.x;
@@ -5944,15 +6400,18 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5944
6400
  dragStateRef.current = { kind: "pan" };
5945
6401
  },
5946
6402
  [
6403
+ closeNativeSelectionContextMenu,
5947
6404
  interactive,
5948
6405
  requestSelectToolAfterUse,
5949
6406
  screenToWorld,
5950
6407
  setRealtimePlacementPreview,
6408
+ startNativeContextMenuLongPress,
5951
6409
  updateToolCursorPoint
5952
6410
  ]
5953
6411
  );
5954
6412
  const applyDragMoveAtScreenPoint = useCallback(
5955
6413
  (point, pagePoint) => {
6414
+ cancelNativeContextMenuLongPressAfterMove(point);
5956
6415
  const cam = cameraRef.current;
5957
6416
  if (!cam) return;
5958
6417
  updateToolCursorPoint(point);
@@ -5978,19 +6437,22 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5978
6437
  const dx = worldX - (last?.x ?? worldX);
5979
6438
  const dy = worldY - (last?.y ?? worldY);
5980
6439
  const shouldAppendPoint = Math.hypot(dx, dy) > 0.5 / cam.zoom;
5981
- if (!shouldAppendPoint) return;
5982
- pts.push({ x: worldX, y: worldY });
6440
+ if (shouldAppendPoint) {
6441
+ pts.push({ x: worldX, y: worldY });
6442
+ }
5983
6443
  if (st.tool === "laser") {
5984
- setLaserTrail((prev) => [
5985
- ...prev,
5986
- { x: worldX, y: worldY, t: Date.now() }
5987
- ]);
6444
+ if (shouldAppendPoint) {
6445
+ setLaserTrail((prev) => [
6446
+ ...prev,
6447
+ { x: worldX, y: worldY, t: Date.now() }
6448
+ ]);
6449
+ }
5988
6450
  return;
5989
6451
  }
5990
6452
  setRealtimePlacementPreview({
5991
6453
  kind: "stroke",
5992
6454
  tool: st.tool,
5993
- points: pts.map((previewPoint) => ({ ...previewPoint })),
6455
+ points: [...pts],
5994
6456
  style: { ...strokeStyleRef.current }
5995
6457
  });
5996
6458
  return;
@@ -6088,6 +6550,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6088
6550
  }
6089
6551
  },
6090
6552
  [
6553
+ cancelNativeContextMenuLongPressAfterMove,
6091
6554
  requestRender,
6092
6555
  screenToWorld,
6093
6556
  setRealtimePlacementPreview,
@@ -6097,6 +6560,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6097
6560
  );
6098
6561
  const finishDragAtScreenPoint = useCallback(
6099
6562
  (point) => {
6563
+ clearNativeContextMenuLongPress();
6100
6564
  lastPinchDist.current = null;
6101
6565
  lastPanPoint.current = null;
6102
6566
  updateToolCursorPoint(point);
@@ -6344,6 +6808,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6344
6808
  dragStateRef.current = { kind: "idle" };
6345
6809
  },
6346
6810
  [
6811
+ clearNativeContextMenuLongPress,
6347
6812
  requestSelectToolAfterNativeLinkUse,
6348
6813
  requestSelectToolAfterUse,
6349
6814
  screenToWorld,
@@ -6387,6 +6852,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6387
6852
  const sx = evt.nativeEvent.locationX;
6388
6853
  const sy = evt.nativeEvent.locationY;
6389
6854
  if (touches && touches.length >= 2) {
6855
+ clearNativeContextMenuLongPress();
6390
6856
  hideToolCursor();
6391
6857
  notifyWorldPointerLeave();
6392
6858
  dragStateRef.current = { kind: "pan" };
@@ -6403,6 +6869,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6403
6869
  const pageX = evt.nativeEvent.pageX;
6404
6870
  const pageY = evt.nativeEvent.pageY;
6405
6871
  if (touches && touches.length >= 2) {
6872
+ clearNativeContextMenuLongPress();
6406
6873
  hideToolCursor();
6407
6874
  notifyWorldPointerLeave();
6408
6875
  const t0 = touches[0];
@@ -6433,6 +6900,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6433
6900
  });
6434
6901
  },
6435
6902
  onPanResponderTerminate: () => {
6903
+ clearNativeContextMenuLongPress();
6436
6904
  lastPinchDist.current = null;
6437
6905
  lastPanPoint.current = null;
6438
6906
  hideToolCursor();
@@ -6449,6 +6917,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6449
6917
  [
6450
6918
  applyDragMoveAtScreenPoint,
6451
6919
  beginDragAtScreenPoint,
6920
+ clearNativeContextMenuLongPress,
6452
6921
  finishDragAtScreenPoint,
6453
6922
  requestRender,
6454
6923
  hideToolCursor,
@@ -6474,9 +6943,18 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6474
6943
  options?.padding ?? 0.08
6475
6944
  );
6476
6945
  requestRender();
6477
- }
6946
+ },
6947
+ copySelectedShapes,
6948
+ pasteCopiedShapes,
6949
+ duplicateSelectedShapes
6478
6950
  }),
6479
- [requestRender, size]
6951
+ [
6952
+ copySelectedShapes,
6953
+ duplicateSelectedShapes,
6954
+ pasteCopiedShapes,
6955
+ requestRender,
6956
+ size
6957
+ ]
6480
6958
  );
6481
6959
  const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
6482
6960
  const styleInspectorToolId = selectedStyleInspectorState?.toolId ?? activeStyleToolId;
@@ -6594,7 +7072,17 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6594
7072
  pointerEvents: "box-none",
6595
7073
  children: toolbar
6596
7074
  }
6597
- )
7075
+ ),
7076
+ interactive && selectionContextMenu ? /* @__PURE__ */ jsx(
7077
+ NativeSelectionContextMenu,
7078
+ {
7079
+ ...selectionContextMenu,
7080
+ onCopy: handleCopySelectedShapesFromMenu,
7081
+ onPaste: handlePasteCopiedShapesFromMenu,
7082
+ onDuplicate: handleDuplicateSelectedShapesFromMenu,
7083
+ onDelete: handleDeleteSelectedShapes
7084
+ }
7085
+ ) : null
6598
7086
  ] }),
6599
7087
  /* @__PURE__ */ jsx(
6600
7088
  Modal,
@@ -6657,6 +7145,49 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
6657
7145
  ] });
6658
7146
  });
6659
7147
  var styles4 = StyleSheet.create({
7148
+ nativeSelectionContextMenu: {
7149
+ position: "absolute",
7150
+ width: NATIVE_CONTEXT_MENU_WIDTH,
7151
+ minHeight: NATIVE_CONTEXT_MENU_HEIGHT,
7152
+ flexDirection: "row",
7153
+ alignItems: "center",
7154
+ justifyContent: "space-between",
7155
+ padding: 4,
7156
+ borderRadius: 14,
7157
+ borderWidth: StyleSheet.hairlineWidth,
7158
+ borderColor: "#d4d4d8",
7159
+ backgroundColor: "#ffffff",
7160
+ shadowColor: "#000000",
7161
+ shadowOpacity: 0.16,
7162
+ shadowRadius: 18,
7163
+ shadowOffset: { width: 0, height: 8 },
7164
+ elevation: NATIVE_VIEWPORT_OVERLAY_ELEVATION + 4,
7165
+ zIndex: NATIVE_VIEWPORT_OVERLAY_Z_INDEX + 4
7166
+ },
7167
+ nativeSelectionContextMenuButton: {
7168
+ minHeight: 42,
7169
+ alignItems: "center",
7170
+ justifyContent: "center",
7171
+ borderRadius: 10,
7172
+ paddingHorizontal: 10
7173
+ },
7174
+ nativeSelectionContextMenuButtonPressed: {
7175
+ backgroundColor: "#f4f4f5"
7176
+ },
7177
+ nativeSelectionContextMenuButtonDisabled: {
7178
+ opacity: 0.45
7179
+ },
7180
+ nativeSelectionContextMenuButtonText: {
7181
+ color: "#18181b",
7182
+ fontSize: 14,
7183
+ fontWeight: "700"
7184
+ },
7185
+ nativeSelectionContextMenuButtonTextDisabled: {
7186
+ color: "#71717a"
7187
+ },
7188
+ nativeSelectionContextMenuDeleteText: {
7189
+ color: "#dc2626"
7190
+ },
6660
7191
  nativeLinkDialogBackdrop: {
6661
7192
  flex: 1,
6662
7193
  alignItems: "center",