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.cjs
CHANGED
|
@@ -56,11 +56,170 @@ var init_custom_shape = __esm({
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
// src/scene/link-item.ts
|
|
60
|
+
function buildLinkCardSvg(width, _height, link) {
|
|
61
|
+
const cardWidth = Math.max(1, width);
|
|
62
|
+
const scale = cardWidth / DEFAULT_LINK_CARD_WIDTH;
|
|
63
|
+
const contentWidth = DEFAULT_LINK_CARD_WIDTH;
|
|
64
|
+
const contentHeight = DEFAULT_LINK_CARD_HEIGHT;
|
|
65
|
+
const padding = 14;
|
|
66
|
+
const badgeSize = 42;
|
|
67
|
+
const gap = 13;
|
|
68
|
+
const buttonSize = 34;
|
|
69
|
+
const textX = padding + badgeSize + gap;
|
|
70
|
+
const textWidth = Math.max(1, contentWidth - textX - buttonSize - gap - padding);
|
|
71
|
+
const hostname = getLinkHostname(link.href);
|
|
72
|
+
const title = link.title?.trim() || hostname || "Link";
|
|
73
|
+
const protocol = getLinkProtocol(link.href);
|
|
74
|
+
const subtitle = hostname || link.href;
|
|
75
|
+
const favicon = link.favicon?.trim() || buildGoogleFaviconUrl(hostname);
|
|
76
|
+
const idSuffix = getStableLinkIdSuffix(`${hostname}:${link.href}`);
|
|
77
|
+
const clipId = `canvu-link-favicon-${idSuffix}`;
|
|
78
|
+
const gradientId = `canvu-link-favicon-gradient-${idSuffix}`;
|
|
79
|
+
const buttonX = contentWidth - padding - buttonSize;
|
|
80
|
+
const buttonY = (contentHeight - buttonSize) / 2;
|
|
81
|
+
const isSecure = protocol === "https:";
|
|
82
|
+
const subtitleX = isSecure ? textX + 13 : textX;
|
|
83
|
+
const subtitleWidth = isSecure ? textWidth - 13 : textWidth;
|
|
84
|
+
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})" />` : "";
|
|
85
|
+
return `
|
|
86
|
+
<style>
|
|
87
|
+
.canvu-link-card-root .canvu-link-card { transition: transform .18s ease, filter .18s ease, stroke .18s ease; }
|
|
88
|
+
.canvu-link-card-root .canvu-link-open { opacity: 0; transform: translateX(-4px); transition: opacity .18s ease, transform .18s ease; }
|
|
89
|
+
.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}; }
|
|
90
|
+
.canvu-link-card-root:hover .canvu-link-open { opacity: 1; transform: translateX(0); }
|
|
91
|
+
</style>
|
|
92
|
+
<g class="canvu-link-card-root" transform="scale(${formatNumber(scale)})">
|
|
93
|
+
<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))" />
|
|
94
|
+
<defs>
|
|
95
|
+
<linearGradient id="${gradientId}" x1="8" y1="48" x2="48" y2="8" gradientUnits="userSpaceOnUse">
|
|
96
|
+
<stop stop-color="${LINK_CARD_ACCENT}" />
|
|
97
|
+
<stop offset="1" stop-color="${LINK_CARD_ACCENT_DEEP}" />
|
|
98
|
+
</linearGradient>
|
|
99
|
+
<clipPath id="${clipId}">
|
|
100
|
+
<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="11" />
|
|
101
|
+
</clipPath>
|
|
102
|
+
</defs>
|
|
103
|
+
<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="11" fill="url(#${gradientId})" />
|
|
104
|
+
<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>
|
|
105
|
+
${faviconImage}
|
|
106
|
+
${buildLinkTextBand({ x: textX, y: 16, width: textWidth, height: 19, text: title, fontSize: 14.5, color: LINK_CARD_TITLE_COLOR, fontWeight: 700 })}
|
|
107
|
+
${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>` : ""}
|
|
108
|
+
${buildLinkTextBand({ x: subtitleX, y: 36, width: subtitleWidth, height: 17, text: subtitle, fontSize: 12.5, color: LINK_CARD_TEXT_COLOR })}
|
|
109
|
+
<g class="canvu-link-open" transform="translate(${formatNumber(buttonX)},${formatNumber(buttonY)})">
|
|
110
|
+
<rect width="${formatNumber(buttonSize)}" height="${formatNumber(buttonSize)}" rx="10" fill="#ffffff" stroke="${LINK_CARD_BORDER}" stroke-width="1" />
|
|
111
|
+
<g transform="translate(10,10)" stroke="${LINK_CARD_TEXT_COLOR}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none">
|
|
112
|
+
<path d="M8 2 H14 V8" />
|
|
113
|
+
<path d="M14 2 L7 9" />
|
|
114
|
+
<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" />
|
|
115
|
+
</g>
|
|
116
|
+
</g>
|
|
117
|
+
</g>
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
function getLinkData(item) {
|
|
121
|
+
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
122
|
+
return isCanvuLinkData(entry) ? entry : null;
|
|
123
|
+
}
|
|
124
|
+
function rebuildLinkItemSvg(item) {
|
|
125
|
+
const link = getLinkData(item);
|
|
126
|
+
if (!link) return item;
|
|
127
|
+
const scale = clamp(
|
|
128
|
+
item.bounds.width / DEFAULT_LINK_CARD_WIDTH,
|
|
129
|
+
LINK_CARD_MIN_SCALE,
|
|
130
|
+
LINK_CARD_MAX_SCALE
|
|
131
|
+
);
|
|
132
|
+
const width = DEFAULT_LINK_CARD_WIDTH * scale;
|
|
133
|
+
const height = DEFAULT_LINK_CARD_HEIGHT * scale;
|
|
134
|
+
const bounds = {
|
|
135
|
+
...item.bounds,
|
|
136
|
+
width,
|
|
137
|
+
height
|
|
138
|
+
};
|
|
139
|
+
const customInnerSvg = buildLinkCardSvg(
|
|
140
|
+
DEFAULT_LINK_CARD_WIDTH,
|
|
141
|
+
DEFAULT_LINK_CARD_HEIGHT,
|
|
142
|
+
link
|
|
143
|
+
);
|
|
144
|
+
return {
|
|
145
|
+
...item,
|
|
146
|
+
x: bounds.x,
|
|
147
|
+
y: bounds.y,
|
|
148
|
+
bounds,
|
|
149
|
+
customIntrinsicSize: {
|
|
150
|
+
width: DEFAULT_LINK_CARD_WIDTH,
|
|
151
|
+
height: DEFAULT_LINK_CARD_HEIGHT
|
|
152
|
+
},
|
|
153
|
+
customInnerSvg,
|
|
154
|
+
childrenSvg: buildLinkCardSvg(width, height, link)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
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;
|
|
158
|
+
var init_link_item = __esm({
|
|
159
|
+
"src/scene/link-item.ts"() {
|
|
160
|
+
LINK_PLUGIN_KEY = "canvuLink";
|
|
161
|
+
DEFAULT_LINK_CARD_WIDTH = 320;
|
|
162
|
+
DEFAULT_LINK_CARD_HEIGHT = 70;
|
|
163
|
+
LINK_CARD_MIN_SCALE = 0.6;
|
|
164
|
+
LINK_CARD_MAX_SCALE = 2.5;
|
|
165
|
+
LINK_CARD_ASPECT = DEFAULT_LINK_CARD_WIDTH / DEFAULT_LINK_CARD_HEIGHT;
|
|
166
|
+
LINK_CARD_BORDER = "oklch(0.918 0.008 255)";
|
|
167
|
+
LINK_CARD_BORDER_STRONG = "oklch(0.86 0.012 255)";
|
|
168
|
+
LINK_CARD_ACCENT = "oklch(0.55 0.19 264)";
|
|
169
|
+
LINK_CARD_ACCENT_DEEP = "oklch(0.46 0.18 264)";
|
|
170
|
+
LINK_CARD_TITLE_COLOR = "oklch(0.26 0.022 265)";
|
|
171
|
+
LINK_CARD_TEXT_COLOR = "oklch(0.55 0.022 265)";
|
|
172
|
+
clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
173
|
+
formatNumber = (value) => {
|
|
174
|
+
const rounded = Math.round(value * 100) / 100;
|
|
175
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
176
|
+
};
|
|
177
|
+
escapeXmlAttribute = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
178
|
+
escapeHtmlText = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
179
|
+
getLinkHostname = (href) => {
|
|
180
|
+
try {
|
|
181
|
+
return new URL(href).hostname.replace(/^www\./, "");
|
|
182
|
+
} catch {
|
|
183
|
+
return href;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
buildLinkTextBand = (band) => {
|
|
187
|
+
const lineHeight = band.height;
|
|
188
|
+
const weight = band.fontWeight != null ? `font-weight:${band.fontWeight};` : "";
|
|
189
|
+
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>`;
|
|
190
|
+
};
|
|
191
|
+
getLinkProtocol = (href) => {
|
|
192
|
+
try {
|
|
193
|
+
return new URL(href).protocol;
|
|
194
|
+
} catch {
|
|
195
|
+
return "";
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
getLinkInitial = (hostname) => {
|
|
199
|
+
const first = hostname.trim().charAt(0).toUpperCase();
|
|
200
|
+
return first || "L";
|
|
201
|
+
};
|
|
202
|
+
buildGoogleFaviconUrl = (hostname) => hostname ? `https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=64` : null;
|
|
203
|
+
getStableLinkIdSuffix = (value) => {
|
|
204
|
+
let hash = 0;
|
|
205
|
+
for (const char of value) {
|
|
206
|
+
hash = hash * 31 + char.charCodeAt(0) >>> 0;
|
|
207
|
+
}
|
|
208
|
+
return hash.toString(36);
|
|
209
|
+
};
|
|
210
|
+
isCanvuLinkData = (value) => {
|
|
211
|
+
if (!value || typeof value !== "object") return false;
|
|
212
|
+
const candidate = value;
|
|
213
|
+
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
59
218
|
// src/scene/text-svg.ts
|
|
60
219
|
function escapeSvgTextContent(s) {
|
|
61
220
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
62
221
|
}
|
|
63
|
-
function
|
|
222
|
+
function escapeHtmlText2(s) {
|
|
64
223
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
65
224
|
}
|
|
66
225
|
function getSharedMeasureContext() {
|
|
@@ -161,9 +320,9 @@ function buildTextFixedBoundsSvg(content, width, height, fillColor = "#1d1d1d",
|
|
|
161
320
|
const trimmed = content.trim();
|
|
162
321
|
const padTop = EDIT_TOP_PAD_RATIO * fontSize;
|
|
163
322
|
if (trimmed.length === 0) {
|
|
164
|
-
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">${
|
|
323
|
+
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>`;
|
|
165
324
|
}
|
|
166
|
-
const body =
|
|
325
|
+
const body = escapeHtmlText2(content);
|
|
167
326
|
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>`;
|
|
168
327
|
}
|
|
169
328
|
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;
|
|
@@ -820,6 +979,9 @@ function rebuildItemSvg(item) {
|
|
|
820
979
|
)
|
|
821
980
|
};
|
|
822
981
|
}
|
|
982
|
+
if (getLinkData(item)) {
|
|
983
|
+
return rebuildLinkItemSvg(item);
|
|
984
|
+
}
|
|
823
985
|
if (k === "custom" && item.customIntrinsicSize && item.customInnerSvg) {
|
|
824
986
|
const b = normalizeRect(item.bounds);
|
|
825
987
|
return {
|
|
@@ -1045,6 +1207,7 @@ var init_shape_builders = __esm({
|
|
|
1045
1207
|
"src/scene/shape-builders.ts"() {
|
|
1046
1208
|
init_rect();
|
|
1047
1209
|
init_custom_shape();
|
|
1210
|
+
init_link_item();
|
|
1048
1211
|
init_text_svg();
|
|
1049
1212
|
DEFAULT_STROKE_STYLE = {
|
|
1050
1213
|
stroke: "#1d1d1d",
|
|
@@ -2205,7 +2368,7 @@ var defaultLabels = {
|
|
|
2205
2368
|
title: "Images",
|
|
2206
2369
|
dragHandle: "Drag to reorder",
|
|
2207
2370
|
focus: "Focus on canvas",
|
|
2208
|
-
|
|
2371
|
+
duplicate: "Duplicate",
|
|
2209
2372
|
rotate: "Rotate",
|
|
2210
2373
|
delete: "Delete",
|
|
2211
2374
|
collapse: "Collapse images menu",
|
|
@@ -2231,7 +2394,11 @@ function ImagesMenu({
|
|
|
2231
2394
|
if (managed.length === 0) {
|
|
2232
2395
|
return null;
|
|
2233
2396
|
}
|
|
2234
|
-
const resolvedLabels = {
|
|
2397
|
+
const resolvedLabels = {
|
|
2398
|
+
...defaultLabels,
|
|
2399
|
+
...labels,
|
|
2400
|
+
duplicate: labels?.duplicate ?? labels?.copy ?? defaultLabels.duplicate
|
|
2401
|
+
};
|
|
2235
2402
|
if (collapsed) {
|
|
2236
2403
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2237
2404
|
"button",
|
|
@@ -2345,7 +2512,7 @@ function ImagesMenuRow({
|
|
|
2345
2512
|
),
|
|
2346
2513
|
/* @__PURE__ */ jsxRuntime.jsx(FocusTarget, { label: labels.focus, onFocus, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: thumbBoxStyle, children: src ? /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: "", style: thumbImgStyle, draggable: false }) : null }) }),
|
|
2347
2514
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: actionsColumnStyle, children: [
|
|
2348
|
-
/* @__PURE__ */ jsxRuntime.jsx(ImagesMenuAction, { label: labels.
|
|
2515
|
+
/* @__PURE__ */ jsxRuntime.jsx(ImagesMenuAction, { label: labels.duplicate, onClick: onCopy, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CopyPlus, { size: 18 }) }),
|
|
2349
2516
|
/* @__PURE__ */ jsxRuntime.jsx(ImagesMenuAction, { label: labels.rotate, onClick: onRotate, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCw, { size: 18 }) }),
|
|
2350
2517
|
/* @__PURE__ */ jsxRuntime.jsx(ImagesMenuAction, { label: labels.delete, onClick: onDelete, danger: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 18 }) })
|
|
2351
2518
|
] })
|
|
@@ -6358,10 +6525,80 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
|
|
|
6358
6525
|
|
|
6359
6526
|
// src/interaction/mutations.ts
|
|
6360
6527
|
init_rect();
|
|
6528
|
+
init_link_item();
|
|
6361
6529
|
init_shape_builders();
|
|
6362
6530
|
init_text_svg();
|
|
6531
|
+
var LINK_CORNER_HANDLES = /* @__PURE__ */ new Set(["nw", "ne", "se", "sw"]);
|
|
6532
|
+
var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
6533
|
+
var clampLinkResizeBounds = (startBounds, nextBounds, handle, intrinsicWidth) => {
|
|
6534
|
+
const next = normalizeRect(nextBounds);
|
|
6535
|
+
const scale = clamp2(
|
|
6536
|
+
next.width / Math.max(1e-9, intrinsicWidth),
|
|
6537
|
+
LINK_CARD_MIN_SCALE,
|
|
6538
|
+
LINK_CARD_MAX_SCALE
|
|
6539
|
+
);
|
|
6540
|
+
const width = intrinsicWidth * scale;
|
|
6541
|
+
const height = width / LINK_CARD_ASPECT;
|
|
6542
|
+
const start = normalizeRect(startBounds);
|
|
6543
|
+
const x0 = start.x;
|
|
6544
|
+
const y0 = start.y;
|
|
6545
|
+
const x1 = start.x + start.width;
|
|
6546
|
+
const y1 = start.y + start.height;
|
|
6547
|
+
switch (handle) {
|
|
6548
|
+
case "nw":
|
|
6549
|
+
return { x: x1 - width, y: y1 - height, width, height };
|
|
6550
|
+
case "ne":
|
|
6551
|
+
return { x: x0, y: y1 - height, width, height };
|
|
6552
|
+
case "sw":
|
|
6553
|
+
return { x: x1 - width, y: y0, width, height };
|
|
6554
|
+
default:
|
|
6555
|
+
return { x: x0, y: y0, width, height };
|
|
6556
|
+
}
|
|
6557
|
+
};
|
|
6363
6558
|
function computeNewBoundsForResize(item, sb, handle, currentWorld) {
|
|
6364
6559
|
const rot = getItemRotationRad(item);
|
|
6560
|
+
const link = getLinkData(item);
|
|
6561
|
+
if (link && item.customIntrinsicSize) {
|
|
6562
|
+
if (!LINK_CORNER_HANDLES.has(handle)) return sb;
|
|
6563
|
+
const intrinsicWidth = Math.max(1e-9, item.customIntrinsicSize.width);
|
|
6564
|
+
if (Math.abs(rot) < 1e-12) {
|
|
6565
|
+
const next = computeResizeBoundsFixedAspect(
|
|
6566
|
+
sb,
|
|
6567
|
+
handle,
|
|
6568
|
+
currentWorld,
|
|
6569
|
+
LINK_CARD_ASPECT
|
|
6570
|
+
);
|
|
6571
|
+
return clampLinkResizeBounds(sb, next, handle, intrinsicWidth);
|
|
6572
|
+
}
|
|
6573
|
+
const local2 = worldToItemLocal(
|
|
6574
|
+
currentWorld.x,
|
|
6575
|
+
currentWorld.y,
|
|
6576
|
+
sb.x,
|
|
6577
|
+
sb.y,
|
|
6578
|
+
sb.width,
|
|
6579
|
+
sb.height,
|
|
6580
|
+
rot
|
|
6581
|
+
);
|
|
6582
|
+
const localBounds2 = { x: 0, y: 0, width: sb.width, height: sb.height };
|
|
6583
|
+
const nextLocal = computeResizeBoundsFixedAspect(
|
|
6584
|
+
localBounds2,
|
|
6585
|
+
handle,
|
|
6586
|
+
local2,
|
|
6587
|
+
LINK_CARD_ASPECT
|
|
6588
|
+
);
|
|
6589
|
+
const clamped = clampLinkResizeBounds(
|
|
6590
|
+
localBounds2,
|
|
6591
|
+
nextLocal,
|
|
6592
|
+
handle,
|
|
6593
|
+
intrinsicWidth
|
|
6594
|
+
);
|
|
6595
|
+
return {
|
|
6596
|
+
x: sb.x + clamped.x,
|
|
6597
|
+
y: sb.y + clamped.y,
|
|
6598
|
+
width: clamped.width,
|
|
6599
|
+
height: clamped.height
|
|
6600
|
+
};
|
|
6601
|
+
}
|
|
6365
6602
|
if (Math.abs(rot) < 1e-12) {
|
|
6366
6603
|
if (item.toolKind === "image") {
|
|
6367
6604
|
let aspect;
|
|
@@ -6834,17 +7071,8 @@ var SvgVectorRenderer = class {
|
|
|
6834
7071
|
}
|
|
6835
7072
|
};
|
|
6836
7073
|
|
|
6837
|
-
// src/
|
|
6838
|
-
|
|
6839
|
-
var isCanvuLinkData = (value) => {
|
|
6840
|
-
if (!value || typeof value !== "object") return false;
|
|
6841
|
-
const candidate = value;
|
|
6842
|
-
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
6843
|
-
};
|
|
6844
|
-
function getLinkData(item) {
|
|
6845
|
-
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
6846
|
-
return isCanvuLinkData(entry) ? entry : null;
|
|
6847
|
-
}
|
|
7074
|
+
// src/react/VectorViewport.tsx
|
|
7075
|
+
init_link_item();
|
|
6848
7076
|
|
|
6849
7077
|
// src/scene/scene.ts
|
|
6850
7078
|
var VectorScene = class {
|
|
@@ -6938,8 +7166,10 @@ function smoothFreehandPointsToPathD(points) {
|
|
|
6938
7166
|
}
|
|
6939
7167
|
|
|
6940
7168
|
// src/react/InteractionOverlay.tsx
|
|
7169
|
+
init_link_item();
|
|
6941
7170
|
init_shape_builders();
|
|
6942
7171
|
var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
7172
|
+
var LINK_HANDLE_ORDER = ["nw", "ne", "se", "sw"];
|
|
6943
7173
|
var ERASER_TINT = "#cbd5e1";
|
|
6944
7174
|
var ERASER_TINT_OPACITY = 0.95;
|
|
6945
7175
|
var ERASER_PREVIEW_OPACITY = 0.3;
|
|
@@ -6992,7 +7222,7 @@ function InteractionOverlay({
|
|
|
6992
7222
|
) }, it.id);
|
|
6993
7223
|
}),
|
|
6994
7224
|
showResizeHandles && bSingle && single && rotHandlePos && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6995
|
-
HANDLE_ORDER.map((hid) => {
|
|
7225
|
+
(getLinkData(single) ? LINK_HANDLE_ORDER : HANDLE_ORDER).map((hid) => {
|
|
6996
7226
|
const p = getHandleWorldPositionRotated(bSingle, hid, rotSingle);
|
|
6997
7227
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6998
7228
|
"circle",
|
|
@@ -7806,6 +8036,7 @@ function defaultPlacementWorld(tool, center) {
|
|
|
7806
8036
|
lineWorld: [a, b]
|
|
7807
8037
|
};
|
|
7808
8038
|
}
|
|
8039
|
+
var LINK_CORNER_HANDLES2 = /* @__PURE__ */ new Set(["nw", "ne", "se", "sw"]);
|
|
7809
8040
|
function pointInSelectedItemBounds(item, worldX, worldY) {
|
|
7810
8041
|
const bounds = normalizeRect(item.bounds);
|
|
7811
8042
|
const local = worldToItemLocal(
|
|
@@ -8283,7 +8514,11 @@ var VectorViewport = react.forwardRef(
|
|
|
8283
8514
|
setEffectiveSelectedIdsRef.current = setEffectiveSelectedIds;
|
|
8284
8515
|
toolIdRef.current = toolId;
|
|
8285
8516
|
interactiveRef.current = interactive;
|
|
8286
|
-
|
|
8517
|
+
const normalizedItems = react.useMemo(
|
|
8518
|
+
() => items.map((item) => getLinkData(item) ? rebuildLinkItemSvg(item) : item),
|
|
8519
|
+
[items]
|
|
8520
|
+
);
|
|
8521
|
+
itemsRef.current = normalizedItems;
|
|
8287
8522
|
onWorldPointerDownRef.current = onWorldPointerDown;
|
|
8288
8523
|
const originalOnItemsChangeRef = react.useRef(onItemsChange);
|
|
8289
8524
|
originalOnItemsChangeRef.current = onItemsChange;
|
|
@@ -8304,7 +8539,10 @@ var VectorViewport = react.forwardRef(
|
|
|
8304
8539
|
autoResetToolToRef.current = autoResetToolTo;
|
|
8305
8540
|
const onToolChangeRequestRef = react.useRef(onToolChangeRequest);
|
|
8306
8541
|
onToolChangeRequestRef.current = onToolChangeRequest;
|
|
8307
|
-
const resolvedItems = react.useMemo(
|
|
8542
|
+
const resolvedItems = react.useMemo(
|
|
8543
|
+
() => resolveArrowBindingsInScene(normalizedItems),
|
|
8544
|
+
[normalizedItems]
|
|
8545
|
+
);
|
|
8308
8546
|
const resolvedItemsRef = react.useRef(resolvedItems);
|
|
8309
8547
|
resolvedItemsRef.current = resolvedItems;
|
|
8310
8548
|
const liveId = react.useId();
|
|
@@ -9725,7 +9963,8 @@ var VectorViewport = react.forwardRef(
|
|
|
9725
9963
|
handleRadiusWorld,
|
|
9726
9964
|
rot
|
|
9727
9965
|
);
|
|
9728
|
-
|
|
9966
|
+
const isLinkResizeHandle = hb && getLinkData(selected) ? LINK_CORNER_HANDLES2.has(hb) : Boolean(hb);
|
|
9967
|
+
if (hb && isLinkResizeHandle) {
|
|
9729
9968
|
const snapRs = bakedSnapshot(selected.id);
|
|
9730
9969
|
if (!snapRs) return;
|
|
9731
9970
|
dragStateRef.current = {
|