canvu-react 0.3.37 → 0.3.39

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.
@@ -1,5 +1,5 @@
1
1
  import { V as VectorSceneItem } from './types-Bnq2HtHQ.js';
2
- import { x as VectorViewportAssetStore } from './shape-builders-CsSXKCcs.js';
2
+ import { E as VectorViewportAssetStore } from './shape-builders-C7bxJBGR.js';
3
3
 
4
4
  declare class IndexedDbImageStore {
5
5
  private dbPromise;
@@ -1,5 +1,5 @@
1
1
  import { V as VectorSceneItem } from './types-Bnq2HtHQ.cjs';
2
- import { x as VectorViewportAssetStore } from './shape-builders-CsbSRZnQ.cjs';
2
+ import { E as VectorViewportAssetStore } from './shape-builders-Dedcl6tw.cjs';
3
3
 
4
4
  declare class IndexedDbImageStore {
5
5
  private dbPromise;
@@ -1,9 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { C as CanvasPlugin } from './types-DWGk2_GZ.cjs';
2
+ import { C as CanvasPlugin } from './types-DUW61Tjy.cjs';
3
3
  import 'react';
4
4
  import './types-Bnq2HtHQ.cjs';
5
5
  import './camera-Di5R_Rwl.cjs';
6
- import './shape-builders-CsbSRZnQ.cjs';
6
+ import './shape-builders-Dedcl6tw.cjs';
7
7
 
8
8
  type ChatbotPluginPanelProps = {
9
9
  /**
package/dist/chatbot.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { C as CanvasPlugin } from './types-B6PAYKzx.js';
2
+ import { C as CanvasPlugin } from './types-BBb8KoyW.js';
3
3
  import 'react';
4
4
  import './types-Bnq2HtHQ.js';
5
5
  import './camera-AoTwBSoE.js';
6
- import './shape-builders-CsSXKCcs.js';
6
+ import './shape-builders-C7bxJBGR.js';
7
7
 
8
8
  type ChatbotPluginPanelProps = {
9
9
  /**
package/dist/index.cjs CHANGED
@@ -414,6 +414,78 @@ async function loadImageFileAsRasterSceneSource(file, maxDimensionOrOptions) {
414
414
  }
415
415
  }
416
416
 
417
+ // src/input/pan-momentum.ts
418
+ var VELOCITY_SAMPLE_WINDOW = 80;
419
+ var FRICTION = 0.94;
420
+ var MIN_VELOCITY = 0.3;
421
+ function createPanMomentumController(camera, onUpdate, sensitivity) {
422
+ const samples = [];
423
+ let animationFrameId = null;
424
+ const cancel = () => {
425
+ if (animationFrameId !== null) {
426
+ cancelAnimationFrame(animationFrameId);
427
+ animationFrameId = null;
428
+ }
429
+ };
430
+ const reset = () => {
431
+ cancel();
432
+ samples.length = 0;
433
+ };
434
+ const recordMove = (dx, dy) => {
435
+ const now = performance.now();
436
+ samples.push({ dx, dy, time: now });
437
+ const cutoff = now - VELOCITY_SAMPLE_WINDOW;
438
+ let oldestSample = samples[0];
439
+ while (oldestSample && oldestSample.time < cutoff) {
440
+ samples.shift();
441
+ oldestSample = samples[0];
442
+ }
443
+ };
444
+ const computeReleaseVelocity = () => {
445
+ if (samples.length < 2) return { vx: 0, vy: 0 };
446
+ const first = samples[0];
447
+ const last = samples[samples.length - 1];
448
+ if (!first || !last) return { vx: 0, vy: 0 };
449
+ const elapsed = last.time - first.time;
450
+ if (elapsed < 4) return { vx: 0, vy: 0 };
451
+ let totalDx = 0;
452
+ let totalDy = 0;
453
+ for (const sample of samples) {
454
+ totalDx += sample.dx;
455
+ totalDy += sample.dy;
456
+ }
457
+ const msPerFrame = 1e3 / 60;
458
+ return {
459
+ vx: totalDx / elapsed * msPerFrame * sensitivity,
460
+ vy: totalDy / elapsed * msPerFrame * sensitivity
461
+ };
462
+ };
463
+ const startMomentum = () => {
464
+ cancel();
465
+ const { vx, vy } = computeReleaseVelocity();
466
+ samples.length = 0;
467
+ if (Math.abs(vx) < MIN_VELOCITY && Math.abs(vy) < MIN_VELOCITY) {
468
+ return;
469
+ }
470
+ let currentVx = vx;
471
+ let currentVy = vy;
472
+ const animate = () => {
473
+ currentVx *= FRICTION;
474
+ currentVy *= FRICTION;
475
+ if (Math.abs(currentVx) < MIN_VELOCITY && Math.abs(currentVy) < MIN_VELOCITY) {
476
+ animationFrameId = null;
477
+ return;
478
+ }
479
+ camera.x += currentVx;
480
+ camera.y += currentVy;
481
+ onUpdate();
482
+ animationFrameId = requestAnimationFrame(animate);
483
+ };
484
+ animationFrameId = requestAnimationFrame(animate);
485
+ };
486
+ return { recordMove, startMomentum, cancel, reset };
487
+ }
488
+
417
489
  // src/input/apple-pencil-navigation.ts
418
490
  var DRAWING_LIKE_TOOLS = /* @__PURE__ */ new Set([
419
491
  "draw",
@@ -444,6 +516,7 @@ function attachApplePencilNavigation(options) {
444
516
  let pinchStartDist = 0;
445
517
  let pinchStartZoom = 1;
446
518
  let panLast = null;
519
+ let touchMomentum = null;
447
520
  const shouldIntercept = (e) => {
448
521
  if (e.pointerType !== "touch") return false;
449
522
  const tool = getCurrentToolId();
@@ -462,6 +535,10 @@ function attachApplePencilNavigation(options) {
462
535
  pointers.clear();
463
536
  mode = "idle";
464
537
  panLast = null;
538
+ if (touchMomentum) {
539
+ touchMomentum.cancel();
540
+ touchMomentum = null;
541
+ }
465
542
  return;
466
543
  }
467
544
  if (e.pointerType === "touch" && activePenPointerIds.size > 0) {
@@ -471,10 +548,18 @@ function attachApplePencilNavigation(options) {
471
548
  return;
472
549
  }
473
550
  if (!shouldIntercept(e)) return;
551
+ if (touchMomentum) {
552
+ touchMomentum.cancel();
553
+ }
474
554
  pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
475
555
  if (pointers.size === 1) {
476
556
  mode = "pan";
477
557
  panLast = { x: e.clientX, y: e.clientY };
558
+ touchMomentum = createPanMomentumController(
559
+ camera,
560
+ onUpdate,
561
+ touchPanSensitivity
562
+ );
478
563
  } else if (pointers.size === 2) {
479
564
  const vals = Array.from(pointers.values());
480
565
  const a = vals[0];
@@ -502,6 +587,7 @@ function attachApplePencilNavigation(options) {
502
587
  panLast = { x: e.clientX, y: e.clientY };
503
588
  camera.x += dx * touchPanSensitivity;
504
589
  camera.y += dy * touchPanSensitivity;
590
+ touchMomentum?.recordMove(dx, dy);
505
591
  onUpdate();
506
592
  e.preventDefault();
507
593
  e.stopImmediatePropagation();
@@ -548,12 +634,20 @@ function attachApplePencilNavigation(options) {
548
634
  if (!pointers.has(e.pointerId)) return;
549
635
  pointers.delete(e.pointerId);
550
636
  if (pointers.size === 0) {
637
+ const wasPanning = mode === "pan";
551
638
  mode = "idle";
552
639
  panLast = null;
640
+ if (wasPanning && touchMomentum) {
641
+ touchMomentum.startMomentum();
642
+ touchMomentum = null;
643
+ }
553
644
  } else if (pointers.size === 1 && mode === "pinch") {
554
645
  mode = "pan";
555
646
  const r = Array.from(pointers.values())[0];
556
647
  panLast = r ?? null;
648
+ if (touchMomentum) {
649
+ touchMomentum.reset();
650
+ }
557
651
  }
558
652
  e.preventDefault();
559
653
  e.stopImmediatePropagation();
@@ -563,6 +657,10 @@ function attachApplePencilNavigation(options) {
563
657
  element.addEventListener("pointerup", onPointerUp, { capture: true });
564
658
  element.addEventListener("pointercancel", onPointerUp, { capture: true });
565
659
  return () => {
660
+ if (touchMomentum) {
661
+ touchMomentum.cancel();
662
+ touchMomentum = null;
663
+ }
566
664
  element.removeEventListener("pointerdown", onPointerDown, { capture: true });
567
665
  element.removeEventListener("pointermove", onPointerMove, { capture: true });
568
666
  element.removeEventListener("pointerup", onPointerUp, { capture: true });
@@ -615,6 +713,7 @@ function attachViewportInput(options) {
615
713
  let pinchStartZoom = 1;
616
714
  let panLast = null;
617
715
  let mousePanButton = null;
716
+ let touchMomentum = null;
618
717
  const onWheel = (e) => {
619
718
  if (e.ctrlKey || e.metaKey) {
620
719
  e.preventDefault();
@@ -641,6 +740,9 @@ function attachViewportInput(options) {
641
740
  if (touchHandledElsewhere && e.pointerType === "touch") {
642
741
  return;
643
742
  }
743
+ if (touchMomentum) {
744
+ touchMomentum.cancel();
745
+ }
644
746
  const panOk = allowPrimaryPointerPan();
645
747
  if (e.pointerType === "mouse" && e.button === 0) {
646
748
  if (!panOk) {
@@ -666,6 +768,11 @@ function attachViewportInput(options) {
666
768
  if (panOk) {
667
769
  mode = "pan";
668
770
  panLast = { x: e.clientX, y: e.clientY };
771
+ touchMomentum = createPanMomentumController(
772
+ camera,
773
+ onUpdate,
774
+ touchPanSensitivity
775
+ );
669
776
  e.preventDefault();
670
777
  }
671
778
  } else if (pointers.size === 2) {
@@ -702,6 +809,7 @@ function attachViewportInput(options) {
702
809
  panLast = { x: e.clientX, y: e.clientY };
703
810
  camera.x += dx * touchPanSensitivity;
704
811
  camera.y += dy * touchPanSensitivity;
812
+ touchMomentum?.recordMove(dx, dy);
705
813
  onUpdate();
706
814
  e.preventDefault();
707
815
  return;
@@ -753,15 +861,27 @@ function attachViewportInput(options) {
753
861
  }
754
862
  pointers.delete(e.pointerId);
755
863
  if (pointers.size === 0) {
864
+ const wasPanning = mode === "pan";
756
865
  mode = "idle";
757
866
  panLast = null;
867
+ if (wasPanning && touchMomentum) {
868
+ touchMomentum.startMomentum();
869
+ touchMomentum = null;
870
+ }
758
871
  } else if (pointers.size === 1 && mode === "pinch") {
759
872
  mode = "pan";
760
873
  const remaining = Array.from(pointers.values())[0];
761
874
  panLast = remaining ?? null;
875
+ if (touchMomentum) {
876
+ touchMomentum.reset();
877
+ }
762
878
  }
763
879
  };
764
880
  const onPointerCancel = (e) => {
881
+ if (touchMomentum) {
882
+ touchMomentum.cancel();
883
+ touchMomentum = null;
884
+ }
765
885
  onPointerUp(e);
766
886
  };
767
887
  wheelTarget.addEventListener("wheel", onWheel, { passive: false });
@@ -770,6 +890,10 @@ function attachViewportInput(options) {
770
890
  element.addEventListener("pointerup", onPointerUp);
771
891
  element.addEventListener("pointercancel", onPointerCancel);
772
892
  return () => {
893
+ if (touchMomentum) {
894
+ touchMomentum.cancel();
895
+ touchMomentum = null;
896
+ }
773
897
  wheelTarget.removeEventListener("wheel", onWheel);
774
898
  element.removeEventListener("pointerdown", onPointerDown);
775
899
  element.removeEventListener("pointermove", onPointerMove);
@@ -2482,6 +2606,7 @@ var SvgVectorRenderer = class {
2482
2606
  svg;
2483
2607
  rootG;
2484
2608
  itemNodeCache = /* @__PURE__ */ new Map();
2609
+ liveOverlay = null;
2485
2610
  resizeObserver;
2486
2611
  constructor(options) {
2487
2612
  this.container = options.container;
@@ -2516,6 +2641,50 @@ var SvgVectorRenderer = class {
2516
2641
  const items = cullItemsByViewport(this.scene.getItems(), visible);
2517
2642
  this.syncVisibleItems(items);
2518
2643
  this.rootG.setAttribute("transform", formatCameraTransform(this.camera));
2644
+ this.keepLiveOverlayOnTop();
2645
+ }
2646
+ /**
2647
+ * Updates only the in-progress (live) stroke node without re-culling or
2648
+ * re-syncing the committed scene items. Drawing tools call this on every
2649
+ * pointer move, so it must stay O(1) regardless of how many items the
2650
+ * board already contains.
2651
+ *
2652
+ * Pass `null` to remove the live overlay (e.g. when the stroke is committed).
2653
+ */
2654
+ renderLiveItem(item) {
2655
+ if (!item) {
2656
+ if (this.liveOverlay) {
2657
+ this.liveOverlay.g.remove();
2658
+ this.liveOverlay = null;
2659
+ }
2660
+ return;
2661
+ }
2662
+ if (!this.liveOverlay) {
2663
+ const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
2664
+ g.setAttribute("data-live-overlay", "true");
2665
+ this.liveOverlay = {
2666
+ g,
2667
+ lastChildrenSvg: "",
2668
+ lastTransform: ""
2669
+ };
2670
+ this.rootG.appendChild(g);
2671
+ }
2672
+ const cached = this.liveOverlay;
2673
+ const t = formatItemPlacementTransform(item);
2674
+ if (cached.lastTransform !== t) {
2675
+ cached.g.setAttribute("transform", t);
2676
+ cached.lastTransform = t;
2677
+ }
2678
+ if (cached.lastChildrenSvg !== item.childrenSvg) {
2679
+ cached.g.innerHTML = item.childrenSvg;
2680
+ cached.lastChildrenSvg = item.childrenSvg;
2681
+ }
2682
+ this.keepLiveOverlayOnTop();
2683
+ }
2684
+ keepLiveOverlayOnTop() {
2685
+ if (this.liveOverlay && this.rootG.lastChild !== this.liveOverlay.g) {
2686
+ this.rootG.appendChild(this.liveOverlay.g);
2687
+ }
2519
2688
  }
2520
2689
  syncVisibleItems(items) {
2521
2690
  const visibleIds = /* @__PURE__ */ new Set();
@@ -2560,6 +2729,7 @@ var SvgVectorRenderer = class {
2560
2729
  destroy() {
2561
2730
  this.resizeObserver.disconnect();
2562
2731
  this.itemNodeCache.clear();
2732
+ this.liveOverlay = null;
2563
2733
  this.svg.remove();
2564
2734
  }
2565
2735
  /** Toggle whether the scene SVG receives pointer events (vs overlay handling them). */
@@ -2591,6 +2761,105 @@ function cloneVectorSceneItemsWithNewIds(items) {
2591
2761
  return items.map(cloneVectorSceneItemWithNewId);
2592
2762
  }
2593
2763
 
2764
+ // src/scene/link-item.ts
2765
+ var LINK_PLUGIN_KEY = "canvuLink";
2766
+ var DEFAULT_LINK_CARD_WIDTH = 360;
2767
+ var DEFAULT_LINK_CARD_HEIGHT = 132;
2768
+ var LINK_CARD_BORDER = "#e2e8f0";
2769
+ var LINK_CARD_ACCENT = "#2563eb";
2770
+ var LINK_CARD_TITLE_COLOR = "#0f172a";
2771
+ var LINK_CARD_TEXT_COLOR = "#475569";
2772
+ var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
2773
+ var formatNumber = (value) => {
2774
+ const rounded = Math.round(value * 100) / 100;
2775
+ return Object.is(rounded, -0) ? "0" : String(rounded);
2776
+ };
2777
+ var escapeXmlAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2778
+ var escapeHtmlText2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2779
+ var getLinkHostname = (href) => {
2780
+ try {
2781
+ return new URL(href).hostname.replace(/^www\./, "");
2782
+ } catch {
2783
+ return href;
2784
+ }
2785
+ };
2786
+ var buildLinkTextBand = (band) => {
2787
+ const lineHeight = band.fontSize * 1.3;
2788
+ const weight = band.fontWeight != null ? `font-weight:${band.fontWeight};` : "";
2789
+ const lineClampStyle = band.clampLines ? `display:-webkit-box;-webkit-line-clamp:${band.clampLines};-webkit-box-orient:vertical;` : "";
2790
+ return `<foreignObject x="${formatNumber(band.x)}" y="${formatNumber(band.y)}" width="${formatNumber(Math.max(1, band.width))}" height="${formatNumber(Math.max(1, band.height))}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;font-family:system-ui,sans-serif;font-size:${formatNumber(band.fontSize)}px;line-height:${formatNumber(lineHeight)}px;color:${band.color};overflow:hidden;word-break:break-word;${lineClampStyle}${weight}">${escapeHtmlText2(band.text)}</div></foreignObject>`;
2791
+ };
2792
+ function buildLinkCardSvg(width, height, link) {
2793
+ const cardWidth = Math.max(1, width);
2794
+ const cardHeight = Math.max(1, height);
2795
+ const padding = 14;
2796
+ const badgeSize = clamp(Math.min(72, cardHeight - padding * 2), 28, 96);
2797
+ const textX = padding + badgeSize + 14;
2798
+ const textWidth = Math.max(1, cardWidth - textX - padding);
2799
+ const hostname = getLinkHostname(link.href);
2800
+ const title = link.title?.trim() || hostname || "Link";
2801
+ const description = link.description?.trim() || link.href;
2802
+ const titleY = padding;
2803
+ const titleHeight = clamp(cardHeight * 0.22, 18, 28);
2804
+ const hostY = titleY + titleHeight + 2;
2805
+ const hostHeight = 16;
2806
+ const descY = hostY + hostHeight + 4;
2807
+ const descHeight = Math.max(1, cardHeight - descY - padding);
2808
+ const badge = link.favicon ? `<clipPath id="canvu-link-badge"><rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="12" /></clipPath>
2809
+ <rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="12" fill="#f8fafc" stroke="${LINK_CARD_BORDER}" stroke-width="1" />
2810
+ <image href="${escapeXmlAttribute(link.favicon)}" x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" preserveAspectRatio="xMidYMid slice" clip-path="url(#canvu-link-badge)" />` : `<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="12" fill="${LINK_CARD_ACCENT}" fill-opacity="0.1" stroke="${LINK_CARD_ACCENT}" stroke-opacity="0.3" stroke-width="1" />
2811
+ <g transform="translate(${formatNumber(padding + badgeSize / 2)},${formatNumber(padding + badgeSize / 2)})" stroke="${LINK_CARD_ACCENT}" stroke-width="2.4" stroke-linecap="round" fill="none">
2812
+ <path d="M-9 3 a6 6 0 0 1 0 -8 l4 -4 a6 6 0 0 1 8 8 l-2 2" />
2813
+ <path d="M9 -3 a6 6 0 0 1 0 8 l-4 4 a6 6 0 0 1 -8 -8 l2 -2" />
2814
+ </g>`;
2815
+ const externalIconX = cardWidth - padding - 16;
2816
+ const externalIcon = `<g transform="translate(${formatNumber(externalIconX)},${formatNumber(padding)})" stroke="${LINK_CARD_TEXT_COLOR}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none">
2817
+ <path d="M6 1 H11 V6" />
2818
+ <path d="M11 1 L4.5 7.5" />
2819
+ <path d="M9 7 V10 a1 1 0 0 1 -1 1 H2 a1 1 0 0 1 -1 -1 V4 a1 1 0 0 1 1 -1 H5" />
2820
+ </g>`;
2821
+ return `
2822
+ <rect width="${formatNumber(cardWidth)}" height="${formatNumber(cardHeight)}" rx="16" fill="#ffffff" stroke="${LINK_CARD_BORDER}" stroke-width="1.5" />
2823
+ ${badge}
2824
+ ${externalIcon}
2825
+ ${buildLinkTextBand({ x: textX, y: titleY, width: textWidth - 18, height: titleHeight, text: title, fontSize: 15, color: LINK_CARD_TITLE_COLOR, fontWeight: 700, clampLines: 1 })}
2826
+ ${buildLinkTextBand({ x: textX, y: hostY, width: textWidth, height: hostHeight, text: hostname, fontSize: 12, color: LINK_CARD_ACCENT, clampLines: 1 })}
2827
+ ${buildLinkTextBand({ x: textX, y: descY, width: textWidth, height: descHeight, text: description, fontSize: 12, color: LINK_CARD_TEXT_COLOR, clampLines: 2 })}
2828
+ `;
2829
+ }
2830
+ var isCanvuLinkData = (value) => {
2831
+ if (!value || typeof value !== "object") return false;
2832
+ const candidate = value;
2833
+ return typeof candidate.href === "string" && candidate.href.length > 0;
2834
+ };
2835
+ function getLinkData(item) {
2836
+ const entry = item.pluginData?.[LINK_PLUGIN_KEY];
2837
+ return isCanvuLinkData(entry) ? entry : null;
2838
+ }
2839
+ function isLinkItem(item) {
2840
+ return getLinkData(item) != null;
2841
+ }
2842
+ function createLinkItem(id, bounds, link) {
2843
+ const width = bounds.width || DEFAULT_LINK_CARD_WIDTH;
2844
+ const height = bounds.height || DEFAULT_LINK_CARD_HEIGHT;
2845
+ const item = createCustomShapeItem(
2846
+ id,
2847
+ { ...bounds, width, height },
2848
+ { render: (size) => buildLinkCardSvg(size.width, size.height, link) }
2849
+ );
2850
+ return {
2851
+ ...item,
2852
+ pluginData: {
2853
+ ...item.pluginData ?? {},
2854
+ [LINK_PLUGIN_KEY]: link
2855
+ }
2856
+ };
2857
+ }
2858
+ var DEFAULT_LINK_CARD_SIZE = {
2859
+ width: DEFAULT_LINK_CARD_WIDTH,
2860
+ height: DEFAULT_LINK_CARD_HEIGHT
2861
+ };
2862
+
2594
2863
  // src/scene/managed-images.ts
2595
2864
  var MANAGED_KEY = "managed";
2596
2865
  var STACK_GAP_WORLD = 16;
@@ -2695,8 +2964,10 @@ var VectorScene = class {
2695
2964
 
2696
2965
  exports.ARROW_BIND_SNAP_PX = ARROW_BIND_SNAP_PX;
2697
2966
  exports.Camera2D = Camera2D;
2967
+ exports.DEFAULT_LINK_CARD_SIZE = DEFAULT_LINK_CARD_SIZE;
2698
2968
  exports.DEFAULT_STROKE_STYLE = DEFAULT_STROKE_STYLE;
2699
2969
  exports.DEFAULT_TEXT_FONT_SIZE = DEFAULT_TEXT_FONT_SIZE;
2970
+ exports.LINK_PLUGIN_KEY = LINK_PLUGIN_KEY;
2700
2971
  exports.MANAGED_KEY = MANAGED_KEY;
2701
2972
  exports.MAX_RASTER_EMBED_DIMENSION = MAX_RASTER_EMBED_DIMENSION;
2702
2973
  exports.SvgVectorRenderer = SvgVectorRenderer;
@@ -2714,6 +2985,7 @@ exports.buildDrawDotSvg = buildDrawDotSvg;
2714
2985
  exports.buildEllipseSvg = buildEllipseSvg;
2715
2986
  exports.buildFreehandPathSvg = buildFreehandPathSvg;
2716
2987
  exports.buildLineSvg = buildLineSvg;
2988
+ exports.buildLinkCardSvg = buildLinkCardSvg;
2717
2989
  exports.buildRectSvg = buildRectSvg;
2718
2990
  exports.buildTextSvg = buildTextSvg;
2719
2991
  exports.cloneVectorSceneItemWithNewId = cloneVectorSceneItemWithNewId;
@@ -2729,6 +3001,7 @@ exports.createFreehandStrokeItem = createFreehandStrokeItem;
2729
3001
  exports.createImageFromVectorTrace = createImageFromVectorTrace;
2730
3002
  exports.createImageItem = createImageItem;
2731
3003
  exports.createLineItem = createLineItem;
3004
+ exports.createLinkItem = createLinkItem;
2732
3005
  exports.createRectangleItem = createRectangleItem;
2733
3006
  exports.createShapeId = createShapeId;
2734
3007
  exports.createTextItem = createTextItem;
@@ -2738,9 +3011,11 @@ exports.encodeCanvasToBlob = encodeCanvasToBlob;
2738
3011
  exports.expandCustomShapeTemplate = expandCustomShapeTemplate;
2739
3012
  exports.formatCameraTransform = formatCameraTransform;
2740
3013
  exports.formatItemPlacementTransform = formatItemPlacementTransform;
3014
+ exports.getLinkData = getLinkData;
2741
3015
  exports.hitTestWorldPoint = hitTestWorldPoint;
2742
3016
  exports.hydrateSceneItemsWithAssets = hydrateSceneItemsWithAssets;
2743
3017
  exports.isArrowBindTarget = isArrowBindTarget;
3018
+ exports.isLinkItem = isLinkItem;
2744
3019
  exports.isManagedImage = isManagedImage;
2745
3020
  exports.itemHitTestWorldPoint = itemHitTestWorldPoint;
2746
3021
  exports.lineEndpointsToLocal = lineEndpointsToLocal;