canvu-react 0.4.41 → 0.4.43

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,8 +1,8 @@
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
3
  import { memo, forwardRef, useState, useRef, useCallback, useEffect, useMemo, useImperativeHandle } from 'react';
4
+ import { StyleSheet, PanResponder, View, Modal, Text, TextInput, Pressable, ScrollView, Image as Image$1 } from 'react-native';
4
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { StyleSheet, PanResponder, View, Modal, Text, TextInput, Pressable, ScrollView } from 'react-native';
6
6
 
7
7
  // src/math/rect.ts
8
8
  function rectsIntersect(a, b) {
@@ -1169,6 +1169,422 @@ function boundsAabbForRotatedItem(item) {
1169
1169
  return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
1170
1170
  }
1171
1171
 
1172
+ // src/scene/clone-item.ts
1173
+ function cloneVectorSceneItemWithNewId(item) {
1174
+ const id = createShapeId();
1175
+ const copy = JSON.parse(JSON.stringify(item));
1176
+ let next = { ...copy, id };
1177
+ if (next.toolKind === "arrow" && next.line) {
1178
+ next = {
1179
+ ...next,
1180
+ childrenSvg: buildArrowSvg(id, next.line, resolveStrokeStyle(next))
1181
+ };
1182
+ }
1183
+ if (next.toolKind === "text" && next.text !== void 0) {
1184
+ return rebuildItemSvg(next);
1185
+ }
1186
+ if (next.toolKind === "custom" && next.customInnerSvg && next.customIntrinsicSize) {
1187
+ return rebuildItemSvg(next);
1188
+ }
1189
+ return next;
1190
+ }
1191
+
1192
+ // src/scene/managed-images.ts
1193
+ var MANAGED_KEY = "managed";
1194
+ var STACK_GAP_WORLD = 16;
1195
+ function isManagedImage(item) {
1196
+ const data = item.pluginData;
1197
+ return data != null && data[MANAGED_KEY] === true;
1198
+ }
1199
+ function markImageAsManaged(item) {
1200
+ return {
1201
+ ...item,
1202
+ locked: true,
1203
+ pluginData: { ...item.pluginData ?? {}, [MANAGED_KEY]: true }
1204
+ };
1205
+ }
1206
+ function restackManagedImages(items) {
1207
+ let anchorAabbY = Infinity;
1208
+ let anchorCenterX = 0;
1209
+ for (const item of items) {
1210
+ if (!isManagedImage(item)) continue;
1211
+ const aabb = boundsAabbForRotatedItem(item);
1212
+ if (aabb.y < anchorAabbY) {
1213
+ anchorAabbY = aabb.y;
1214
+ anchorCenterX = aabb.x + aabb.width / 2;
1215
+ }
1216
+ }
1217
+ if (!Number.isFinite(anchorAabbY)) return [...items];
1218
+ let cursorY = anchorAabbY;
1219
+ return items.map((item) => {
1220
+ if (!isManagedImage(item)) return item;
1221
+ const aabb = boundsAabbForRotatedItem(item);
1222
+ const centerY = cursorY + aabb.height / 2;
1223
+ const newX = anchorCenterX - item.bounds.width / 2;
1224
+ const newY = centerY - item.bounds.height / 2;
1225
+ cursorY += aabb.height + STACK_GAP_WORLD;
1226
+ if (item.bounds.x === newX && item.bounds.y === newY) return item;
1227
+ return {
1228
+ ...item,
1229
+ x: newX,
1230
+ y: newY,
1231
+ bounds: { ...item.bounds, x: newX, y: newY }
1232
+ };
1233
+ });
1234
+ }
1235
+ function copyManagedImage(items, id) {
1236
+ const idx = items.findIndex((i) => i.id === id);
1237
+ if (idx < 0) return [...items];
1238
+ const source = items[idx];
1239
+ if (!source) return [...items];
1240
+ const clone = markImageAsManaged(cloneVectorSceneItemWithNewId(source));
1241
+ const inserted = [...items.slice(0, idx + 1), clone, ...items.slice(idx + 1)];
1242
+ return restackManagedImages(inserted);
1243
+ }
1244
+ function rotateManagedImage(items, id) {
1245
+ return restackManagedImages(
1246
+ items.map(
1247
+ (i) => i.id === id ? { ...i, rotation: ((i.rotation ?? 0) + Math.PI / 2) % (Math.PI * 2) } : i
1248
+ )
1249
+ );
1250
+ }
1251
+ function deleteManagedImage(items, id) {
1252
+ return restackManagedImages(items.filter((i) => i.id !== id));
1253
+ }
1254
+ var defaultLabels = {
1255
+ title: "Images",
1256
+ focus: "Focus on canvas",
1257
+ duplicate: "Duplicate",
1258
+ rotate: "Rotate",
1259
+ delete: "Delete",
1260
+ collapse: "Collapse images menu",
1261
+ expand: "Open images menu"
1262
+ };
1263
+ var iconPaths = {
1264
+ images: ["M4 5h16v14H4z", "M4 15l4-4 3 3 5-6 4 5"],
1265
+ chevronRight: ["M9 18l6-6-6-6"],
1266
+ copy: ["M8 8h10v10H8z", "M6 16H4V4h12v2", "M13 11v4", "M11 13h4"],
1267
+ rotate: ["M21 12a9 9 0 1 1-2.64-6.36", "M21 3v6h-6"],
1268
+ trash: ["M3 6h18", "M8 6V4h8v2", "M6 6l1 15h10l1-15", "M10 11v6", "M14 11v6"],
1269
+ grip: ["M8 7h8", "M8 12h8", "M8 17h8"]
1270
+ };
1271
+ function NativeImagesMenuIcon({
1272
+ name,
1273
+ color = "#0f172a",
1274
+ size = 18
1275
+ }) {
1276
+ const scale = size / 24;
1277
+ return /* @__PURE__ */ jsx(Canvas, { style: { width: size, height: size }, children: /* @__PURE__ */ jsx(Group, { transform: [{ scale }], children: iconPaths[name].map((path) => /* @__PURE__ */ jsx(
1278
+ Path,
1279
+ {
1280
+ path,
1281
+ color,
1282
+ style: "stroke",
1283
+ strokeWidth: 2,
1284
+ strokeCap: "round",
1285
+ strokeJoin: "round"
1286
+ },
1287
+ path
1288
+ )) }) });
1289
+ }
1290
+ function resolveLabels(labels) {
1291
+ return {
1292
+ ...defaultLabels,
1293
+ ...labels,
1294
+ duplicate: labels?.duplicate ?? labels?.copy ?? defaultLabels.duplicate
1295
+ };
1296
+ }
1297
+ function getDefaultImageUri(item) {
1298
+ return item.imageThumbnailHref ?? item.imageRasterHref ?? null;
1299
+ }
1300
+ function NativeImagesMenuAction({
1301
+ label,
1302
+ danger = false,
1303
+ children,
1304
+ onPress,
1305
+ style
1306
+ }) {
1307
+ return /* @__PURE__ */ jsx(
1308
+ Pressable,
1309
+ {
1310
+ accessibilityRole: "button",
1311
+ accessibilityLabel: label,
1312
+ onPress,
1313
+ style: ({ pressed }) => [
1314
+ styles.actionButton,
1315
+ pressed && styles.actionButtonPressed,
1316
+ danger && styles.dangerActionButton,
1317
+ style
1318
+ ],
1319
+ children
1320
+ }
1321
+ );
1322
+ }
1323
+ function NativeImagesMenuRow({
1324
+ item,
1325
+ items,
1326
+ labels,
1327
+ onItemsChange,
1328
+ onFocusItem,
1329
+ getImageUri,
1330
+ renderThumbnail,
1331
+ rowStyle,
1332
+ actionButtonStyle
1333
+ }) {
1334
+ const imageUri = getImageUri(item);
1335
+ return /* @__PURE__ */ jsxs(View, { style: [styles.row, rowStyle], children: [
1336
+ /* @__PURE__ */ jsx(View, { style: styles.handle, children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "grip", color: "#94a3b8" }) }),
1337
+ /* @__PURE__ */ jsx(
1338
+ Pressable,
1339
+ {
1340
+ accessibilityRole: "button",
1341
+ accessibilityLabel: labels.focus,
1342
+ disabled: !onFocusItem,
1343
+ onPress: () => onFocusItem?.(item),
1344
+ style: styles.thumbnailButton,
1345
+ children: /* @__PURE__ */ jsxs(View, { style: styles.thumbnailBox, children: [
1346
+ renderThumbnail ? renderThumbnail(item) : null,
1347
+ !renderThumbnail && imageUri ? /* @__PURE__ */ jsx(Image$1, { source: { uri: imageUri }, style: styles.thumbnailImage }) : null
1348
+ ] })
1349
+ }
1350
+ ),
1351
+ /* @__PURE__ */ jsxs(View, { style: styles.actionsColumn, children: [
1352
+ /* @__PURE__ */ jsx(
1353
+ NativeImagesMenuAction,
1354
+ {
1355
+ label: labels.duplicate,
1356
+ onPress: () => onItemsChange(copyManagedImage(items, item.id)),
1357
+ style: actionButtonStyle,
1358
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "copy" })
1359
+ }
1360
+ ),
1361
+ /* @__PURE__ */ jsx(
1362
+ NativeImagesMenuAction,
1363
+ {
1364
+ label: labels.rotate,
1365
+ onPress: () => onItemsChange(rotateManagedImage(items, item.id)),
1366
+ style: actionButtonStyle,
1367
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "rotate" })
1368
+ }
1369
+ ),
1370
+ /* @__PURE__ */ jsx(
1371
+ NativeImagesMenuAction,
1372
+ {
1373
+ label: labels.delete,
1374
+ danger: true,
1375
+ onPress: () => onItemsChange(deleteManagedImage(items, item.id)),
1376
+ style: actionButtonStyle,
1377
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "trash", color: "#b91c1c" })
1378
+ }
1379
+ )
1380
+ ] })
1381
+ ] });
1382
+ }
1383
+ function NativeImagesMenu({
1384
+ items,
1385
+ onItemsChange,
1386
+ onFocusItem,
1387
+ labels,
1388
+ defaultOpen = false,
1389
+ style,
1390
+ panelStyle,
1391
+ collapsedButtonStyle,
1392
+ rowStyle,
1393
+ actionButtonStyle,
1394
+ getImageUri = getDefaultImageUri,
1395
+ renderThumbnail
1396
+ }) {
1397
+ const managed = useMemo(() => items.filter(isManagedImage), [items]);
1398
+ const [collapsed, setCollapsed] = useState(!defaultOpen);
1399
+ if (managed.length === 0) return null;
1400
+ const resolvedLabels = resolveLabels(labels);
1401
+ if (collapsed) {
1402
+ return /* @__PURE__ */ jsx(
1403
+ Pressable,
1404
+ {
1405
+ accessibilityRole: "button",
1406
+ accessibilityLabel: resolvedLabels.expand,
1407
+ onPress: () => setCollapsed(false),
1408
+ style: ({ pressed }) => [
1409
+ styles.collapsedButton,
1410
+ pressed && styles.collapsedButtonPressed,
1411
+ collapsedButtonStyle,
1412
+ style
1413
+ ],
1414
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "images", size: 20 })
1415
+ }
1416
+ );
1417
+ }
1418
+ return /* @__PURE__ */ jsxs(View, { style: [styles.panel, panelStyle, style], children: [
1419
+ /* @__PURE__ */ jsxs(View, { style: styles.header, children: [
1420
+ /* @__PURE__ */ jsxs(View, { style: styles.headerTitleRow, children: [
1421
+ /* @__PURE__ */ jsx(Text, { style: styles.headerTitle, children: resolvedLabels.title }),
1422
+ /* @__PURE__ */ jsx(Text, { style: styles.count, children: managed.length })
1423
+ ] }),
1424
+ /* @__PURE__ */ jsx(
1425
+ Pressable,
1426
+ {
1427
+ accessibilityRole: "button",
1428
+ accessibilityLabel: resolvedLabels.collapse,
1429
+ onPress: () => setCollapsed(true),
1430
+ style: ({ pressed }) => [
1431
+ styles.collapseButton,
1432
+ pressed && styles.actionButtonPressed
1433
+ ],
1434
+ children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "chevronRight", size: 20 })
1435
+ }
1436
+ )
1437
+ ] }),
1438
+ /* @__PURE__ */ jsx(
1439
+ ScrollView,
1440
+ {
1441
+ style: styles.listScroll,
1442
+ contentContainerStyle: styles.listContent,
1443
+ showsVerticalScrollIndicator: false,
1444
+ children: managed.map((item) => /* @__PURE__ */ jsx(
1445
+ NativeImagesMenuRow,
1446
+ {
1447
+ item,
1448
+ items,
1449
+ labels: resolvedLabels,
1450
+ onItemsChange,
1451
+ onFocusItem,
1452
+ getImageUri,
1453
+ renderThumbnail,
1454
+ rowStyle,
1455
+ actionButtonStyle
1456
+ },
1457
+ item.id
1458
+ ))
1459
+ }
1460
+ )
1461
+ ] });
1462
+ }
1463
+ var styles = StyleSheet.create({
1464
+ actionButton: {
1465
+ alignItems: "center",
1466
+ borderRadius: 6,
1467
+ height: 32,
1468
+ justifyContent: "center",
1469
+ width: 32
1470
+ },
1471
+ actionButtonPressed: {
1472
+ backgroundColor: "#f1f5f9"
1473
+ },
1474
+ actionsColumn: {
1475
+ alignItems: "center",
1476
+ gap: 4,
1477
+ justifyContent: "center"
1478
+ },
1479
+ collapseButton: {
1480
+ alignItems: "center",
1481
+ borderRadius: 8,
1482
+ height: 32,
1483
+ justifyContent: "center",
1484
+ width: 32
1485
+ },
1486
+ collapsedButton: {
1487
+ alignItems: "center",
1488
+ backgroundColor: "#ffffff",
1489
+ borderColor: "#e2e8f0",
1490
+ borderRadius: 10,
1491
+ borderWidth: 1,
1492
+ height: 44,
1493
+ justifyContent: "center",
1494
+ shadowColor: "#0f172a",
1495
+ shadowOffset: { width: 0, height: 8 },
1496
+ shadowOpacity: 0.12,
1497
+ shadowRadius: 24,
1498
+ width: 44
1499
+ },
1500
+ collapsedButtonPressed: {
1501
+ backgroundColor: "#f8fafc"
1502
+ },
1503
+ count: {
1504
+ color: "#64748b",
1505
+ fontSize: 14,
1506
+ fontWeight: "500"
1507
+ },
1508
+ dangerActionButton: {
1509
+ backgroundColor: "#fef2f2"
1510
+ },
1511
+ handle: {
1512
+ alignItems: "center",
1513
+ height: 128,
1514
+ justifyContent: "center",
1515
+ width: 28
1516
+ },
1517
+ header: {
1518
+ alignItems: "center",
1519
+ borderBottomColor: "#e2e8f0",
1520
+ borderBottomWidth: 1,
1521
+ flexDirection: "row",
1522
+ gap: 8,
1523
+ justifyContent: "space-between",
1524
+ paddingBottom: 8,
1525
+ paddingLeft: 14,
1526
+ paddingRight: 8,
1527
+ paddingTop: 8
1528
+ },
1529
+ headerTitle: {
1530
+ color: "#0f172a",
1531
+ fontSize: 14,
1532
+ fontWeight: "600",
1533
+ letterSpacing: 0.28
1534
+ },
1535
+ headerTitleRow: {
1536
+ alignItems: "center",
1537
+ flexDirection: "row",
1538
+ gap: 8
1539
+ },
1540
+ listContent: {
1541
+ gap: 6,
1542
+ padding: 8
1543
+ },
1544
+ listScroll: {
1545
+ maxHeight: 640
1546
+ },
1547
+ panel: {
1548
+ backgroundColor: "#ffffff",
1549
+ borderColor: "#e2e8f0",
1550
+ borderRadius: 10,
1551
+ borderWidth: 1,
1552
+ maxHeight: 820,
1553
+ overflow: "hidden",
1554
+ shadowColor: "#0f172a",
1555
+ shadowOffset: { width: 0, height: 10 },
1556
+ shadowOpacity: 0.12,
1557
+ shadowRadius: 40
1558
+ },
1559
+ row: {
1560
+ alignItems: "center",
1561
+ borderRadius: 10,
1562
+ flexDirection: "row",
1563
+ gap: 12,
1564
+ paddingBottom: 10,
1565
+ paddingLeft: 12,
1566
+ paddingRight: 12,
1567
+ paddingTop: 10
1568
+ },
1569
+ thumbnailBox: {
1570
+ alignItems: "center",
1571
+ backgroundColor: "#e2e8f0",
1572
+ borderRadius: 8,
1573
+ height: 128,
1574
+ justifyContent: "center",
1575
+ overflow: "hidden",
1576
+ width: 128
1577
+ },
1578
+ thumbnailButton: {
1579
+ alignItems: "center",
1580
+ justifyContent: "center"
1581
+ },
1582
+ thumbnailImage: {
1583
+ height: "100%",
1584
+ width: "100%"
1585
+ }
1586
+ });
1587
+
1172
1588
  // src/interaction/resize-handles.ts
1173
1589
  var HANDLE_IDS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
1174
1590
  function getHandleWorldPosition(bounds, id) {
@@ -3379,12 +3795,12 @@ function DashPreview({
3379
3795
  dashed = false
3380
3796
  }) {
3381
3797
  const strokeHeight = Math.max(3, Math.min(6, width));
3382
- return /* @__PURE__ */ jsxs(View, { style: styles.dashPreviewTrack, children: [
3798
+ return /* @__PURE__ */ jsxs(View, { style: styles2.dashPreviewTrack, children: [
3383
3799
  /* @__PURE__ */ jsx(
3384
3800
  View,
3385
3801
  {
3386
3802
  style: [
3387
- styles.dashPreviewStroke,
3803
+ styles2.dashPreviewStroke,
3388
3804
  {
3389
3805
  width: dashed ? 11 : 26,
3390
3806
  height: strokeHeight,
@@ -3398,7 +3814,7 @@ function DashPreview({
3398
3814
  View,
3399
3815
  {
3400
3816
  style: [
3401
- styles.dashPreviewStroke,
3817
+ styles2.dashPreviewStroke,
3402
3818
  {
3403
3819
  width: 11,
3404
3820
  height: strokeHeight,
@@ -3442,29 +3858,29 @@ function RangeControl({
3442
3858
  }),
3443
3859
  [updateFromX]
3444
3860
  );
3445
- return /* @__PURE__ */ jsxs(View, { style: styles.rangeRow, children: [
3861
+ return /* @__PURE__ */ jsxs(View, { style: styles2.rangeRow, children: [
3446
3862
  /* @__PURE__ */ jsxs(
3447
3863
  View,
3448
3864
  {
3449
- style: styles.rangeTrack,
3865
+ style: styles2.rangeTrack,
3450
3866
  onLayout: (event) => setTrackWidth(event.nativeEvent.layout.width),
3451
3867
  ...panResponder.panHandlers,
3452
3868
  children: [
3453
- /* @__PURE__ */ jsx(View, { style: styles.rangeBase }),
3454
- /* @__PURE__ */ jsx(View, { style: [styles.rangeFill, { width: `${normalized * 100}%` }] }),
3455
- /* @__PURE__ */ jsx(View, { style: [styles.rangeThumb, { left: `${normalized * 100}%` }] })
3869
+ /* @__PURE__ */ jsx(View, { style: styles2.rangeBase }),
3870
+ /* @__PURE__ */ jsx(View, { style: [styles2.rangeFill, { width: `${normalized * 100}%` }] }),
3871
+ /* @__PURE__ */ jsx(View, { style: [styles2.rangeThumb, { left: `${normalized * 100}%` }] })
3456
3872
  ]
3457
3873
  }
3458
3874
  ),
3459
- valueLabel ? /* @__PURE__ */ jsx(Text, { style: styles.rangeValue, children: valueLabel }) : null
3875
+ valueLabel ? /* @__PURE__ */ jsx(Text, { style: styles2.rangeValue, children: valueLabel }) : null
3460
3876
  ] });
3461
3877
  }
3462
3878
  function InspectorSection({
3463
3879
  label,
3464
3880
  children
3465
3881
  }) {
3466
- return /* @__PURE__ */ jsxs(View, { style: styles.section, children: [
3467
- /* @__PURE__ */ jsx(Text, { style: styles.sectionLabel, children: label }),
3882
+ return /* @__PURE__ */ jsxs(View, { style: styles2.section, children: [
3883
+ /* @__PURE__ */ jsx(Text, { style: styles2.sectionLabel, children: label }),
3468
3884
  children
3469
3885
  ] });
3470
3886
  }
@@ -3473,7 +3889,7 @@ function SegmentControl({
3473
3889
  value,
3474
3890
  onChange
3475
3891
  }) {
3476
- return /* @__PURE__ */ jsx(View, { style: styles.segmentGroup, children: segments.map((segment) => {
3892
+ return /* @__PURE__ */ jsx(View, { style: styles2.segmentGroup, children: segments.map((segment) => {
3477
3893
  const selected = segment.value === value;
3478
3894
  return /* @__PURE__ */ jsxs(
3479
3895
  Pressable,
@@ -3482,15 +3898,15 @@ function SegmentControl({
3482
3898
  accessibilityState: { selected },
3483
3899
  accessibilityLabel: segment.label,
3484
3900
  onPress: () => onChange(segment.value),
3485
- style: [styles.segment, selected ? styles.segmentSelected : null],
3901
+ style: [styles2.segment, selected ? styles2.segmentSelected : null],
3486
3902
  children: [
3487
3903
  segment.preview,
3488
3904
  /* @__PURE__ */ jsx(
3489
3905
  Text,
3490
3906
  {
3491
3907
  style: [
3492
- styles.segmentLabel,
3493
- selected ? styles.segmentLabelSelected : null
3908
+ styles2.segmentLabel,
3909
+ selected ? styles2.segmentLabelSelected : null
3494
3910
  ],
3495
3911
  children: segment.label
3496
3912
  }
@@ -3526,11 +3942,11 @@ function NativeVectorStyleInspector({
3526
3942
  View,
3527
3943
  {
3528
3944
  pointerEvents: "auto",
3529
- style: [styles.shell, style],
3945
+ style: [styles2.shell, style],
3530
3946
  accessibilityRole: "summary",
3531
3947
  accessibilityLabel: toolId === "marker" ? "Configura\xE7\xF5es do marcador" : "Configura\xE7\xF5es da caneta",
3532
3948
  children: [
3533
- /* @__PURE__ */ jsx(InspectorSection, { label: "Cor", children: /* @__PURE__ */ jsx(View, { style: styles.palette, children: NATIVE_STYLE_PALETTE.map((color) => {
3949
+ /* @__PURE__ */ jsx(InspectorSection, { label: "Cor", children: /* @__PURE__ */ jsx(View, { style: styles2.palette, children: NATIVE_STYLE_PALETTE.map((color) => {
3534
3950
  const selected = hexesEqual(color.hex, hex);
3535
3951
  return /* @__PURE__ */ jsx(
3536
3952
  Pressable,
@@ -3540,7 +3956,7 @@ function NativeVectorStyleInspector({
3540
3956
  accessibilityLabel: color.name,
3541
3957
  onPress: () => onChange({ stroke: color.hex }),
3542
3958
  style: [
3543
- styles.swatch,
3959
+ styles2.swatch,
3544
3960
  {
3545
3961
  backgroundColor: color.hex,
3546
3962
  borderWidth: selected ? 2 : 1,
@@ -3582,7 +3998,7 @@ function NativeVectorStyleInspector({
3582
3998
  }
3583
3999
  );
3584
4000
  }
3585
- var styles = StyleSheet.create({
4001
+ var styles2 = StyleSheet.create({
3586
4002
  shell: {
3587
4003
  minWidth: 240,
3588
4004
  paddingHorizontal: 14,
@@ -3894,7 +4310,7 @@ function NativeVectorToolbar({
3894
4310
  View,
3895
4311
  {
3896
4312
  accessibilityLabel,
3897
- style: [styles2.shell, style],
4313
+ style: [styles3.shell, style],
3898
4314
  pointerEvents: "box-none",
3899
4315
  children: [
3900
4316
  /* @__PURE__ */ jsxs(
@@ -3903,8 +4319,8 @@ function NativeVectorToolbar({
3903
4319
  horizontal: true,
3904
4320
  showsHorizontalScrollIndicator: false,
3905
4321
  contentContainerStyle: [
3906
- styles2.content,
3907
- density === "comfortable" ? styles2.comfortableContent : void 0,
4322
+ styles3.content,
4323
+ density === "comfortable" ? styles3.comfortableContent : void 0,
3908
4324
  contentContainerStyle
3909
4325
  ],
3910
4326
  children: [
@@ -3921,21 +4337,21 @@ function NativeVectorToolbar({
3921
4337
  disabled: toolLockDisabled,
3922
4338
  onPress: toggleToolLock,
3923
4339
  style: ({ pressed }) => [
3924
- styles2.toolButton,
3925
- density === "comfortable" ? styles2.comfortableToolButton : void 0,
3926
- toolLocked ? styles2.activeToolButton : void 0,
3927
- pressed && !toolLockDisabled ? styles2.pressedToolButton : void 0,
3928
- toolLockDisabled ? styles2.disabledToolButton : void 0
4340
+ styles3.toolButton,
4341
+ density === "comfortable" ? styles3.comfortableToolButton : void 0,
4342
+ toolLocked ? styles3.activeToolButton : void 0,
4343
+ pressed && !toolLockDisabled ? styles3.pressedToolButton : void 0,
4344
+ toolLockDisabled ? styles3.disabledToolButton : void 0
3929
4345
  ],
3930
4346
  children: renderToolLockIcon?.({
3931
4347
  locked: toolLocked,
3932
4348
  disabled: toolLockDisabled,
3933
4349
  foregroundColor: "#18181b",
3934
4350
  onToggle: toggleToolLock
3935
- }) ?? /* @__PURE__ */ jsx(Text, { style: styles2.lockGlyph, children: toolLocked ? "L" : "U" })
4351
+ }) ?? /* @__PURE__ */ jsx(Text, { style: styles3.lockGlyph, children: toolLocked ? "L" : "U" })
3936
4352
  }
3937
4353
  ),
3938
- /* @__PURE__ */ jsx(View, { style: styles2.toolLockDivider })
4354
+ /* @__PURE__ */ jsx(View, { style: styles3.toolLockDivider })
3939
4355
  ] }) : null,
3940
4356
  toolbarTools.map(
3941
4357
  (tool) => renderNativeToolButton({
@@ -3953,7 +4369,7 @@ function NativeVectorToolbar({
3953
4369
  renderToolButton
3954
4370
  })
3955
4371
  ),
3956
- showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: styles2.overflowSpacer }) : null,
4372
+ showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: styles3.overflowSpacer }) : null,
3957
4373
  showOverflowMenu ? /* @__PURE__ */ jsxs(
3958
4374
  Pressable,
3959
4375
  {
@@ -3967,27 +4383,27 @@ function NativeVectorToolbar({
3967
4383
  disabled,
3968
4384
  onPress: toggleOverflow,
3969
4385
  style: ({ pressed }) => [
3970
- styles2.overflowTrigger,
3971
- overflowOpen || activeOverflowTool ? styles2.activeToolButton : void 0,
3972
- pressed && !disabled ? styles2.pressedToolButton : void 0,
3973
- disabled ? styles2.disabledToolButton : void 0
4386
+ styles3.overflowTrigger,
4387
+ overflowOpen || activeOverflowTool ? styles3.activeToolButton : void 0,
4388
+ pressed && !disabled ? styles3.pressedToolButton : void 0,
4389
+ disabled ? styles3.disabledToolButton : void 0
3974
4390
  ],
3975
4391
  children: [
3976
- /* @__PURE__ */ jsx(View, { style: styles2.iconSlot, children: activeOverflowTool ? renderToolIcon?.({
4392
+ /* @__PURE__ */ jsx(View, { style: styles3.iconSlot, children: activeOverflowTool ? renderToolIcon?.({
3977
4393
  tool: activeOverflowTool,
3978
4394
  selected: true,
3979
4395
  disabled,
3980
4396
  foregroundColor: "#18181b",
3981
4397
  onSelect: () => onChange(activeOverflowTool.id)
3982
- }) ?? renderNativeToolFallback(activeOverflowTool, "#18181b") : renderOverflowIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text, { style: styles2.shapesGlyph, children: "S" }) }),
3983
- renderOverflowChevronIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text, { style: styles2.chevronGlyph, children: overflowOpen ? "^" : "v" })
4398
+ }) ?? renderNativeToolFallback(activeOverflowTool, "#18181b") : renderOverflowIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text, { style: styles3.shapesGlyph, children: "S" }) }),
4399
+ renderOverflowChevronIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text, { style: styles3.chevronGlyph, children: overflowOpen ? "^" : "v" })
3984
4400
  ]
3985
4401
  }
3986
4402
  ) : null
3987
4403
  ]
3988
4404
  }
3989
4405
  ),
3990
- overflowOpen && showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: [styles2.overflowPanel, overflowPanelStyle], children: remainingOverflowTools.map(
4406
+ overflowOpen && showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: [styles3.overflowPanel, overflowPanelStyle], children: remainingOverflowTools.map(
3991
4407
  (tool) => renderNativeToolButton({
3992
4408
  tool,
3993
4409
  value,
@@ -3998,7 +4414,7 @@ function NativeVectorToolbar({
3998
4414
  disabled,
3999
4415
  disabledIds,
4000
4416
  density: "compact",
4001
- toolButtonStyle: styles2.overflowToolButton,
4417
+ toolButtonStyle: styles3.overflowToolButton,
4002
4418
  activeToolButtonStyle,
4003
4419
  toolLabelStyle,
4004
4420
  activeToolLabelStyle,
@@ -4039,22 +4455,22 @@ function renderNativeToolButton(input) {
4039
4455
  disabled: toolDisabled,
4040
4456
  onPress: onSelect,
4041
4457
  style: ({ pressed }) => [
4042
- styles2.toolButton,
4043
- input.density === "comfortable" ? styles2.comfortableToolButton : void 0,
4458
+ styles3.toolButton,
4459
+ input.density === "comfortable" ? styles3.comfortableToolButton : void 0,
4044
4460
  input.toolButtonStyle,
4045
- selected ? styles2.activeToolButton : void 0,
4461
+ selected ? styles3.activeToolButton : void 0,
4046
4462
  selected ? input.activeToolButtonStyle : void 0,
4047
- pressed && !toolDisabled ? styles2.pressedToolButton : void 0,
4048
- toolDisabled ? styles2.disabledToolButton : void 0
4463
+ pressed && !toolDisabled ? styles3.pressedToolButton : void 0,
4464
+ toolDisabled ? styles3.disabledToolButton : void 0
4049
4465
  ],
4050
4466
  children: [
4051
- /* @__PURE__ */ jsx(View, { style: styles2.iconSlot, children: icon }),
4467
+ /* @__PURE__ */ jsx(View, { style: styles3.iconSlot, children: icon }),
4052
4468
  input.density === "comfortable" ? /* @__PURE__ */ jsx(
4053
4469
  Text,
4054
4470
  {
4055
4471
  numberOfLines: 1,
4056
4472
  style: [
4057
- styles2.toolLabel,
4473
+ styles3.toolLabel,
4058
4474
  { color: foregroundColor },
4059
4475
  input.toolLabelStyle,
4060
4476
  selected ? input.activeToolLabelStyle : void 0
@@ -4074,9 +4490,9 @@ function renderNativeToolFallback(tool, foregroundColor, toolLabelStyle) {
4074
4490
  if (tool.id === "link") {
4075
4491
  return /* @__PURE__ */ jsx(NativeLinkToolIcon, { color: foregroundColor });
4076
4492
  }
4077
- return /* @__PURE__ */ jsx(Text, { style: [styles2.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
4493
+ return /* @__PURE__ */ jsx(Text, { style: [styles3.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
4078
4494
  }
4079
- var styles2 = StyleSheet.create({
4495
+ var styles3 = StyleSheet.create({
4080
4496
  shell: {
4081
4497
  borderRadius: 8,
4082
4498
  borderWidth: StyleSheet.hairlineWidth,
@@ -4672,6 +5088,84 @@ function resizeItemByHandle(item, start, handle, currentWorld) {
4672
5088
  }
4673
5089
  return { ...item, x: nb.x, y: nb.y, bounds: nb };
4674
5090
  }
5091
+
5092
+ // src/native/native-remote-presence-hit-test.ts
5093
+ var REMOTE_CURSOR_SCREEN_PX2 = 22;
5094
+ var REMOTE_LABEL_SCREEN_PX2 = 12;
5095
+ var REMOTE_LABEL_OFFSET_X = 14;
5096
+ var REMOTE_LABEL_BASELINE_OFFSET_Y = 18;
5097
+ var REMOTE_LABEL_AVERAGE_CHAR_PX = 7;
5098
+ var REMOTE_LABEL_MAX_WIDTH_PX = 180;
5099
+ var REMOTE_LABEL_MIN_HIT_WIDTH_PX = 44;
5100
+ var REMOTE_LABEL_HIT_PADDING_X = 10;
5101
+ var REMOTE_LABEL_HIT_PADDING_Y = 10;
5102
+ var REMOTE_CURSOR_MIN_HIT_SIZE_PX = 36;
5103
+ function pointInScreenRect(point, rect) {
5104
+ return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
5105
+ }
5106
+ function displayLabelForPeer(peer) {
5107
+ const displayName = peer.displayName?.trim();
5108
+ if (displayName) return displayName;
5109
+ return null;
5110
+ }
5111
+ function followIdForPeer(peer) {
5112
+ return peer.clientId ?? peer.peerId ?? peer.id;
5113
+ }
5114
+ function estimateLabelWidth(label) {
5115
+ return Math.max(
5116
+ REMOTE_LABEL_MIN_HIT_WIDTH_PX,
5117
+ Math.min(REMOTE_LABEL_MAX_WIDTH_PX, label.length * REMOTE_LABEL_AVERAGE_CHAR_PX)
5118
+ );
5119
+ }
5120
+ function buildHit(peer, camera, point, target) {
5121
+ const world = camera.screenToWorld(point.x, point.y);
5122
+ return {
5123
+ peer,
5124
+ followId: followIdForPeer(peer),
5125
+ target,
5126
+ screenX: point.x,
5127
+ screenY: point.y,
5128
+ worldX: world.worldX,
5129
+ worldY: world.worldY,
5130
+ ...peer.clientId ? { clientId: peer.clientId } : {},
5131
+ ...peer.peerId ? { peerId: peer.peerId } : {}
5132
+ };
5133
+ }
5134
+ function hitTestNativeRemotePresence(peers, camera, point) {
5135
+ for (let index = peers.length - 1; index >= 0; index -= 1) {
5136
+ const peer = peers[index];
5137
+ if (!peer || peer.isSelf || !peer.cursor) continue;
5138
+ const cursorScreen = camera.worldToScreen(peer.cursor.x, peer.cursor.y);
5139
+ const label = displayLabelForPeer(peer);
5140
+ if (label) {
5141
+ const labelX = cursorScreen.screenX + REMOTE_LABEL_OFFSET_X;
5142
+ const labelBaselineY = cursorScreen.screenY + REMOTE_LABEL_BASELINE_OFFSET_Y;
5143
+ const labelRect = {
5144
+ x: labelX - REMOTE_LABEL_HIT_PADDING_X,
5145
+ y: labelBaselineY - REMOTE_LABEL_SCREEN_PX2 - REMOTE_LABEL_HIT_PADDING_Y,
5146
+ width: estimateLabelWidth(label) + REMOTE_LABEL_HIT_PADDING_X * 2,
5147
+ height: REMOTE_LABEL_SCREEN_PX2 + REMOTE_LABEL_HIT_PADDING_Y * 2
5148
+ };
5149
+ if (pointInScreenRect(point, labelRect)) {
5150
+ return buildHit(peer, camera, point, "label");
5151
+ }
5152
+ }
5153
+ const cursorHitSize = Math.max(
5154
+ REMOTE_CURSOR_MIN_HIT_SIZE_PX,
5155
+ REMOTE_CURSOR_SCREEN_PX2
5156
+ );
5157
+ const cursorRect = {
5158
+ x: cursorScreen.screenX - (cursorHitSize - REMOTE_CURSOR_SCREEN_PX2) / 2,
5159
+ y: cursorScreen.screenY - (cursorHitSize - REMOTE_CURSOR_SCREEN_PX2) / 2,
5160
+ width: cursorHitSize,
5161
+ height: cursorHitSize
5162
+ };
5163
+ if (pointInScreenRect(point, cursorRect)) {
5164
+ return buildHit(peer, camera, point, "cursor");
5165
+ }
5166
+ }
5167
+ return null;
5168
+ }
4675
5169
  var DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS = {
4676
5170
  title: "Add link",
4677
5171
  description: "Paste the link you want to add to the board.",
@@ -4768,6 +5262,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4768
5262
  onLinkToolRequest,
4769
5263
  linkToolDialogLabels,
4770
5264
  onWorldPointerDown,
5265
+ onRemotePresencePress,
4771
5266
  onWorldPointerMove,
4772
5267
  onWorldPointerLeave,
4773
5268
  onPlacementPreviewChange,
@@ -4791,6 +5286,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4791
5286
  onLinkToolRequestRef.current = onLinkToolRequest;
4792
5287
  const onWorldPointerDownRef = useRef(onWorldPointerDown);
4793
5288
  onWorldPointerDownRef.current = onWorldPointerDown;
5289
+ const onRemotePresencePressRef = useRef(onRemotePresencePress);
5290
+ onRemotePresencePressRef.current = onRemotePresencePress;
4794
5291
  const onWorldPointerMoveRef = useRef(onWorldPointerMove);
4795
5292
  onWorldPointerMoveRef.current = onWorldPointerMove;
4796
5293
  const onWorldPointerLeaveRef = useRef(onWorldPointerLeave);
@@ -4811,6 +5308,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4811
5308
  itemsRef.current = items;
4812
5309
  const selectedIdsRef = useRef(selectedIds);
4813
5310
  selectedIdsRef.current = selectedIds;
5311
+ const remotePresenceRef = useRef(remotePresence);
5312
+ remotePresenceRef.current = remotePresence;
4814
5313
  const dragStateRef = useRef({ kind: "idle" });
4815
5314
  const [placementPreview, setPlacementPreviewState] = useState(null);
4816
5315
  const setRealtimePlacementPreview = useCallback(
@@ -4970,13 +5469,19 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4970
5469
  const sx = point.x;
4971
5470
  const sy = point.y;
4972
5471
  updateToolCursorPoint(point);
5472
+ const cam = cameraRef.current;
5473
+ if (!cam) return;
5474
+ const remotePresenceHit = interactive && onRemotePresencePressRef.current ? hitTestNativeRemotePresence(remotePresenceRef.current, cam, point) : null;
5475
+ if (remotePresenceHit) {
5476
+ dragStateRef.current = { kind: "idle" };
5477
+ onRemotePresencePressRef.current?.(remotePresenceHit);
5478
+ return;
5479
+ }
4973
5480
  if (!interactive) {
4974
5481
  dragStateRef.current = { kind: "pan" };
4975
5482
  return;
4976
5483
  }
4977
5484
  const tool = toolIdRef.current;
4978
- const cam = cameraRef.current;
4979
- if (!cam) return;
4980
5485
  const { worldX, worldY } = screenToWorld(sx, sy);
4981
5486
  onWorldPointerMoveRef.current?.({ x: worldX, y: worldY });
4982
5487
  if (tool === "hand") {
@@ -5822,9 +6327,9 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5822
6327
  transparent: true,
5823
6328
  visible: pendingNativeLinkRequest !== null,
5824
6329
  onRequestClose: closeNativeLinkDialog,
5825
- children: /* @__PURE__ */ jsx(View, { style: styles3.nativeLinkDialogBackdrop, children: /* @__PURE__ */ jsxs(View, { style: styles3.nativeLinkDialogCard, children: [
5826
- /* @__PURE__ */ jsx(Text, { style: styles3.nativeLinkDialogTitle, children: nativeLinkDialogLabels.title }),
5827
- /* @__PURE__ */ jsx(Text, { style: styles3.nativeLinkDialogDescription, children: nativeLinkDialogLabels.description }),
6330
+ children: /* @__PURE__ */ jsx(View, { style: styles4.nativeLinkDialogBackdrop, children: /* @__PURE__ */ jsxs(View, { style: styles4.nativeLinkDialogCard, children: [
6331
+ /* @__PURE__ */ jsx(Text, { style: styles4.nativeLinkDialogTitle, children: nativeLinkDialogLabels.title }),
6332
+ /* @__PURE__ */ jsx(Text, { style: styles4.nativeLinkDialogDescription, children: nativeLinkDialogLabels.description }),
5828
6333
  /* @__PURE__ */ jsx(
5829
6334
  TextInput,
5830
6335
  {
@@ -5836,21 +6341,21 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5836
6341
  onSubmitEditing: submitNativeLinkDialog,
5837
6342
  placeholder: nativeLinkDialogLabels.inputPlaceholder,
5838
6343
  returnKeyType: "done",
5839
- style: styles3.nativeLinkDialogInput,
6344
+ style: styles4.nativeLinkDialogInput,
5840
6345
  value: nativeLinkInputValue
5841
6346
  }
5842
6347
  ),
5843
- /* @__PURE__ */ jsxs(View, { style: styles3.nativeLinkDialogActions, children: [
6348
+ /* @__PURE__ */ jsxs(View, { style: styles4.nativeLinkDialogActions, children: [
5844
6349
  /* @__PURE__ */ jsx(
5845
6350
  Pressable,
5846
6351
  {
5847
6352
  accessibilityRole: "button",
5848
6353
  onPress: closeNativeLinkDialog,
5849
6354
  style: ({ pressed }) => [
5850
- styles3.nativeLinkDialogButton,
5851
- pressed ? styles3.nativeLinkDialogButtonPressed : void 0
6355
+ styles4.nativeLinkDialogButton,
6356
+ pressed ? styles4.nativeLinkDialogButtonPressed : void 0
5852
6357
  ],
5853
- children: /* @__PURE__ */ jsx(Text, { style: styles3.nativeLinkDialogButtonText, children: nativeLinkDialogLabels.cancelLabel })
6358
+ children: /* @__PURE__ */ jsx(Text, { style: styles4.nativeLinkDialogButtonText, children: nativeLinkDialogLabels.cancelLabel })
5854
6359
  }
5855
6360
  ),
5856
6361
  /* @__PURE__ */ jsx(
@@ -5861,12 +6366,12 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5861
6366
  disabled: !nativeLinkCanSubmit,
5862
6367
  onPress: submitNativeLinkDialog,
5863
6368
  style: ({ pressed }) => [
5864
- styles3.nativeLinkDialogButton,
5865
- styles3.nativeLinkDialogPrimaryButton,
5866
- pressed && nativeLinkCanSubmit ? styles3.nativeLinkDialogPrimaryButtonPressed : void 0,
5867
- !nativeLinkCanSubmit ? styles3.nativeLinkDialogDisabledButton : void 0
6369
+ styles4.nativeLinkDialogButton,
6370
+ styles4.nativeLinkDialogPrimaryButton,
6371
+ pressed && nativeLinkCanSubmit ? styles4.nativeLinkDialogPrimaryButtonPressed : void 0,
6372
+ !nativeLinkCanSubmit ? styles4.nativeLinkDialogDisabledButton : void 0
5868
6373
  ],
5869
- children: /* @__PURE__ */ jsx(Text, { style: styles3.nativeLinkDialogPrimaryButtonText, children: nativeLinkDialogLabels.addLabel })
6374
+ children: /* @__PURE__ */ jsx(Text, { style: styles4.nativeLinkDialogPrimaryButtonText, children: nativeLinkDialogLabels.addLabel })
5870
6375
  }
5871
6376
  )
5872
6377
  ] })
@@ -5875,7 +6380,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5875
6380
  )
5876
6381
  ] });
5877
6382
  });
5878
- var styles3 = StyleSheet.create({
6383
+ var styles4 = StyleSheet.create({
5879
6384
  nativeLinkDialogBackdrop: {
5880
6385
  flex: 1,
5881
6386
  alignItems: "center",
@@ -5961,6 +6466,6 @@ var styles3 = StyleSheet.create({
5961
6466
  }
5962
6467
  });
5963
6468
 
5964
- export { DEFAULT_LINK_CARD_SIZE, DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, NativeInteractionOverlay, NativeSceneRenderer, NativeShapeRenderer, NativeVectorStyleInspector, NativeVectorToolbar, NativeVectorViewport, createFreehandStrokeItem, createImageItem, createLinkItem, createShapeId, getLinkData, isLinkItem, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
6469
+ export { DEFAULT_LINK_CARD_SIZE, DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, NativeImagesMenu, NativeInteractionOverlay, NativeSceneRenderer, NativeShapeRenderer, NativeVectorStyleInspector, NativeVectorToolbar, NativeVectorViewport, createFreehandStrokeItem, createImageItem, createLinkItem, createShapeId, getLinkData, isLinkItem, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
5965
6470
  //# sourceMappingURL=native.js.map
5966
6471
  //# sourceMappingURL=native.js.map