canvu-react 0.4.34 → 0.4.36

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) {
@@ -1405,6 +1450,179 @@ function smoothFreehandPointsToPathD(points) {
1405
1450
  return d;
1406
1451
  }
1407
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 buildNativeLinkCardDisplay(link) {
1473
+ const hostname = linkHostname(link.href);
1474
+ const title = link.title?.trim() || hostname || "Link";
1475
+ return {
1476
+ title,
1477
+ subtitle: hostname || link.href,
1478
+ initial: linkInitial(hostname || title),
1479
+ secure: linkProtocol(link.href) === "https:"
1480
+ };
1481
+ }
1482
+ function createNativeLinkCardBoundsAtPoint(point) {
1483
+ return {
1484
+ x: point.x - DEFAULT_LINK_CARD_SIZE.width / 2,
1485
+ y: point.y - DEFAULT_LINK_CARD_SIZE.height / 2,
1486
+ width: DEFAULT_LINK_CARD_SIZE.width,
1487
+ height: DEFAULT_LINK_CARD_SIZE.height
1488
+ };
1489
+ }
1490
+ var DEFAULT_NATIVE_IMAGE_CACHE_MAX_ENTRIES = 96;
1491
+ function disposeCachedImage(image) {
1492
+ try {
1493
+ image.dispose?.();
1494
+ } catch {
1495
+ }
1496
+ }
1497
+ function createNativeImageCache({
1498
+ loadImage,
1499
+ maxEntries = DEFAULT_NATIVE_IMAGE_CACHE_MAX_ENTRIES
1500
+ }) {
1501
+ const safeMaxEntries = Math.max(1, Math.round(maxEntries));
1502
+ const entries = /* @__PURE__ */ new Map();
1503
+ let clock = 0;
1504
+ const touchEntry = (entry) => {
1505
+ clock += 1;
1506
+ entry.lastUsed = clock;
1507
+ };
1508
+ const createEntry = () => {
1509
+ clock += 1;
1510
+ return {
1511
+ lastUsed: clock,
1512
+ retainCount: 0
1513
+ };
1514
+ };
1515
+ const prune = () => {
1516
+ const cachedEntries = [...entries.entries()].filter(
1517
+ (entry) => entry[1].image != null && entry[1].retainCount === 0
1518
+ );
1519
+ if (cachedEntries.length <= safeMaxEntries) return;
1520
+ const evictedEntries = cachedEntries.sort(
1521
+ (leftEntry, rightEntry) => leftEntry[1].lastUsed - rightEntry[1].lastUsed
1522
+ ).slice(0, cachedEntries.length - safeMaxEntries);
1523
+ for (const [href, entry] of evictedEntries) {
1524
+ entries.delete(href);
1525
+ disposeCachedImage(entry.image);
1526
+ }
1527
+ };
1528
+ const getCached = (href) => {
1529
+ const entry = entries.get(href);
1530
+ if (!entry?.image) return null;
1531
+ touchEntry(entry);
1532
+ return entry.image;
1533
+ };
1534
+ const load = async (href) => {
1535
+ const cachedImage = getCached(href);
1536
+ if (cachedImage) return cachedImage;
1537
+ const existingEntry = entries.get(href);
1538
+ if (existingEntry?.promise) return await existingEntry.promise;
1539
+ const entry = existingEntry ?? createEntry();
1540
+ const promise = loadImage(href).then((image) => {
1541
+ if (!image) {
1542
+ if (entry.retainCount === 0) entries.delete(href);
1543
+ return null;
1544
+ }
1545
+ entry.image = image;
1546
+ entry.promise = void 0;
1547
+ touchEntry(entry);
1548
+ prune();
1549
+ return image;
1550
+ }).catch((error) => {
1551
+ entries.delete(href);
1552
+ throw error;
1553
+ });
1554
+ entry.promise = promise;
1555
+ entries.set(href, entry);
1556
+ return await promise;
1557
+ };
1558
+ const retain = (href) => {
1559
+ const entry = entries.get(href) ?? createEntry();
1560
+ entry.retainCount += 1;
1561
+ touchEntry(entry);
1562
+ entries.set(href, entry);
1563
+ let released = false;
1564
+ return () => {
1565
+ if (released) return;
1566
+ released = true;
1567
+ entry.retainCount = Math.max(0, entry.retainCount - 1);
1568
+ if (!entry.image && !entry.promise && entry.retainCount === 0) {
1569
+ entries.delete(href);
1570
+ return;
1571
+ }
1572
+ prune();
1573
+ };
1574
+ };
1575
+ const clear = () => {
1576
+ for (const entry of entries.values()) {
1577
+ if (entry.image) disposeCachedImage(entry.image);
1578
+ }
1579
+ entries.clear();
1580
+ };
1581
+ const size = () => [...entries.values()].filter((entry) => entry.image != null).length;
1582
+ return { getCached, load, retain, clear, size };
1583
+ }
1584
+ async function loadSkiaImageFromHref(href) {
1585
+ const { Skia } = await import('@shopify/react-native-skia');
1586
+ const data = await Skia.Data.fromURI(href);
1587
+ return Skia.Image.MakeImageFromEncoded(data);
1588
+ }
1589
+ var nativeSkiaImageCache = createNativeImageCache({
1590
+ loadImage: loadSkiaImageFromHref
1591
+ });
1592
+ function useCachedSkiaImage(href) {
1593
+ const [loadedImage, setLoadedImage] = react.useState(
1594
+ () => href ? { href, image: nativeSkiaImageCache.getCached(href) } : null
1595
+ );
1596
+ react.useEffect(() => {
1597
+ if (!href) {
1598
+ setLoadedImage(null);
1599
+ return;
1600
+ }
1601
+ const releaseImage = nativeSkiaImageCache.retain(href);
1602
+ const cachedImage = nativeSkiaImageCache.getCached(href);
1603
+ if (cachedImage) {
1604
+ setLoadedImage({ href, image: cachedImage });
1605
+ return releaseImage;
1606
+ }
1607
+ let active = true;
1608
+ setLoadedImage({ href, image: null });
1609
+ void nativeSkiaImageCache.load(href).then(
1610
+ (loadedImage2) => {
1611
+ if (active) setLoadedImage({ href, image: loadedImage2 });
1612
+ },
1613
+ () => {
1614
+ if (active) setLoadedImage({ href, image: null });
1615
+ }
1616
+ );
1617
+ return () => {
1618
+ active = false;
1619
+ releaseImage();
1620
+ };
1621
+ }, [href]);
1622
+ if (!href || loadedImage?.href !== href) return null;
1623
+ return loadedImage.image;
1624
+ }
1625
+
1408
1626
  // src/native/skia-transform.ts
1409
1627
  function parseNum(s) {
1410
1628
  return Number(s);
@@ -1668,7 +1886,7 @@ function SvgNodeItem({ node }) {
1668
1886
  function SvgImageNodeItem({
1669
1887
  node
1670
1888
  }) {
1671
- const image = reactNativeSkia.useImage(node.href);
1889
+ const image = useCachedSkiaImage(node.href);
1672
1890
  if (!image) return null;
1673
1891
  return /* @__PURE__ */ jsxRuntime.jsx(
1674
1892
  reactNativeSkia.Image,
@@ -1958,7 +2176,119 @@ function localBounds(bounds) {
1958
2176
  const b = normalizeRect(bounds);
1959
2177
  return { w: Math.max(0, b.width), h: Math.max(0, b.height) };
1960
2178
  }
2179
+ function truncateText(value, maxChars) {
2180
+ if (value.length <= maxChars) return value;
2181
+ return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
2182
+ }
2183
+ function NativeLinkCardRenderer({
2184
+ item,
2185
+ link
2186
+ }) {
2187
+ const bounds = normalizeRect(item.bounds);
2188
+ const scaleX = Math.max(0.01, bounds.width / DEFAULT_LINK_CARD_SIZE.width);
2189
+ const scaleY = Math.max(0.01, bounds.height / DEFAULT_LINK_CARD_SIZE.height);
2190
+ const display = buildNativeLinkCardDisplay(link);
2191
+ const titleFont = reactNativeSkia.matchFont({ fontSize: 14.5, fontWeight: "700" });
2192
+ const subtitleFont = reactNativeSkia.matchFont({ fontSize: 12.5 });
2193
+ const initialFont = reactNativeSkia.matchFont({ fontSize: 17, fontWeight: "700" });
2194
+ const title = truncateText(display.title, 28);
2195
+ const subtitle = truncateText(display.subtitle, display.secure ? 28 : 31);
2196
+ const subtitleX = display.secure ? 82 : 69;
2197
+ const subtitleWidth = display.secure ? 188 : 201;
2198
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNativeSkia.Group, { transform: [{ scaleX }, { scaleY }], children: [
2199
+ /* @__PURE__ */ jsxRuntime.jsx(
2200
+ reactNativeSkia.RoundedRect,
2201
+ {
2202
+ x: 0,
2203
+ y: 0,
2204
+ width: DEFAULT_LINK_CARD_SIZE.width,
2205
+ height: DEFAULT_LINK_CARD_SIZE.height,
2206
+ r: 16,
2207
+ color: "#ffffff",
2208
+ style: "fill",
2209
+ antiAlias: true
2210
+ }
2211
+ ),
2212
+ /* @__PURE__ */ jsxRuntime.jsx(
2213
+ reactNativeSkia.RoundedRect,
2214
+ {
2215
+ x: 0.5,
2216
+ y: 0.5,
2217
+ width: DEFAULT_LINK_CARD_SIZE.width - 1,
2218
+ height: DEFAULT_LINK_CARD_SIZE.height - 1,
2219
+ r: 15.5,
2220
+ color: "#dfe4ea",
2221
+ style: "stroke",
2222
+ strokeWidth: 1,
2223
+ antiAlias: true
2224
+ }
2225
+ ),
2226
+ /* @__PURE__ */ jsxRuntime.jsx(
2227
+ reactNativeSkia.RoundedRect,
2228
+ {
2229
+ x: 14,
2230
+ y: 14,
2231
+ width: 42,
2232
+ height: 42,
2233
+ r: 11,
2234
+ color: "#315bd6",
2235
+ style: "fill",
2236
+ antiAlias: true
2237
+ }
2238
+ ),
2239
+ /* @__PURE__ */ jsxRuntime.jsx(
2240
+ reactNativeSkia.Text,
2241
+ {
2242
+ x: 29.5,
2243
+ y: 41,
2244
+ text: display.initial,
2245
+ color: "#ffffff",
2246
+ font: initialFont
2247
+ }
2248
+ ),
2249
+ /* @__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 }) }),
2250
+ display.secure ? /* @__PURE__ */ jsxRuntime.jsxs(reactNativeSkia.Group, { transform: [{ translateX: 69 }, { translateY: 41 }], children: [
2251
+ /* @__PURE__ */ jsxRuntime.jsx(
2252
+ reactNativeSkia.Rect,
2253
+ {
2254
+ x: 1.5,
2255
+ y: 4.5,
2256
+ width: 7,
2257
+ height: 6,
2258
+ color: "#6b7280",
2259
+ style: "stroke",
2260
+ strokeWidth: 1.3
2261
+ }
2262
+ ),
2263
+ /* @__PURE__ */ jsxRuntime.jsx(
2264
+ reactNativeSkia.Path,
2265
+ {
2266
+ path: "M3 4.5 V3 a2 2 0 0 1 4 0 v1.5",
2267
+ color: "#6b7280",
2268
+ style: "stroke",
2269
+ strokeWidth: 1.3,
2270
+ strokeCap: "round",
2271
+ strokeJoin: "round"
2272
+ }
2273
+ )
2274
+ ] }) : null,
2275
+ /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { clip: { x: subtitleX, y: 34, width: subtitleWidth, height: 22 }, children: /* @__PURE__ */ jsxRuntime.jsx(
2276
+ reactNativeSkia.Text,
2277
+ {
2278
+ x: subtitleX,
2279
+ y: 51,
2280
+ text: subtitle,
2281
+ color: "#6b7280",
2282
+ font: subtitleFont
2283
+ }
2284
+ ) })
2285
+ ] });
2286
+ }
1961
2287
  function NativeShapeRenderer({ item }) {
2288
+ const link = getLinkData(item);
2289
+ if (link) {
2290
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeLinkCardRenderer, { item, link });
2291
+ }
1962
2292
  const style = resolveStrokeStyle(item);
1963
2293
  const k = item.toolKind;
1964
2294
  if (k === "rect") {
@@ -3381,9 +3711,14 @@ var DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = [
3381
3711
  "line",
3382
3712
  "marker",
3383
3713
  "laser",
3384
- "image"
3714
+ "image",
3715
+ "link"
3385
3716
  ];
3386
3717
  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";
3718
+ var LINK_TOOL_ICON_PATHS = [
3719
+ "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
3720
+ "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
3721
+ ];
3387
3722
  function NativeCloudToolIcon({
3388
3723
  color,
3389
3724
  size = 19,
@@ -3402,6 +3737,25 @@ function NativeCloudToolIcon({
3402
3737
  }
3403
3738
  ) }) });
3404
3739
  }
3740
+ function NativeLinkToolIcon({
3741
+ color,
3742
+ size = 20,
3743
+ strokeWidth = 2.4
3744
+ }) {
3745
+ const scale = size / 24;
3746
+ 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(
3747
+ reactNativeSkia.Path,
3748
+ {
3749
+ path,
3750
+ color,
3751
+ style: "stroke",
3752
+ strokeWidth,
3753
+ strokeCap: "round",
3754
+ strokeJoin: "round"
3755
+ },
3756
+ path
3757
+ )) }) });
3758
+ }
3405
3759
  var DEFAULT_NATIVE_VECTOR_TOOLS = [
3406
3760
  { id: "hand", label: "Hand", shortcutHint: "H", shortLabel: "H" },
3407
3761
  { id: "select", label: "Select", shortcutHint: "V", shortLabel: "V" },
@@ -3439,7 +3793,8 @@ var DEFAULT_NATIVE_VECTOR_TOOLS = [
3439
3793
  shortLabel: "E"
3440
3794
  },
3441
3795
  { id: "text", label: "Text", shortcutHint: "T", shortLabel: "T" },
3442
- { id: "image", label: "File", shortcutHint: "I", shortLabel: "I" }
3796
+ { id: "image", label: "File", shortcutHint: "I", shortLabel: "I" },
3797
+ { id: "link", label: "Link", shortcutHint: "N", shortLabel: "L" }
3443
3798
  ];
3444
3799
  function splitToolbarTools(tools, overflowToolIds) {
3445
3800
  const overflowIds = new Set(overflowToolIds);
@@ -3701,6 +4056,9 @@ function renderNativeToolFallback(tool, foregroundColor, toolLabelStyle) {
3701
4056
  if (tool.id === "architectural-cloud") {
3702
4057
  return /* @__PURE__ */ jsxRuntime.jsx(NativeCloudToolIcon, { color: foregroundColor });
3703
4058
  }
4059
+ if (tool.id === "link") {
4060
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeLinkToolIcon, { color: foregroundColor });
4061
+ }
3704
4062
  return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
3705
4063
  }
3706
4064
  var styles2 = reactNative.StyleSheet.create({
@@ -4381,6 +4739,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4381
4739
  onSelectionChange,
4382
4740
  onItemsChange,
4383
4741
  onToolChangeRequest,
4742
+ onLinkToolRequest,
4384
4743
  onWorldPointerDown,
4385
4744
  onWorldPointerMove,
4386
4745
  onWorldPointerLeave,
@@ -4400,6 +4759,8 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4400
4759
  toolLockedRef.current = toolLocked;
4401
4760
  const onToolChangeRequestRef = react.useRef(onToolChangeRequest);
4402
4761
  onToolChangeRequestRef.current = onToolChangeRequest;
4762
+ const onLinkToolRequestRef = react.useRef(onLinkToolRequest);
4763
+ onLinkToolRequestRef.current = onLinkToolRequest;
4403
4764
  const onWorldPointerDownRef = react.useRef(onWorldPointerDown);
4404
4765
  onWorldPointerDownRef.current = onWorldPointerDown;
4405
4766
  const onWorldPointerMoveRef = react.useRef(onWorldPointerMove);
@@ -4742,7 +5103,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4742
5103
  });
4743
5104
  return;
4744
5105
  }
4745
- if (tool === "note" || tool === "text") {
5106
+ if (tool === "link" || tool === "note" || tool === "text") {
4746
5107
  dragStateRef.current = {
4747
5108
  kind: "tap",
4748
5109
  tool,
@@ -5078,6 +5439,44 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5078
5439
  const screenDy = point.y - st.startScreen.y;
5079
5440
  if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
5080
5441
  const change = onItemsChangeRef.current;
5442
+ if (st.tool === "link") {
5443
+ const suggestedBounds = createNativeLinkCardBoundsAtPoint(st.startWorld);
5444
+ const insertLink = (link, options = {}) => {
5445
+ const currentChange = onItemsChangeRef.current;
5446
+ if (!currentChange) return null;
5447
+ const id = options.id ?? createShapeId();
5448
+ const item = createLinkItem(id, options.bounds ?? suggestedBounds, link);
5449
+ currentChange([...itemsRef.current, item]);
5450
+ onSelectionChangeRef.current?.([id]);
5451
+ requestSelectToolAfterUse();
5452
+ return item;
5453
+ };
5454
+ const requestLink = onLinkToolRequestRef.current;
5455
+ if (requestLink) {
5456
+ requestLink({
5457
+ toolId: "link",
5458
+ worldX: st.startWorld.x,
5459
+ worldY: st.startWorld.y,
5460
+ screenX: st.startScreen.x,
5461
+ screenY: st.startScreen.y,
5462
+ suggestedBounds,
5463
+ insertLink
5464
+ });
5465
+ return;
5466
+ }
5467
+ const handleWorldPointerDown = onWorldPointerDownRef.current;
5468
+ if (handleWorldPointerDown) {
5469
+ handleWorldPointerDown({
5470
+ toolId: "link",
5471
+ worldX: st.startWorld.x,
5472
+ worldY: st.startWorld.y,
5473
+ screenX: st.startScreen.x,
5474
+ screenY: st.startScreen.y
5475
+ });
5476
+ requestSelectToolAfterUse();
5477
+ }
5478
+ return;
5479
+ }
5081
5480
  if (!change) return;
5082
5481
  if (st.tool === "text") {
5083
5482
  const id = createShapeId();
@@ -5345,6 +5744,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5345
5744
  );
5346
5745
  });
5347
5746
 
5747
+ exports.DEFAULT_LINK_CARD_SIZE = DEFAULT_LINK_CARD_SIZE;
5348
5748
  exports.DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = DEFAULT_NATIVE_OVERFLOW_TOOL_IDS;
5349
5749
  exports.DEFAULT_NATIVE_VECTOR_TOOLS = DEFAULT_NATIVE_VECTOR_TOOLS;
5350
5750
  exports.NATIVE_STYLE_PALETTE = NATIVE_STYLE_PALETTE;
@@ -5356,7 +5756,10 @@ exports.NativeVectorToolbar = NativeVectorToolbar;
5356
5756
  exports.NativeVectorViewport = NativeVectorViewport;
5357
5757
  exports.createFreehandStrokeItem = createFreehandStrokeItem;
5358
5758
  exports.createImageItem = createImageItem;
5759
+ exports.createLinkItem = createLinkItem;
5359
5760
  exports.createShapeId = createShapeId;
5761
+ exports.getLinkData = getLinkData;
5762
+ exports.isLinkItem = isLinkItem;
5360
5763
  exports.nativeStyleColorWithOpacity = nativeStyleColorWithOpacity;
5361
5764
  exports.normalizeNativeStyleHex = normalizeNativeStyleHex;
5362
5765
  exports.parseSvgFragment = parseSvgFragment;