canvu-react 0.4.35 → 0.4.37

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.cjs CHANGED
@@ -10,8 +10,6 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
10
 
11
11
  var getStroke__default = /*#__PURE__*/_interopDefault(getStroke);
12
12
 
13
- // src/scene/shape-builders.ts
14
-
15
13
  // src/math/rect.ts
16
14
  function rectsIntersect(a, b) {
17
15
  return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
@@ -28,12 +26,36 @@ function normalizeRect(r) {
28
26
  }
29
27
 
30
28
  // src/scene/custom-shape.ts
29
+ function expandCustomShapeTemplate(template, width, height) {
30
+ return template.replace(/\{\{w\}\}/g, String(width)).replace(/\{\{h\}\}/g, String(height)).replace(/\{\{width\}\}/g, String(width)).replace(/\{\{height\}\}/g, String(height));
31
+ }
32
+ function resolveCustomInner(content, size) {
33
+ if ("render" in content) {
34
+ return content.render(size);
35
+ }
36
+ return expandCustomShapeTemplate(content.svg, size.width, size.height);
37
+ }
31
38
  function buildCustomShapeChildrenSvg(inner, intrinsic, bounds) {
32
39
  const b = normalizeRect(bounds);
33
40
  const sx = b.width / intrinsic.width;
34
41
  const sy = b.height / intrinsic.height;
35
42
  return `<g transform="scale(${sx},${sy})">${inner}</g>`;
36
43
  }
44
+ function createCustomShapeItem(id, bounds, content) {
45
+ const r = normalizeRect(bounds);
46
+ const intrinsic = { width: r.width, height: r.height };
47
+ const inner = resolveCustomInner(content, intrinsic);
48
+ return {
49
+ id,
50
+ x: r.x,
51
+ y: r.y,
52
+ bounds: { ...r },
53
+ toolKind: "custom",
54
+ customIntrinsicSize: intrinsic,
55
+ customInnerSvg: inner,
56
+ childrenSvg: buildCustomShapeChildrenSvg(inner, intrinsic, r)
57
+ };
58
+ }
37
59
 
38
60
  // src/scene/link-item.ts
39
61
  var LINK_PLUGIN_KEY = "canvuLink";
@@ -146,6 +168,9 @@ function getLinkData(item) {
146
168
  const entry = item.pluginData?.[LINK_PLUGIN_KEY];
147
169
  return isCanvuLinkData(entry) ? entry : null;
148
170
  }
171
+ function isLinkItem(item) {
172
+ return getLinkData(item) != null;
173
+ }
149
174
  function rebuildLinkItemSvg(item) {
150
175
  const link = getLinkData(item);
151
176
  if (!link) return item;
@@ -179,6 +204,26 @@ function rebuildLinkItemSvg(item) {
179
204
  childrenSvg: buildLinkCardSvg(width, height, link)
180
205
  };
181
206
  }
207
+ function createLinkItem(id, bounds, link) {
208
+ const width = bounds.width || DEFAULT_LINK_CARD_WIDTH;
209
+ const height = bounds.height || DEFAULT_LINK_CARD_HEIGHT;
210
+ const item = createCustomShapeItem(
211
+ id,
212
+ { ...bounds, width, height },
213
+ { render: (size) => buildLinkCardSvg(size.width, size.height, link) }
214
+ );
215
+ return rebuildLinkItemSvg({
216
+ ...item,
217
+ pluginData: {
218
+ ...item.pluginData ?? {},
219
+ [LINK_PLUGIN_KEY]: link
220
+ }
221
+ });
222
+ }
223
+ var DEFAULT_LINK_CARD_SIZE = {
224
+ width: DEFAULT_LINK_CARD_WIDTH,
225
+ height: DEFAULT_LINK_CARD_HEIGHT
226
+ };
182
227
 
183
228
  // src/scene/text-svg.ts
184
229
  function escapeSvgTextContent(s) {
@@ -1404,6 +1449,50 @@ function smoothFreehandPointsToPathD(points) {
1404
1449
  d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
1405
1450
  return d;
1406
1451
  }
1452
+
1453
+ // src/native/native-link-card.ts
1454
+ function linkHostname(href) {
1455
+ try {
1456
+ return new URL(href).hostname.replace(/^www\./, "");
1457
+ } catch {
1458
+ return href;
1459
+ }
1460
+ }
1461
+ function linkProtocol(href) {
1462
+ try {
1463
+ return new URL(href).protocol;
1464
+ } catch {
1465
+ return "";
1466
+ }
1467
+ }
1468
+ function linkInitial(value) {
1469
+ const first = value.trim().charAt(0).toUpperCase();
1470
+ return first || "L";
1471
+ }
1472
+ function normalizeNativeLinkHref(value) {
1473
+ const trimmed = value.trim();
1474
+ if (!trimmed) return null;
1475
+ if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) return trimmed;
1476
+ return `https://${trimmed}`;
1477
+ }
1478
+ function buildNativeLinkCardDisplay(link) {
1479
+ const hostname = linkHostname(link.href);
1480
+ const title = link.title?.trim() || hostname || "Link";
1481
+ return {
1482
+ title,
1483
+ subtitle: hostname || link.href,
1484
+ initial: linkInitial(hostname || title),
1485
+ secure: linkProtocol(link.href) === "https:"
1486
+ };
1487
+ }
1488
+ function createNativeLinkCardBoundsAtPoint(point) {
1489
+ return {
1490
+ x: point.x - DEFAULT_LINK_CARD_SIZE.width / 2,
1491
+ y: point.y - DEFAULT_LINK_CARD_SIZE.height / 2,
1492
+ width: DEFAULT_LINK_CARD_SIZE.width,
1493
+ height: DEFAULT_LINK_CARD_SIZE.height
1494
+ };
1495
+ }
1407
1496
  var DEFAULT_NATIVE_IMAGE_CACHE_MAX_ENTRIES = 96;
1408
1497
  function disposeCachedImage(image) {
1409
1498
  try {
@@ -2093,7 +2182,119 @@ function localBounds(bounds) {
2093
2182
  const b = normalizeRect(bounds);
2094
2183
  return { w: Math.max(0, b.width), h: Math.max(0, b.height) };
2095
2184
  }
2185
+ function truncateText(value, maxChars) {
2186
+ if (value.length <= maxChars) return value;
2187
+ return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
2188
+ }
2189
+ function NativeLinkCardRenderer({
2190
+ item,
2191
+ link
2192
+ }) {
2193
+ const bounds = normalizeRect(item.bounds);
2194
+ const scaleX = Math.max(0.01, bounds.width / DEFAULT_LINK_CARD_SIZE.width);
2195
+ const scaleY = Math.max(0.01, bounds.height / DEFAULT_LINK_CARD_SIZE.height);
2196
+ const display = buildNativeLinkCardDisplay(link);
2197
+ const titleFont = reactNativeSkia.matchFont({ fontSize: 14.5, fontWeight: "700" });
2198
+ const subtitleFont = reactNativeSkia.matchFont({ fontSize: 12.5 });
2199
+ const initialFont = reactNativeSkia.matchFont({ fontSize: 17, fontWeight: "700" });
2200
+ const title = truncateText(display.title, 28);
2201
+ const subtitle = truncateText(display.subtitle, display.secure ? 28 : 31);
2202
+ const subtitleX = display.secure ? 82 : 69;
2203
+ const subtitleWidth = display.secure ? 188 : 201;
2204
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNativeSkia.Group, { transform: [{ scaleX }, { scaleY }], children: [
2205
+ /* @__PURE__ */ jsxRuntime.jsx(
2206
+ reactNativeSkia.RoundedRect,
2207
+ {
2208
+ x: 0,
2209
+ y: 0,
2210
+ width: DEFAULT_LINK_CARD_SIZE.width,
2211
+ height: DEFAULT_LINK_CARD_SIZE.height,
2212
+ r: 16,
2213
+ color: "#ffffff",
2214
+ style: "fill",
2215
+ antiAlias: true
2216
+ }
2217
+ ),
2218
+ /* @__PURE__ */ jsxRuntime.jsx(
2219
+ reactNativeSkia.RoundedRect,
2220
+ {
2221
+ x: 0.5,
2222
+ y: 0.5,
2223
+ width: DEFAULT_LINK_CARD_SIZE.width - 1,
2224
+ height: DEFAULT_LINK_CARD_SIZE.height - 1,
2225
+ r: 15.5,
2226
+ color: "#dfe4ea",
2227
+ style: "stroke",
2228
+ strokeWidth: 1,
2229
+ antiAlias: true
2230
+ }
2231
+ ),
2232
+ /* @__PURE__ */ jsxRuntime.jsx(
2233
+ reactNativeSkia.RoundedRect,
2234
+ {
2235
+ x: 14,
2236
+ y: 14,
2237
+ width: 42,
2238
+ height: 42,
2239
+ r: 11,
2240
+ color: "#315bd6",
2241
+ style: "fill",
2242
+ antiAlias: true
2243
+ }
2244
+ ),
2245
+ /* @__PURE__ */ jsxRuntime.jsx(
2246
+ reactNativeSkia.Text,
2247
+ {
2248
+ x: 29.5,
2249
+ y: 41,
2250
+ text: display.initial,
2251
+ color: "#ffffff",
2252
+ font: initialFont
2253
+ }
2254
+ ),
2255
+ /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { clip: { x: 69, y: 13, width: 215, height: 24 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Text, { x: 69, y: 32, text: title, color: "#1f2937", font: titleFont }) }),
2256
+ display.secure ? /* @__PURE__ */ jsxRuntime.jsxs(reactNativeSkia.Group, { transform: [{ translateX: 69 }, { translateY: 41 }], children: [
2257
+ /* @__PURE__ */ jsxRuntime.jsx(
2258
+ reactNativeSkia.Rect,
2259
+ {
2260
+ x: 1.5,
2261
+ y: 4.5,
2262
+ width: 7,
2263
+ height: 6,
2264
+ color: "#6b7280",
2265
+ style: "stroke",
2266
+ strokeWidth: 1.3
2267
+ }
2268
+ ),
2269
+ /* @__PURE__ */ jsxRuntime.jsx(
2270
+ reactNativeSkia.Path,
2271
+ {
2272
+ path: "M3 4.5 V3 a2 2 0 0 1 4 0 v1.5",
2273
+ color: "#6b7280",
2274
+ style: "stroke",
2275
+ strokeWidth: 1.3,
2276
+ strokeCap: "round",
2277
+ strokeJoin: "round"
2278
+ }
2279
+ )
2280
+ ] }) : null,
2281
+ /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { clip: { x: subtitleX, y: 34, width: subtitleWidth, height: 22 }, children: /* @__PURE__ */ jsxRuntime.jsx(
2282
+ reactNativeSkia.Text,
2283
+ {
2284
+ x: subtitleX,
2285
+ y: 51,
2286
+ text: subtitle,
2287
+ color: "#6b7280",
2288
+ font: subtitleFont
2289
+ }
2290
+ ) })
2291
+ ] });
2292
+ }
2096
2293
  function NativeShapeRenderer({ item }) {
2294
+ const link = getLinkData(item);
2295
+ if (link) {
2296
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeLinkCardRenderer, { item, link });
2297
+ }
2097
2298
  const style = resolveStrokeStyle(item);
2098
2299
  const k = item.toolKind;
2099
2300
  if (k === "rect") {
@@ -3516,9 +3717,14 @@ var DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = [
3516
3717
  "line",
3517
3718
  "marker",
3518
3719
  "laser",
3519
- "image"
3720
+ "image",
3721
+ "link"
3520
3722
  ];
3521
3723
  var CLOUD_TOOL_ICON_PATH = "M17.5 19H8.5a6.5 6.5 0 1 1 6.17-8.55A4.75 4.75 0 0 1 17.5 9.5a4.75 4.75 0 0 1 0 9.5Z";
3724
+ var LINK_TOOL_ICON_PATHS = [
3725
+ "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
3726
+ "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
3727
+ ];
3522
3728
  function NativeCloudToolIcon({
3523
3729
  color,
3524
3730
  size = 19,
@@ -3537,6 +3743,25 @@ function NativeCloudToolIcon({
3537
3743
  }
3538
3744
  ) }) });
3539
3745
  }
3746
+ function NativeLinkToolIcon({
3747
+ color,
3748
+ size = 20,
3749
+ strokeWidth = 2.4
3750
+ }) {
3751
+ const scale = size / 24;
3752
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Canvas, { style: { width: size, height: size }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { transform: [{ scale }], children: LINK_TOOL_ICON_PATHS.map((path) => /* @__PURE__ */ jsxRuntime.jsx(
3753
+ reactNativeSkia.Path,
3754
+ {
3755
+ path,
3756
+ color,
3757
+ style: "stroke",
3758
+ strokeWidth,
3759
+ strokeCap: "round",
3760
+ strokeJoin: "round"
3761
+ },
3762
+ path
3763
+ )) }) });
3764
+ }
3540
3765
  var DEFAULT_NATIVE_VECTOR_TOOLS = [
3541
3766
  { id: "hand", label: "Hand", shortcutHint: "H", shortLabel: "H" },
3542
3767
  { id: "select", label: "Select", shortcutHint: "V", shortLabel: "V" },
@@ -3574,7 +3799,8 @@ var DEFAULT_NATIVE_VECTOR_TOOLS = [
3574
3799
  shortLabel: "E"
3575
3800
  },
3576
3801
  { id: "text", label: "Text", shortcutHint: "T", shortLabel: "T" },
3577
- { id: "image", label: "File", shortcutHint: "I", shortLabel: "I" }
3802
+ { id: "image", label: "File", shortcutHint: "I", shortLabel: "I" },
3803
+ { id: "link", label: "Link", shortcutHint: "N", shortLabel: "L" }
3578
3804
  ];
3579
3805
  function splitToolbarTools(tools, overflowToolIds) {
3580
3806
  const overflowIds = new Set(overflowToolIds);
@@ -3836,6 +4062,9 @@ function renderNativeToolFallback(tool, foregroundColor, toolLabelStyle) {
3836
4062
  if (tool.id === "architectural-cloud") {
3837
4063
  return /* @__PURE__ */ jsxRuntime.jsx(NativeCloudToolIcon, { color: foregroundColor });
3838
4064
  }
4065
+ if (tool.id === "link") {
4066
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeLinkToolIcon, { color: foregroundColor });
4067
+ }
3839
4068
  return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
3840
4069
  }
3841
4070
  var styles2 = reactNative.StyleSheet.create({
@@ -4434,6 +4663,13 @@ function resizeItemByHandle(item, start, handle, currentWorld) {
4434
4663
  }
4435
4664
  return { ...item, x: nb.x, y: nb.y, bounds: nb };
4436
4665
  }
4666
+ var DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS = {
4667
+ title: "Add link",
4668
+ description: "Paste the link you want to add to the board.",
4669
+ inputPlaceholder: "https://example.com",
4670
+ cancelLabel: "Cancel",
4671
+ addLabel: "Add"
4672
+ };
4437
4673
  var MIN_PLACE_SIZE = 8;
4438
4674
  var MIN_ARROW_DRAG_PX = 8;
4439
4675
  var TAP_PX = 20;
@@ -4516,6 +4752,8 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4516
4752
  onSelectionChange,
4517
4753
  onItemsChange,
4518
4754
  onToolChangeRequest,
4755
+ onLinkToolRequest,
4756
+ linkToolDialogLabels,
4519
4757
  onWorldPointerDown,
4520
4758
  onWorldPointerMove,
4521
4759
  onWorldPointerLeave,
@@ -4535,6 +4773,8 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4535
4773
  toolLockedRef.current = toolLocked;
4536
4774
  const onToolChangeRequestRef = react.useRef(onToolChangeRequest);
4537
4775
  onToolChangeRequestRef.current = onToolChangeRequest;
4776
+ const onLinkToolRequestRef = react.useRef(onLinkToolRequest);
4777
+ onLinkToolRequestRef.current = onLinkToolRequest;
4538
4778
  const onWorldPointerDownRef = react.useRef(onWorldPointerDown);
4539
4779
  onWorldPointerDownRef.current = onWorldPointerDown;
4540
4780
  const onWorldPointerMoveRef = react.useRef(onWorldPointerMove);
@@ -4568,6 +4808,8 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4568
4808
  );
4569
4809
  const [eraserTrail, setEraserTrail] = react.useState([]);
4570
4810
  const [laserTrail, setLaserTrail] = react.useState([]);
4811
+ const [pendingNativeLinkRequest, setPendingNativeLinkRequest] = react.useState(null);
4812
+ const [nativeLinkInputValue, setNativeLinkInputValue] = react.useState("");
4571
4813
  const laserClearTimerRef = react.useRef(null);
4572
4814
  const strokeStyleRef = react.useRef({ ...DEFAULT_STROKE_STYLE });
4573
4815
  const markerStrokeStyleRef = react.useRef({ ...MARKER_TOOL_STYLE });
@@ -4645,6 +4887,21 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4645
4887
  onToolChangeRequestRef.current?.("select");
4646
4888
  }
4647
4889
  }, []);
4890
+ const requestSelectToolAfterNativeLinkUse = react.useCallback(() => {
4891
+ onToolChangeRequestRef.current?.("select");
4892
+ }, []);
4893
+ const closeNativeLinkDialog = react.useCallback(() => {
4894
+ setPendingNativeLinkRequest(null);
4895
+ setNativeLinkInputValue("");
4896
+ }, []);
4897
+ const submitNativeLinkDialog = react.useCallback(() => {
4898
+ const href = normalizeNativeLinkHref(nativeLinkInputValue);
4899
+ if (!href || !pendingNativeLinkRequest) return;
4900
+ const inserted = pendingNativeLinkRequest.insertLink({ href });
4901
+ if (inserted) {
4902
+ closeNativeLinkDialog();
4903
+ }
4904
+ }, [closeNativeLinkDialog, nativeLinkInputValue, pendingNativeLinkRequest]);
4648
4905
  if (!cameraRef.current) {
4649
4906
  cameraRef.current = new Camera2D({ minZoom: 0.05, maxZoom: 32 });
4650
4907
  }
@@ -4877,7 +5134,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4877
5134
  });
4878
5135
  return;
4879
5136
  }
4880
- if (tool === "note" || tool === "text") {
5137
+ if (tool === "link" || tool === "note" || tool === "text") {
4881
5138
  dragStateRef.current = {
4882
5139
  kind: "tap",
4883
5140
  tool,
@@ -5213,6 +5470,43 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5213
5470
  const screenDy = point.y - st.startScreen.y;
5214
5471
  if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
5215
5472
  const change = onItemsChangeRef.current;
5473
+ if (st.tool === "link") {
5474
+ const suggestedBounds = createNativeLinkCardBoundsAtPoint(st.startWorld);
5475
+ const insertLink = (link, options = {}) => {
5476
+ const currentChange = onItemsChangeRef.current;
5477
+ if (!currentChange) return null;
5478
+ const id = options.id ?? createShapeId();
5479
+ const item = createLinkItem(id, options.bounds ?? suggestedBounds, link);
5480
+ currentChange([...itemsRef.current, item]);
5481
+ onSelectionChangeRef.current?.([id]);
5482
+ requestSelectToolAfterNativeLinkUse();
5483
+ return item;
5484
+ };
5485
+ const requestLink = onLinkToolRequestRef.current;
5486
+ if (requestLink) {
5487
+ requestLink({
5488
+ toolId: "link",
5489
+ worldX: st.startWorld.x,
5490
+ worldY: st.startWorld.y,
5491
+ screenX: st.startScreen.x,
5492
+ screenY: st.startScreen.y,
5493
+ suggestedBounds,
5494
+ insertLink
5495
+ });
5496
+ return;
5497
+ }
5498
+ setNativeLinkInputValue("");
5499
+ setPendingNativeLinkRequest({
5500
+ toolId: "link",
5501
+ worldX: st.startWorld.x,
5502
+ worldY: st.startWorld.y,
5503
+ screenX: st.startScreen.x,
5504
+ screenY: st.startScreen.y,
5505
+ suggestedBounds,
5506
+ insertLink
5507
+ });
5508
+ return;
5509
+ }
5216
5510
  if (!change) return;
5217
5511
  if (st.tool === "text") {
5218
5512
  const id = createShapeId();
@@ -5260,6 +5554,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5260
5554
  dragStateRef.current = { kind: "idle" };
5261
5555
  },
5262
5556
  [
5557
+ requestSelectToolAfterNativeLinkUse,
5263
5558
  requestSelectToolAfterUse,
5264
5559
  screenToWorld,
5265
5560
  setRealtimePlacementPreview,
@@ -5391,7 +5686,13 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5391
5686
  [requestRender, size]
5392
5687
  );
5393
5688
  const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
5394
- return /* @__PURE__ */ jsxRuntime.jsx(
5689
+ const nativeLinkDialogLabels = {
5690
+ ...DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS,
5691
+ ...linkToolDialogLabels ?? {}
5692
+ };
5693
+ const normalizedNativeLinkHref = normalizeNativeLinkHref(nativeLinkInputValue);
5694
+ const nativeLinkCanSubmit = pendingNativeLinkRequest !== null && normalizedNativeLinkHref !== null && onItemsChange != null;
5695
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5395
5696
  reactNative.View,
5396
5697
  {
5397
5698
  style: { flex: 1, overflow: "hidden" },
@@ -5405,81 +5706,227 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5405
5706
  notifyWorldPointerLeave();
5406
5707
  },
5407
5708
  ...panResponder.panHandlers,
5408
- children: size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5709
+ children: [
5710
+ size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5711
+ /* @__PURE__ */ jsxRuntime.jsx(
5712
+ NativeSceneRenderer,
5713
+ {
5714
+ items: sceneItems,
5715
+ camera,
5716
+ width: size.width,
5717
+ height: size.height
5718
+ }
5719
+ ),
5720
+ interactive && /* @__PURE__ */ jsxRuntime.jsx(
5721
+ NativeInteractionOverlay,
5722
+ {
5723
+ camera,
5724
+ width: size.width,
5725
+ height: size.height,
5726
+ selectedItems,
5727
+ showResizeHandles,
5728
+ placementPreview,
5729
+ laserTrail,
5730
+ eraserTrail,
5731
+ eraserPreviewItems: items.filter(
5732
+ (it) => eraserPreviewIds.includes(it.id)
5733
+ ),
5734
+ previewStrokeStyle: strokeStyleState,
5735
+ remotePresence
5736
+ }
5737
+ ),
5738
+ interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsxRuntime.jsx(
5739
+ reactNative.View,
5740
+ {
5741
+ pointerEvents: "box-none",
5742
+ style: styleInspectorPlacement === "top-left" ? {
5743
+ position: "absolute",
5744
+ left: 16,
5745
+ top: 104,
5746
+ alignItems: "flex-start"
5747
+ } : {
5748
+ position: "absolute",
5749
+ left: 16,
5750
+ right: 16,
5751
+ bottom: 84,
5752
+ alignItems: "center"
5753
+ },
5754
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5755
+ NativeVectorStyleInspector,
5756
+ {
5757
+ toolId: activeStyleToolId,
5758
+ value: strokeStyleState,
5759
+ onChange: patchCurrentStrokeStyle
5760
+ }
5761
+ )
5762
+ }
5763
+ ) : null,
5764
+ toolbar && /* @__PURE__ */ jsxRuntime.jsx(
5765
+ reactNative.View,
5766
+ {
5767
+ style: {
5768
+ position: "absolute",
5769
+ bottom: 16,
5770
+ left: 16,
5771
+ right: 16,
5772
+ flexDirection: "row",
5773
+ justifyContent: "center",
5774
+ alignItems: "center"
5775
+ },
5776
+ pointerEvents: "box-none",
5777
+ children: toolbar
5778
+ }
5779
+ )
5780
+ ] }),
5409
5781
  /* @__PURE__ */ jsxRuntime.jsx(
5410
- NativeSceneRenderer,
5411
- {
5412
- items: sceneItems,
5413
- camera,
5414
- width: size.width,
5415
- height: size.height
5416
- }
5417
- ),
5418
- interactive && /* @__PURE__ */ jsxRuntime.jsx(
5419
- NativeInteractionOverlay,
5420
- {
5421
- camera,
5422
- width: size.width,
5423
- height: size.height,
5424
- selectedItems,
5425
- showResizeHandles,
5426
- placementPreview,
5427
- laserTrail,
5428
- eraserTrail,
5429
- eraserPreviewItems: items.filter(
5430
- (it) => eraserPreviewIds.includes(it.id)
5431
- ),
5432
- previewStrokeStyle: strokeStyleState,
5433
- remotePresence
5434
- }
5435
- ),
5436
- interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsxRuntime.jsx(
5437
- reactNative.View,
5782
+ reactNative.Modal,
5438
5783
  {
5439
- pointerEvents: "box-none",
5440
- style: styleInspectorPlacement === "top-left" ? {
5441
- position: "absolute",
5442
- left: 16,
5443
- top: 104,
5444
- alignItems: "flex-start"
5445
- } : {
5446
- position: "absolute",
5447
- left: 16,
5448
- right: 16,
5449
- bottom: 84,
5450
- alignItems: "center"
5451
- },
5452
- children: /* @__PURE__ */ jsxRuntime.jsx(
5453
- NativeVectorStyleInspector,
5454
- {
5455
- toolId: activeStyleToolId,
5456
- value: strokeStyleState,
5457
- onChange: patchCurrentStrokeStyle
5458
- }
5459
- )
5460
- }
5461
- ) : null,
5462
- toolbar && /* @__PURE__ */ jsxRuntime.jsx(
5463
- reactNative.View,
5464
- {
5465
- style: {
5466
- position: "absolute",
5467
- bottom: 16,
5468
- left: 16,
5469
- right: 16,
5470
- flexDirection: "row",
5471
- justifyContent: "center",
5472
- alignItems: "center"
5473
- },
5474
- pointerEvents: "box-none",
5475
- children: toolbar
5784
+ animationType: "fade",
5785
+ transparent: true,
5786
+ visible: pendingNativeLinkRequest !== null,
5787
+ onRequestClose: closeNativeLinkDialog,
5788
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.nativeLinkDialogBackdrop, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.nativeLinkDialogCard, children: [
5789
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogTitle, children: nativeLinkDialogLabels.title }),
5790
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogDescription, children: nativeLinkDialogLabels.description }),
5791
+ /* @__PURE__ */ jsxRuntime.jsx(
5792
+ reactNative.TextInput,
5793
+ {
5794
+ accessibilityLabel: nativeLinkDialogLabels.title,
5795
+ autoCapitalize: "none",
5796
+ autoCorrect: false,
5797
+ keyboardType: "url",
5798
+ onChangeText: setNativeLinkInputValue,
5799
+ onSubmitEditing: submitNativeLinkDialog,
5800
+ placeholder: nativeLinkDialogLabels.inputPlaceholder,
5801
+ returnKeyType: "done",
5802
+ style: styles3.nativeLinkDialogInput,
5803
+ value: nativeLinkInputValue
5804
+ }
5805
+ ),
5806
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.nativeLinkDialogActions, children: [
5807
+ /* @__PURE__ */ jsxRuntime.jsx(
5808
+ reactNative.Pressable,
5809
+ {
5810
+ accessibilityRole: "button",
5811
+ onPress: closeNativeLinkDialog,
5812
+ style: ({ pressed }) => [
5813
+ styles3.nativeLinkDialogButton,
5814
+ pressed ? styles3.nativeLinkDialogButtonPressed : void 0
5815
+ ],
5816
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogButtonText, children: nativeLinkDialogLabels.cancelLabel })
5817
+ }
5818
+ ),
5819
+ /* @__PURE__ */ jsxRuntime.jsx(
5820
+ reactNative.Pressable,
5821
+ {
5822
+ accessibilityRole: "button",
5823
+ accessibilityState: { disabled: !nativeLinkCanSubmit },
5824
+ disabled: !nativeLinkCanSubmit,
5825
+ onPress: submitNativeLinkDialog,
5826
+ style: ({ pressed }) => [
5827
+ styles3.nativeLinkDialogButton,
5828
+ styles3.nativeLinkDialogPrimaryButton,
5829
+ pressed && nativeLinkCanSubmit ? styles3.nativeLinkDialogPrimaryButtonPressed : void 0,
5830
+ !nativeLinkCanSubmit ? styles3.nativeLinkDialogDisabledButton : void 0
5831
+ ],
5832
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogPrimaryButtonText, children: nativeLinkDialogLabels.addLabel })
5833
+ }
5834
+ )
5835
+ ] })
5836
+ ] }) })
5476
5837
  }
5477
5838
  )
5478
- ] })
5839
+ ]
5479
5840
  }
5480
5841
  );
5481
5842
  });
5843
+ var styles3 = reactNative.StyleSheet.create({
5844
+ nativeLinkDialogBackdrop: {
5845
+ flex: 1,
5846
+ alignItems: "center",
5847
+ justifyContent: "center",
5848
+ paddingHorizontal: 24,
5849
+ backgroundColor: "rgba(0, 0, 0, 0.45)"
5850
+ },
5851
+ nativeLinkDialogCard: {
5852
+ width: "100%",
5853
+ maxWidth: 480,
5854
+ padding: 24,
5855
+ borderRadius: 16,
5856
+ backgroundColor: "#ffffff",
5857
+ shadowColor: "#000000",
5858
+ shadowOpacity: 0.18,
5859
+ shadowRadius: 24,
5860
+ shadowOffset: { width: 0, height: 8 },
5861
+ elevation: 12
5862
+ },
5863
+ nativeLinkDialogTitle: {
5864
+ color: "#111827",
5865
+ fontSize: 24,
5866
+ fontWeight: "700",
5867
+ lineHeight: 30
5868
+ },
5869
+ nativeLinkDialogDescription: {
5870
+ marginTop: 12,
5871
+ color: "#6b7280",
5872
+ fontSize: 16,
5873
+ lineHeight: 22
5874
+ },
5875
+ nativeLinkDialogInput: {
5876
+ marginTop: 24,
5877
+ height: 52,
5878
+ paddingHorizontal: 14,
5879
+ borderRadius: 10,
5880
+ borderWidth: reactNative.StyleSheet.hairlineWidth,
5881
+ borderColor: "#d1d5db",
5882
+ color: "#111827",
5883
+ fontSize: 18,
5884
+ backgroundColor: "#ffffff"
5885
+ },
5886
+ nativeLinkDialogActions: {
5887
+ marginTop: 24,
5888
+ flexDirection: "row",
5889
+ justifyContent: "flex-end",
5890
+ gap: 12
5891
+ },
5892
+ nativeLinkDialogButton: {
5893
+ minWidth: 92,
5894
+ height: 48,
5895
+ alignItems: "center",
5896
+ justifyContent: "center",
5897
+ borderRadius: 10,
5898
+ borderWidth: reactNative.StyleSheet.hairlineWidth,
5899
+ borderColor: "#d1d5db",
5900
+ backgroundColor: "#ffffff",
5901
+ paddingHorizontal: 18
5902
+ },
5903
+ nativeLinkDialogButtonPressed: {
5904
+ backgroundColor: "#f3f4f6"
5905
+ },
5906
+ nativeLinkDialogButtonText: {
5907
+ color: "#111827",
5908
+ fontSize: 17,
5909
+ fontWeight: "600"
5910
+ },
5911
+ nativeLinkDialogPrimaryButton: {
5912
+ borderColor: "#18181b",
5913
+ backgroundColor: "#18181b"
5914
+ },
5915
+ nativeLinkDialogPrimaryButtonPressed: {
5916
+ backgroundColor: "#27272a"
5917
+ },
5918
+ nativeLinkDialogDisabledButton: {
5919
+ borderColor: "#9ca3af",
5920
+ backgroundColor: "#9ca3af"
5921
+ },
5922
+ nativeLinkDialogPrimaryButtonText: {
5923
+ color: "#ffffff",
5924
+ fontSize: 17,
5925
+ fontWeight: "700"
5926
+ }
5927
+ });
5482
5928
 
5929
+ exports.DEFAULT_LINK_CARD_SIZE = DEFAULT_LINK_CARD_SIZE;
5483
5930
  exports.DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = DEFAULT_NATIVE_OVERFLOW_TOOL_IDS;
5484
5931
  exports.DEFAULT_NATIVE_VECTOR_TOOLS = DEFAULT_NATIVE_VECTOR_TOOLS;
5485
5932
  exports.NATIVE_STYLE_PALETTE = NATIVE_STYLE_PALETTE;
@@ -5491,7 +5938,10 @@ exports.NativeVectorToolbar = NativeVectorToolbar;
5491
5938
  exports.NativeVectorViewport = NativeVectorViewport;
5492
5939
  exports.createFreehandStrokeItem = createFreehandStrokeItem;
5493
5940
  exports.createImageItem = createImageItem;
5941
+ exports.createLinkItem = createLinkItem;
5494
5942
  exports.createShapeId = createShapeId;
5943
+ exports.getLinkData = getLinkData;
5944
+ exports.isLinkItem = isLinkItem;
5495
5945
  exports.nativeStyleColorWithOpacity = nativeStyleColorWithOpacity;
5496
5946
  exports.normalizeNativeStyleHex = normalizeNativeStyleHex;
5497
5947
  exports.parseSvgFragment = parseSvgFragment;