canvu-react 0.4.30 → 0.4.32
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/{asset-hydration-CWhld14A.d.cts → asset-hydration-BFGZ3igr.d.cts} +1 -1
- package/dist/{asset-hydration-D35mHbUP.d.ts → asset-hydration-D6Q3TJL1.d.ts} +1 -1
- package/dist/chatbot.d.cts +2 -2
- package/dist/chatbot.d.ts +2 -2
- package/dist/index.cjs +182 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +182 -102
- package/dist/index.js.map +1 -1
- package/dist/{link-item-D870_X6P.d.cts → link-item-Dncuz2d_.d.cts} +3 -3
- package/dist/{link-item-Bg5vj0RI.d.ts → link-item-voRU0Up9.d.ts} +3 -3
- package/dist/native.cjs +229 -3
- package/dist/native.cjs.map +1 -1
- package/dist/native.js +229 -3
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +260 -21
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +9 -7
- package/dist/react.d.ts +9 -7
- package/dist/react.js +261 -22
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +3 -3
- package/dist/realtime.d.ts +3 -3
- package/dist/realtime.js.map +1 -1
- package/dist/tldraw.cjs +129 -38
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.js +129 -38
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-Bw1SdC9v.d.cts → types-BS-YG8Hx.d.cts} +1 -1
- package/dist/{types-DeXFfs7Y.d.ts → types-UZYYwK-v.d.ts} +1 -1
- package/package.json +1 -1
package/dist/react.d.cts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { I as IndexedDbImageStore } from './asset-hydration-
|
|
2
|
-
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-
|
|
1
|
+
import { I as IndexedDbImageStore } from './asset-hydration-BFGZ3igr.cjs';
|
|
2
|
+
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-BFGZ3igr.cjs';
|
|
3
3
|
import { V as VectorSceneItem } from './types-BCCvY6ie.cjs';
|
|
4
|
-
import { V as VectorViewportAssetKind, a as VectorViewportAssetStore } from './link-item-
|
|
5
|
-
export { d as VectorViewportAssetHydrationRequest, e as VectorViewportAssetResolveRequest, f as VectorViewportAssetResolveResult, h as VectorViewportAssetUploadRequest, j as VectorViewportAssetUploadResult } from './link-item-
|
|
6
|
-
import { B as BoardComponentPosition, V as VectorToolDefinition, C as CanvasPlugin, a as VectorSelectionInspector } from './types-
|
|
7
|
-
export { b as CanvasPluginComponentProps, c as CanvasPluginContribution, d as CanvasPluginItemsChangeMiddlewareContext, e as CanvasPluginRenderContext, f as CanvuChromeActiveToolStyle, g as CanvuChromeContext, h as CanvuChromeContextValue, i as CanvuChromeSelectionStyleChange, j as CanvuPluginContext, k as CanvuPluginContextValue, l as CanvuPluginViewportSnapshot, m as CustomShapePlacementOptions, P as PlacementPreview, n as VectorCanvasSpacePosition, o as VectorSelectionInspectorProps, p as VectorViewport, q as VectorViewportHandle, r as VectorViewportProps, W as WorldPointerDownDetail, s as createCanvuPlugin, t as getBoardPositionStyle, u as useCanvuChromeContext, v as useCanvuDocumentContext, w as useCanvuPluginContext, x as useCanvuPluginContribution, y as useCanvuResolvedTools, z as useCanvuViewportContext } from './types-
|
|
4
|
+
import { V as VectorViewportAssetKind, a as VectorViewportAssetStore } from './link-item-Dncuz2d_.cjs';
|
|
5
|
+
export { d as VectorViewportAssetHydrationRequest, e as VectorViewportAssetResolveRequest, f as VectorViewportAssetResolveResult, h as VectorViewportAssetUploadRequest, j as VectorViewportAssetUploadResult } from './link-item-Dncuz2d_.cjs';
|
|
6
|
+
import { B as BoardComponentPosition, V as VectorToolDefinition, C as CanvasPlugin, a as VectorSelectionInspector } from './types-BS-YG8Hx.cjs';
|
|
7
|
+
export { b as CanvasPluginComponentProps, c as CanvasPluginContribution, d as CanvasPluginItemsChangeMiddlewareContext, e as CanvasPluginRenderContext, f as CanvuChromeActiveToolStyle, g as CanvuChromeContext, h as CanvuChromeContextValue, i as CanvuChromeSelectionStyleChange, j as CanvuPluginContext, k as CanvuPluginContextValue, l as CanvuPluginViewportSnapshot, m as CustomShapePlacementOptions, P as PlacementPreview, n as VectorCanvasSpacePosition, o as VectorSelectionInspectorProps, p as VectorViewport, q as VectorViewportHandle, r as VectorViewportProps, W as WorldPointerDownDetail, s as createCanvuPlugin, t as getBoardPositionStyle, u as useCanvuChromeContext, v as useCanvuDocumentContext, w as useCanvuPluginContext, x as useCanvuPluginContribution, y as useCanvuResolvedTools, z as useCanvuViewportContext } from './types-BS-YG8Hx.cjs';
|
|
8
8
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
9
9
|
import * as react from 'react';
|
|
10
10
|
import { CSSProperties, ReactNode, ReactElement, SVGProps } from 'react';
|
|
@@ -158,6 +158,8 @@ type ImagesMenuLabels = {
|
|
|
158
158
|
title?: string;
|
|
159
159
|
dragHandle?: string;
|
|
160
160
|
focus?: string;
|
|
161
|
+
duplicate?: string;
|
|
162
|
+
/** @deprecated Use `duplicate` instead. */
|
|
161
163
|
copy?: string;
|
|
162
164
|
rotate?: string;
|
|
163
165
|
delete?: string;
|
|
@@ -181,7 +183,7 @@ type ImagesMenuProps = {
|
|
|
181
183
|
};
|
|
182
184
|
/**
|
|
183
185
|
* Floating side panel that lists images marked as "managed"
|
|
184
|
-
* (via `pluginData.managed === true`) and offers
|
|
186
|
+
* (via `pluginData.managed === true`) and offers duplicate, rotate, delete, and
|
|
185
187
|
* drag-to-reorder actions. Managed images are also `locked`, so they do not
|
|
186
188
|
* respond to direct canvas interaction.
|
|
187
189
|
*/
|
package/dist/react.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { I as IndexedDbImageStore } from './asset-hydration-
|
|
2
|
-
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-
|
|
1
|
+
import { I as IndexedDbImageStore } from './asset-hydration-D6Q3TJL1.js';
|
|
2
|
+
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-D6Q3TJL1.js';
|
|
3
3
|
import { V as VectorSceneItem } from './types-BCCvY6ie.js';
|
|
4
|
-
import { V as VectorViewportAssetKind, a as VectorViewportAssetStore } from './link-item-
|
|
5
|
-
export { d as VectorViewportAssetHydrationRequest, e as VectorViewportAssetResolveRequest, f as VectorViewportAssetResolveResult, h as VectorViewportAssetUploadRequest, j as VectorViewportAssetUploadResult } from './link-item-
|
|
6
|
-
import { B as BoardComponentPosition, V as VectorToolDefinition, C as CanvasPlugin, a as VectorSelectionInspector } from './types-
|
|
7
|
-
export { b as CanvasPluginComponentProps, c as CanvasPluginContribution, d as CanvasPluginItemsChangeMiddlewareContext, e as CanvasPluginRenderContext, f as CanvuChromeActiveToolStyle, g as CanvuChromeContext, h as CanvuChromeContextValue, i as CanvuChromeSelectionStyleChange, j as CanvuPluginContext, k as CanvuPluginContextValue, l as CanvuPluginViewportSnapshot, m as CustomShapePlacementOptions, P as PlacementPreview, n as VectorCanvasSpacePosition, o as VectorSelectionInspectorProps, p as VectorViewport, q as VectorViewportHandle, r as VectorViewportProps, W as WorldPointerDownDetail, s as createCanvuPlugin, t as getBoardPositionStyle, u as useCanvuChromeContext, v as useCanvuDocumentContext, w as useCanvuPluginContext, x as useCanvuPluginContribution, y as useCanvuResolvedTools, z as useCanvuViewportContext } from './types-
|
|
4
|
+
import { V as VectorViewportAssetKind, a as VectorViewportAssetStore } from './link-item-voRU0Up9.js';
|
|
5
|
+
export { d as VectorViewportAssetHydrationRequest, e as VectorViewportAssetResolveRequest, f as VectorViewportAssetResolveResult, h as VectorViewportAssetUploadRequest, j as VectorViewportAssetUploadResult } from './link-item-voRU0Up9.js';
|
|
6
|
+
import { B as BoardComponentPosition, V as VectorToolDefinition, C as CanvasPlugin, a as VectorSelectionInspector } from './types-UZYYwK-v.js';
|
|
7
|
+
export { b as CanvasPluginComponentProps, c as CanvasPluginContribution, d as CanvasPluginItemsChangeMiddlewareContext, e as CanvasPluginRenderContext, f as CanvuChromeActiveToolStyle, g as CanvuChromeContext, h as CanvuChromeContextValue, i as CanvuChromeSelectionStyleChange, j as CanvuPluginContext, k as CanvuPluginContextValue, l as CanvuPluginViewportSnapshot, m as CustomShapePlacementOptions, P as PlacementPreview, n as VectorCanvasSpacePosition, o as VectorSelectionInspectorProps, p as VectorViewport, q as VectorViewportHandle, r as VectorViewportProps, W as WorldPointerDownDetail, s as createCanvuPlugin, t as getBoardPositionStyle, u as useCanvuChromeContext, v as useCanvuDocumentContext, w as useCanvuPluginContext, x as useCanvuPluginContribution, y as useCanvuResolvedTools, z as useCanvuViewportContext } from './types-UZYYwK-v.js';
|
|
8
8
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
9
9
|
import * as react from 'react';
|
|
10
10
|
import { CSSProperties, ReactNode, ReactElement, SVGProps } from 'react';
|
|
@@ -158,6 +158,8 @@ type ImagesMenuLabels = {
|
|
|
158
158
|
title?: string;
|
|
159
159
|
dragHandle?: string;
|
|
160
160
|
focus?: string;
|
|
161
|
+
duplicate?: string;
|
|
162
|
+
/** @deprecated Use `duplicate` instead. */
|
|
161
163
|
copy?: string;
|
|
162
164
|
rotate?: string;
|
|
163
165
|
delete?: string;
|
|
@@ -181,7 +183,7 @@ type ImagesMenuProps = {
|
|
|
181
183
|
};
|
|
182
184
|
/**
|
|
183
185
|
* Floating side panel that lists images marked as "managed"
|
|
184
|
-
* (via `pluginData.managed === true`) and offers
|
|
186
|
+
* (via `pluginData.managed === true`) and offers duplicate, rotate, delete, and
|
|
185
187
|
* drag-to-reorder actions. Managed images are also `locked`, so they do not
|
|
186
188
|
* respond to direct canvas interaction.
|
|
187
189
|
*/
|
package/dist/react.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createContext, forwardRef, useRef, useState, useCallback, useMemo, useI
|
|
|
3
3
|
import { useSensors, useSensor, PointerSensor, DndContext } from '@dnd-kit/core';
|
|
4
4
|
import { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable';
|
|
5
5
|
import { CSS } from '@dnd-kit/utilities';
|
|
6
|
-
import { Undo2, Redo2, Shapes, ChevronDown, Hand, MousePointer2, Square, Circle, Minus, ArrowUpRight, PenLine, Highlighter, Eraser, Type, Image as Image$1, Lock, LockOpen, Images, ChevronRight, GripVertical,
|
|
6
|
+
import { Undo2, Redo2, Shapes, ChevronDown, Hand, MousePointer2, Square, Circle, Minus, ArrowUpRight, PenLine, Highlighter, Eraser, Type, Image as Image$1, Lock, LockOpen, Images, ChevronRight, GripVertical, CopyPlus, RotateCw, Trash2 } from 'lucide-react';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
|
|
@@ -49,11 +49,170 @@ var init_custom_shape = __esm({
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
// src/scene/link-item.ts
|
|
53
|
+
function buildLinkCardSvg(width, _height, link) {
|
|
54
|
+
const cardWidth = Math.max(1, width);
|
|
55
|
+
const scale = cardWidth / DEFAULT_LINK_CARD_WIDTH;
|
|
56
|
+
const contentWidth = DEFAULT_LINK_CARD_WIDTH;
|
|
57
|
+
const contentHeight = DEFAULT_LINK_CARD_HEIGHT;
|
|
58
|
+
const padding = 14;
|
|
59
|
+
const badgeSize = 42;
|
|
60
|
+
const gap = 13;
|
|
61
|
+
const buttonSize = 34;
|
|
62
|
+
const textX = padding + badgeSize + gap;
|
|
63
|
+
const textWidth = Math.max(1, contentWidth - textX - buttonSize - gap - padding);
|
|
64
|
+
const hostname = getLinkHostname(link.href);
|
|
65
|
+
const title = link.title?.trim() || hostname || "Link";
|
|
66
|
+
const protocol = getLinkProtocol(link.href);
|
|
67
|
+
const subtitle = hostname || link.href;
|
|
68
|
+
const favicon = link.favicon?.trim() || buildGoogleFaviconUrl(hostname);
|
|
69
|
+
const idSuffix = getStableLinkIdSuffix(`${hostname}:${link.href}`);
|
|
70
|
+
const clipId = `canvu-link-favicon-${idSuffix}`;
|
|
71
|
+
const gradientId = `canvu-link-favicon-gradient-${idSuffix}`;
|
|
72
|
+
const buttonX = contentWidth - padding - buttonSize;
|
|
73
|
+
const buttonY = (contentHeight - buttonSize) / 2;
|
|
74
|
+
const isSecure = protocol === "https:";
|
|
75
|
+
const subtitleX = isSecure ? textX + 13 : textX;
|
|
76
|
+
const subtitleWidth = isSecure ? textWidth - 13 : textWidth;
|
|
77
|
+
const faviconImage = favicon ? `<image class="canvu-link-favicon-img" href="${escapeXmlAttribute(favicon)}" x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" preserveAspectRatio="xMidYMid slice" clip-path="url(#${clipId})" />` : "";
|
|
78
|
+
return `
|
|
79
|
+
<style>
|
|
80
|
+
.canvu-link-card-root .canvu-link-card { transition: transform .18s ease, filter .18s ease, stroke .18s ease; }
|
|
81
|
+
.canvu-link-card-root .canvu-link-open { opacity: 0; transform: translateX(-4px); transition: opacity .18s ease, transform .18s ease; }
|
|
82
|
+
.canvu-link-card-root:hover .canvu-link-card { transform: translateY(-2px); filter: drop-shadow(0 4px 14px oklch(0.4 0.05 265 / .08)) drop-shadow(0 1px 3px oklch(0.4 0.05 265 / .06)); stroke: ${LINK_CARD_BORDER_STRONG}; }
|
|
83
|
+
.canvu-link-card-root:hover .canvu-link-open { opacity: 1; transform: translateX(0); }
|
|
84
|
+
</style>
|
|
85
|
+
<g class="canvu-link-card-root" transform="scale(${formatNumber(scale)})">
|
|
86
|
+
<rect class="canvu-link-card" width="${formatNumber(contentWidth)}" height="${formatNumber(contentHeight)}" rx="16" fill="#ffffff" stroke="${LINK_CARD_BORDER}" stroke-width="1" filter="drop-shadow(0 1px 2px oklch(0.4 0.03 265 / .05)) drop-shadow(0 1px 1px oklch(0.4 0.03 265 / .04))" />
|
|
87
|
+
<defs>
|
|
88
|
+
<linearGradient id="${gradientId}" x1="8" y1="48" x2="48" y2="8" gradientUnits="userSpaceOnUse">
|
|
89
|
+
<stop stop-color="${LINK_CARD_ACCENT}" />
|
|
90
|
+
<stop offset="1" stop-color="${LINK_CARD_ACCENT_DEEP}" />
|
|
91
|
+
</linearGradient>
|
|
92
|
+
<clipPath id="${clipId}">
|
|
93
|
+
<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="11" />
|
|
94
|
+
</clipPath>
|
|
95
|
+
</defs>
|
|
96
|
+
<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="11" fill="url(#${gradientId})" />
|
|
97
|
+
<text x="${formatNumber(padding + badgeSize / 2)}" y="${formatNumber(padding + badgeSize / 2 + 5)}" text-anchor="middle" font-family="system-ui,sans-serif" font-size="17" font-weight="700" fill="#ffffff">${escapeHtmlText(getLinkInitial(hostname))}</text>
|
|
98
|
+
${faviconImage}
|
|
99
|
+
${buildLinkTextBand({ x: textX, y: 16, width: textWidth, height: 19, text: title, fontSize: 14.5, color: LINK_CARD_TITLE_COLOR, fontWeight: 700 })}
|
|
100
|
+
${isSecure ? `<g transform="translate(${formatNumber(textX)},40)" stroke="${LINK_CARD_TEXT_COLOR}" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" fill="none"><rect x="1.5" y="4.5" width="7" height="6" rx="1" /><path d="M3 4.5 V3 a2 2 0 0 1 4 0 v1.5" /></g>` : ""}
|
|
101
|
+
${buildLinkTextBand({ x: subtitleX, y: 36, width: subtitleWidth, height: 17, text: subtitle, fontSize: 12.5, color: LINK_CARD_TEXT_COLOR })}
|
|
102
|
+
<g class="canvu-link-open" transform="translate(${formatNumber(buttonX)},${formatNumber(buttonY)})">
|
|
103
|
+
<rect width="${formatNumber(buttonSize)}" height="${formatNumber(buttonSize)}" rx="10" fill="#ffffff" stroke="${LINK_CARD_BORDER}" stroke-width="1" />
|
|
104
|
+
<g transform="translate(10,10)" stroke="${LINK_CARD_TEXT_COLOR}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none">
|
|
105
|
+
<path d="M8 2 H14 V8" />
|
|
106
|
+
<path d="M14 2 L7 9" />
|
|
107
|
+
<path d="M12 10 V13 a1 1 0 0 1 -1 1 H3 a1 1 0 0 1 -1 -1 V5 a1 1 0 0 1 1 -1 H6" />
|
|
108
|
+
</g>
|
|
109
|
+
</g>
|
|
110
|
+
</g>
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
function getLinkData(item) {
|
|
114
|
+
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
115
|
+
return isCanvuLinkData(entry) ? entry : null;
|
|
116
|
+
}
|
|
117
|
+
function rebuildLinkItemSvg(item) {
|
|
118
|
+
const link = getLinkData(item);
|
|
119
|
+
if (!link) return item;
|
|
120
|
+
const scale = clamp(
|
|
121
|
+
item.bounds.width / DEFAULT_LINK_CARD_WIDTH,
|
|
122
|
+
LINK_CARD_MIN_SCALE,
|
|
123
|
+
LINK_CARD_MAX_SCALE
|
|
124
|
+
);
|
|
125
|
+
const width = DEFAULT_LINK_CARD_WIDTH * scale;
|
|
126
|
+
const height = DEFAULT_LINK_CARD_HEIGHT * scale;
|
|
127
|
+
const bounds = {
|
|
128
|
+
...item.bounds,
|
|
129
|
+
width,
|
|
130
|
+
height
|
|
131
|
+
};
|
|
132
|
+
const customInnerSvg = buildLinkCardSvg(
|
|
133
|
+
DEFAULT_LINK_CARD_WIDTH,
|
|
134
|
+
DEFAULT_LINK_CARD_HEIGHT,
|
|
135
|
+
link
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
...item,
|
|
139
|
+
x: bounds.x,
|
|
140
|
+
y: bounds.y,
|
|
141
|
+
bounds,
|
|
142
|
+
customIntrinsicSize: {
|
|
143
|
+
width: DEFAULT_LINK_CARD_WIDTH,
|
|
144
|
+
height: DEFAULT_LINK_CARD_HEIGHT
|
|
145
|
+
},
|
|
146
|
+
customInnerSvg,
|
|
147
|
+
childrenSvg: buildLinkCardSvg(width, height, link)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
var LINK_PLUGIN_KEY, DEFAULT_LINK_CARD_WIDTH, DEFAULT_LINK_CARD_HEIGHT, LINK_CARD_MIN_SCALE, LINK_CARD_MAX_SCALE, LINK_CARD_ASPECT, LINK_CARD_BORDER, LINK_CARD_BORDER_STRONG, LINK_CARD_ACCENT, LINK_CARD_ACCENT_DEEP, LINK_CARD_TITLE_COLOR, LINK_CARD_TEXT_COLOR, clamp, formatNumber, escapeXmlAttribute, escapeHtmlText, getLinkHostname, buildLinkTextBand, getLinkProtocol, getLinkInitial, buildGoogleFaviconUrl, getStableLinkIdSuffix, isCanvuLinkData;
|
|
151
|
+
var init_link_item = __esm({
|
|
152
|
+
"src/scene/link-item.ts"() {
|
|
153
|
+
LINK_PLUGIN_KEY = "canvuLink";
|
|
154
|
+
DEFAULT_LINK_CARD_WIDTH = 320;
|
|
155
|
+
DEFAULT_LINK_CARD_HEIGHT = 70;
|
|
156
|
+
LINK_CARD_MIN_SCALE = 0.6;
|
|
157
|
+
LINK_CARD_MAX_SCALE = 2.5;
|
|
158
|
+
LINK_CARD_ASPECT = DEFAULT_LINK_CARD_WIDTH / DEFAULT_LINK_CARD_HEIGHT;
|
|
159
|
+
LINK_CARD_BORDER = "oklch(0.918 0.008 255)";
|
|
160
|
+
LINK_CARD_BORDER_STRONG = "oklch(0.86 0.012 255)";
|
|
161
|
+
LINK_CARD_ACCENT = "oklch(0.55 0.19 264)";
|
|
162
|
+
LINK_CARD_ACCENT_DEEP = "oklch(0.46 0.18 264)";
|
|
163
|
+
LINK_CARD_TITLE_COLOR = "oklch(0.26 0.022 265)";
|
|
164
|
+
LINK_CARD_TEXT_COLOR = "oklch(0.55 0.022 265)";
|
|
165
|
+
clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
166
|
+
formatNumber = (value) => {
|
|
167
|
+
const rounded = Math.round(value * 100) / 100;
|
|
168
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
169
|
+
};
|
|
170
|
+
escapeXmlAttribute = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
171
|
+
escapeHtmlText = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
172
|
+
getLinkHostname = (href) => {
|
|
173
|
+
try {
|
|
174
|
+
return new URL(href).hostname.replace(/^www\./, "");
|
|
175
|
+
} catch {
|
|
176
|
+
return href;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
buildLinkTextBand = (band) => {
|
|
180
|
+
const lineHeight = band.height;
|
|
181
|
+
const weight = band.fontWeight != null ? `font-weight:${band.fontWeight};` : "";
|
|
182
|
+
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;letter-spacing:-0.01em;color:${band.color};overflow:hidden;white-space:nowrap;text-overflow:ellipsis;${weight}">${escapeHtmlText(band.text)}</div></foreignObject>`;
|
|
183
|
+
};
|
|
184
|
+
getLinkProtocol = (href) => {
|
|
185
|
+
try {
|
|
186
|
+
return new URL(href).protocol;
|
|
187
|
+
} catch {
|
|
188
|
+
return "";
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
getLinkInitial = (hostname) => {
|
|
192
|
+
const first = hostname.trim().charAt(0).toUpperCase();
|
|
193
|
+
return first || "L";
|
|
194
|
+
};
|
|
195
|
+
buildGoogleFaviconUrl = (hostname) => hostname ? `https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=64` : null;
|
|
196
|
+
getStableLinkIdSuffix = (value) => {
|
|
197
|
+
let hash = 0;
|
|
198
|
+
for (const char of value) {
|
|
199
|
+
hash = hash * 31 + char.charCodeAt(0) >>> 0;
|
|
200
|
+
}
|
|
201
|
+
return hash.toString(36);
|
|
202
|
+
};
|
|
203
|
+
isCanvuLinkData = (value) => {
|
|
204
|
+
if (!value || typeof value !== "object") return false;
|
|
205
|
+
const candidate = value;
|
|
206
|
+
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
52
211
|
// src/scene/text-svg.ts
|
|
53
212
|
function escapeSvgTextContent(s) {
|
|
54
213
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
55
214
|
}
|
|
56
|
-
function
|
|
215
|
+
function escapeHtmlText2(s) {
|
|
57
216
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
58
217
|
}
|
|
59
218
|
function getSharedMeasureContext() {
|
|
@@ -154,9 +313,9 @@ function buildTextFixedBoundsSvg(content, width, height, fillColor = "#1d1d1d",
|
|
|
154
313
|
const trimmed = content.trim();
|
|
155
314
|
const padTop = EDIT_TOP_PAD_RATIO * fontSize;
|
|
156
315
|
if (trimmed.length === 0) {
|
|
157
|
-
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:#94a3b8;font-style:italic">${
|
|
316
|
+
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:#94a3b8;font-style:italic">${escapeHtmlText2(PLACEHOLDER)}</div></foreignObject>`;
|
|
158
317
|
}
|
|
159
|
-
const body =
|
|
318
|
+
const body = escapeHtmlText2(content);
|
|
160
319
|
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:${fillColor}">${body}</div></foreignObject>`;
|
|
161
320
|
}
|
|
162
321
|
var DEFAULT_TEXT_FONT_SIZE, DEFAULT_TEXT_TOOL_FONT_SIZE, TEXT_FONT_FAMILY, LINE_HEIGHT_RATIO, FIRST_LINE_BASELINE_RATIO, EDIT_TOP_PAD_RATIO, BOTTOM_PAD_RATIO, PLACEHOLDER, MIN_TEXT_BOX_W, MIN_TEXT_BOX_H, TEXT_PAD_X, MAX_TEXT_MEASURE_CACHE_ENTRIES, sharedMeasureContext, textMeasureCache;
|
|
@@ -813,6 +972,9 @@ function rebuildItemSvg(item) {
|
|
|
813
972
|
)
|
|
814
973
|
};
|
|
815
974
|
}
|
|
975
|
+
if (getLinkData(item)) {
|
|
976
|
+
return rebuildLinkItemSvg(item);
|
|
977
|
+
}
|
|
816
978
|
if (k === "custom" && item.customIntrinsicSize && item.customInnerSvg) {
|
|
817
979
|
const b = normalizeRect(item.bounds);
|
|
818
980
|
return {
|
|
@@ -1038,6 +1200,7 @@ var init_shape_builders = __esm({
|
|
|
1038
1200
|
"src/scene/shape-builders.ts"() {
|
|
1039
1201
|
init_rect();
|
|
1040
1202
|
init_custom_shape();
|
|
1203
|
+
init_link_item();
|
|
1041
1204
|
init_text_svg();
|
|
1042
1205
|
DEFAULT_STROKE_STYLE = {
|
|
1043
1206
|
stroke: "#1d1d1d",
|
|
@@ -2198,7 +2361,7 @@ var defaultLabels = {
|
|
|
2198
2361
|
title: "Images",
|
|
2199
2362
|
dragHandle: "Drag to reorder",
|
|
2200
2363
|
focus: "Focus on canvas",
|
|
2201
|
-
|
|
2364
|
+
duplicate: "Duplicate",
|
|
2202
2365
|
rotate: "Rotate",
|
|
2203
2366
|
delete: "Delete",
|
|
2204
2367
|
collapse: "Collapse images menu",
|
|
@@ -2224,7 +2387,11 @@ function ImagesMenu({
|
|
|
2224
2387
|
if (managed.length === 0) {
|
|
2225
2388
|
return null;
|
|
2226
2389
|
}
|
|
2227
|
-
const resolvedLabels = {
|
|
2390
|
+
const resolvedLabels = {
|
|
2391
|
+
...defaultLabels,
|
|
2392
|
+
...labels,
|
|
2393
|
+
duplicate: labels?.duplicate ?? labels?.copy ?? defaultLabels.duplicate
|
|
2394
|
+
};
|
|
2228
2395
|
if (collapsed) {
|
|
2229
2396
|
return /* @__PURE__ */ jsx(
|
|
2230
2397
|
"button",
|
|
@@ -2338,7 +2505,7 @@ function ImagesMenuRow({
|
|
|
2338
2505
|
),
|
|
2339
2506
|
/* @__PURE__ */ jsx(FocusTarget, { label: labels.focus, onFocus, children: /* @__PURE__ */ jsx("div", { style: thumbBoxStyle, children: src ? /* @__PURE__ */ jsx("img", { src, alt: "", style: thumbImgStyle, draggable: false }) : null }) }),
|
|
2340
2507
|
/* @__PURE__ */ jsxs("div", { style: actionsColumnStyle, children: [
|
|
2341
|
-
/* @__PURE__ */ jsx(ImagesMenuAction, { label: labels.
|
|
2508
|
+
/* @__PURE__ */ jsx(ImagesMenuAction, { label: labels.duplicate, onClick: onCopy, children: /* @__PURE__ */ jsx(CopyPlus, { size: 18 }) }),
|
|
2342
2509
|
/* @__PURE__ */ jsx(ImagesMenuAction, { label: labels.rotate, onClick: onRotate, children: /* @__PURE__ */ jsx(RotateCw, { size: 18 }) }),
|
|
2343
2510
|
/* @__PURE__ */ jsx(ImagesMenuAction, { label: labels.delete, onClick: onDelete, danger: true, children: /* @__PURE__ */ jsx(Trash2, { size: 18 }) })
|
|
2344
2511
|
] })
|
|
@@ -6351,10 +6518,80 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
|
|
|
6351
6518
|
|
|
6352
6519
|
// src/interaction/mutations.ts
|
|
6353
6520
|
init_rect();
|
|
6521
|
+
init_link_item();
|
|
6354
6522
|
init_shape_builders();
|
|
6355
6523
|
init_text_svg();
|
|
6524
|
+
var LINK_CORNER_HANDLES = /* @__PURE__ */ new Set(["nw", "ne", "se", "sw"]);
|
|
6525
|
+
var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
6526
|
+
var clampLinkResizeBounds = (startBounds, nextBounds, handle, intrinsicWidth) => {
|
|
6527
|
+
const next = normalizeRect(nextBounds);
|
|
6528
|
+
const scale = clamp2(
|
|
6529
|
+
next.width / Math.max(1e-9, intrinsicWidth),
|
|
6530
|
+
LINK_CARD_MIN_SCALE,
|
|
6531
|
+
LINK_CARD_MAX_SCALE
|
|
6532
|
+
);
|
|
6533
|
+
const width = intrinsicWidth * scale;
|
|
6534
|
+
const height = width / LINK_CARD_ASPECT;
|
|
6535
|
+
const start = normalizeRect(startBounds);
|
|
6536
|
+
const x0 = start.x;
|
|
6537
|
+
const y0 = start.y;
|
|
6538
|
+
const x1 = start.x + start.width;
|
|
6539
|
+
const y1 = start.y + start.height;
|
|
6540
|
+
switch (handle) {
|
|
6541
|
+
case "nw":
|
|
6542
|
+
return { x: x1 - width, y: y1 - height, width, height };
|
|
6543
|
+
case "ne":
|
|
6544
|
+
return { x: x0, y: y1 - height, width, height };
|
|
6545
|
+
case "sw":
|
|
6546
|
+
return { x: x1 - width, y: y0, width, height };
|
|
6547
|
+
default:
|
|
6548
|
+
return { x: x0, y: y0, width, height };
|
|
6549
|
+
}
|
|
6550
|
+
};
|
|
6356
6551
|
function computeNewBoundsForResize(item, sb, handle, currentWorld) {
|
|
6357
6552
|
const rot = getItemRotationRad(item);
|
|
6553
|
+
const link = getLinkData(item);
|
|
6554
|
+
if (link && item.customIntrinsicSize) {
|
|
6555
|
+
if (!LINK_CORNER_HANDLES.has(handle)) return sb;
|
|
6556
|
+
const intrinsicWidth = Math.max(1e-9, item.customIntrinsicSize.width);
|
|
6557
|
+
if (Math.abs(rot) < 1e-12) {
|
|
6558
|
+
const next = computeResizeBoundsFixedAspect(
|
|
6559
|
+
sb,
|
|
6560
|
+
handle,
|
|
6561
|
+
currentWorld,
|
|
6562
|
+
LINK_CARD_ASPECT
|
|
6563
|
+
);
|
|
6564
|
+
return clampLinkResizeBounds(sb, next, handle, intrinsicWidth);
|
|
6565
|
+
}
|
|
6566
|
+
const local2 = worldToItemLocal(
|
|
6567
|
+
currentWorld.x,
|
|
6568
|
+
currentWorld.y,
|
|
6569
|
+
sb.x,
|
|
6570
|
+
sb.y,
|
|
6571
|
+
sb.width,
|
|
6572
|
+
sb.height,
|
|
6573
|
+
rot
|
|
6574
|
+
);
|
|
6575
|
+
const localBounds2 = { x: 0, y: 0, width: sb.width, height: sb.height };
|
|
6576
|
+
const nextLocal = computeResizeBoundsFixedAspect(
|
|
6577
|
+
localBounds2,
|
|
6578
|
+
handle,
|
|
6579
|
+
local2,
|
|
6580
|
+
LINK_CARD_ASPECT
|
|
6581
|
+
);
|
|
6582
|
+
const clamped = clampLinkResizeBounds(
|
|
6583
|
+
localBounds2,
|
|
6584
|
+
nextLocal,
|
|
6585
|
+
handle,
|
|
6586
|
+
intrinsicWidth
|
|
6587
|
+
);
|
|
6588
|
+
return {
|
|
6589
|
+
x: sb.x + clamped.x,
|
|
6590
|
+
y: sb.y + clamped.y,
|
|
6591
|
+
width: clamped.width,
|
|
6592
|
+
height: clamped.height
|
|
6593
|
+
};
|
|
6594
|
+
}
|
|
6358
6595
|
if (Math.abs(rot) < 1e-12) {
|
|
6359
6596
|
if (item.toolKind === "image") {
|
|
6360
6597
|
let aspect;
|
|
@@ -6827,17 +7064,8 @@ var SvgVectorRenderer = class {
|
|
|
6827
7064
|
}
|
|
6828
7065
|
};
|
|
6829
7066
|
|
|
6830
|
-
// src/
|
|
6831
|
-
|
|
6832
|
-
var isCanvuLinkData = (value) => {
|
|
6833
|
-
if (!value || typeof value !== "object") return false;
|
|
6834
|
-
const candidate = value;
|
|
6835
|
-
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
6836
|
-
};
|
|
6837
|
-
function getLinkData(item) {
|
|
6838
|
-
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
6839
|
-
return isCanvuLinkData(entry) ? entry : null;
|
|
6840
|
-
}
|
|
7067
|
+
// src/react/VectorViewport.tsx
|
|
7068
|
+
init_link_item();
|
|
6841
7069
|
|
|
6842
7070
|
// src/scene/scene.ts
|
|
6843
7071
|
var VectorScene = class {
|
|
@@ -6931,8 +7159,10 @@ function smoothFreehandPointsToPathD(points) {
|
|
|
6931
7159
|
}
|
|
6932
7160
|
|
|
6933
7161
|
// src/react/InteractionOverlay.tsx
|
|
7162
|
+
init_link_item();
|
|
6934
7163
|
init_shape_builders();
|
|
6935
7164
|
var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
7165
|
+
var LINK_HANDLE_ORDER = ["nw", "ne", "se", "sw"];
|
|
6936
7166
|
var ERASER_TINT = "#cbd5e1";
|
|
6937
7167
|
var ERASER_TINT_OPACITY = 0.95;
|
|
6938
7168
|
var ERASER_PREVIEW_OPACITY = 0.3;
|
|
@@ -6985,7 +7215,7 @@ function InteractionOverlay({
|
|
|
6985
7215
|
) }, it.id);
|
|
6986
7216
|
}),
|
|
6987
7217
|
showResizeHandles && bSingle && single && rotHandlePos && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6988
|
-
HANDLE_ORDER.map((hid) => {
|
|
7218
|
+
(getLinkData(single) ? LINK_HANDLE_ORDER : HANDLE_ORDER).map((hid) => {
|
|
6989
7219
|
const p = getHandleWorldPositionRotated(bSingle, hid, rotSingle);
|
|
6990
7220
|
return /* @__PURE__ */ jsx(
|
|
6991
7221
|
"circle",
|
|
@@ -7799,6 +8029,7 @@ function defaultPlacementWorld(tool, center) {
|
|
|
7799
8029
|
lineWorld: [a, b]
|
|
7800
8030
|
};
|
|
7801
8031
|
}
|
|
8032
|
+
var LINK_CORNER_HANDLES2 = /* @__PURE__ */ new Set(["nw", "ne", "se", "sw"]);
|
|
7802
8033
|
function pointInSelectedItemBounds(item, worldX, worldY) {
|
|
7803
8034
|
const bounds = normalizeRect(item.bounds);
|
|
7804
8035
|
const local = worldToItemLocal(
|
|
@@ -8276,7 +8507,11 @@ var VectorViewport = forwardRef(
|
|
|
8276
8507
|
setEffectiveSelectedIdsRef.current = setEffectiveSelectedIds;
|
|
8277
8508
|
toolIdRef.current = toolId;
|
|
8278
8509
|
interactiveRef.current = interactive;
|
|
8279
|
-
|
|
8510
|
+
const normalizedItems = useMemo(
|
|
8511
|
+
() => items.map((item) => getLinkData(item) ? rebuildLinkItemSvg(item) : item),
|
|
8512
|
+
[items]
|
|
8513
|
+
);
|
|
8514
|
+
itemsRef.current = normalizedItems;
|
|
8280
8515
|
onWorldPointerDownRef.current = onWorldPointerDown;
|
|
8281
8516
|
const originalOnItemsChangeRef = useRef(onItemsChange);
|
|
8282
8517
|
originalOnItemsChangeRef.current = onItemsChange;
|
|
@@ -8297,7 +8532,10 @@ var VectorViewport = forwardRef(
|
|
|
8297
8532
|
autoResetToolToRef.current = autoResetToolTo;
|
|
8298
8533
|
const onToolChangeRequestRef = useRef(onToolChangeRequest);
|
|
8299
8534
|
onToolChangeRequestRef.current = onToolChangeRequest;
|
|
8300
|
-
const resolvedItems = useMemo(
|
|
8535
|
+
const resolvedItems = useMemo(
|
|
8536
|
+
() => resolveArrowBindingsInScene(normalizedItems),
|
|
8537
|
+
[normalizedItems]
|
|
8538
|
+
);
|
|
8301
8539
|
const resolvedItemsRef = useRef(resolvedItems);
|
|
8302
8540
|
resolvedItemsRef.current = resolvedItems;
|
|
8303
8541
|
const liveId = useId();
|
|
@@ -9718,7 +9956,8 @@ var VectorViewport = forwardRef(
|
|
|
9718
9956
|
handleRadiusWorld,
|
|
9719
9957
|
rot
|
|
9720
9958
|
);
|
|
9721
|
-
|
|
9959
|
+
const isLinkResizeHandle = hb && getLinkData(selected) ? LINK_CORNER_HANDLES2.has(hb) : Boolean(hb);
|
|
9960
|
+
if (hb && isLinkResizeHandle) {
|
|
9722
9961
|
const snapRs = bakedSnapshot(selected.id);
|
|
9723
9962
|
if (!snapRs) return;
|
|
9724
9963
|
dragStateRef.current = {
|