canvu-react 0.4.31 → 0.4.33
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 +173 -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 +173 -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 +220 -3
- package/dist/native.cjs.map +1 -1
- package/dist/native.js +220 -3
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +244 -18
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -6
- package/dist/react.d.ts +6 -6
- package/dist/react.js +244 -18
- 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 +121 -39
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.js +121 -39
- 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/index.d.cts
CHANGED
|
@@ -2,8 +2,8 @@ import { C as Camera2D } from './shape-builders-CKEMjivV.cjs';
|
|
|
2
2
|
export { a as Camera2DOptions, D as DEFAULT_STROKE_STYLE, F as FreehandSvgPayload, S as StrokeStyle, b as applyStrokeToItem, c as buildArchitecturalCloudPathD, d as buildArchitecturalCloudSvg, e as buildArrowSvg, f as buildDrawDotSvg, g as buildEllipseSvg, h as buildFreehandPathSvg, i as buildLineSvg, j as buildRectSvg, k as computeFreehandSvgPayload, l as createArchitecturalCloudItem, m as createDrawDotItem, n as createEllipseItem, o as createFreehandStrokeItem, p as createImageFromVectorTrace, q as createImageItem, r as createLineItem, s as createRectangleItem, t as createShapeId, u as createTextItem, v as lineEndpointsToLocal, w as rebuildItemSvg, x as resolveStrokeStyle } from './shape-builders-CKEMjivV.cjs';
|
|
3
3
|
import { V as VectorSceneItem, A as ArrowEndpointBinding, R as Rect } from './types-BCCvY6ie.cjs';
|
|
4
4
|
export { a as ArrowBindings, b as VectorPathPoint, n as normalizeRect, r as rectsIntersect } from './types-BCCvY6ie.cjs';
|
|
5
|
-
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-
|
|
6
|
-
export { C as CanvuLinkData, D as DEFAULT_LINK_CARD_SIZE, L as LINK_PLUGIN_KEY, b as buildLinkCardSvg, c as createLinkItem, g as getLinkData, i as isLinkItem } from './link-item-
|
|
5
|
+
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-BFGZ3igr.cjs';
|
|
6
|
+
export { C as CanvuLinkData, D as DEFAULT_LINK_CARD_SIZE, L as LINK_PLUGIN_KEY, b as buildLinkCardSvg, c as createLinkItem, g as getLinkData, i as isLinkItem } from './link-item-Dncuz2d_.cjs';
|
|
7
7
|
|
|
8
8
|
type EncodeCanvasToBlobOptions = {
|
|
9
9
|
mimeType?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { C as Camera2D } from './shape-builders-Cyh8zvDG.js';
|
|
|
2
2
|
export { a as Camera2DOptions, D as DEFAULT_STROKE_STYLE, F as FreehandSvgPayload, S as StrokeStyle, b as applyStrokeToItem, c as buildArchitecturalCloudPathD, d as buildArchitecturalCloudSvg, e as buildArrowSvg, f as buildDrawDotSvg, g as buildEllipseSvg, h as buildFreehandPathSvg, i as buildLineSvg, j as buildRectSvg, k as computeFreehandSvgPayload, l as createArchitecturalCloudItem, m as createDrawDotItem, n as createEllipseItem, o as createFreehandStrokeItem, p as createImageFromVectorTrace, q as createImageItem, r as createLineItem, s as createRectangleItem, t as createShapeId, u as createTextItem, v as lineEndpointsToLocal, w as rebuildItemSvg, x as resolveStrokeStyle } from './shape-builders-Cyh8zvDG.js';
|
|
3
3
|
import { V as VectorSceneItem, A as ArrowEndpointBinding, R as Rect } from './types-BCCvY6ie.js';
|
|
4
4
|
export { a as ArrowBindings, b as VectorPathPoint, n as normalizeRect, r as rectsIntersect } from './types-BCCvY6ie.js';
|
|
5
|
-
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-
|
|
6
|
-
export { C as CanvuLinkData, D as DEFAULT_LINK_CARD_SIZE, L as LINK_PLUGIN_KEY, b as buildLinkCardSvg, c as createLinkItem, g as getLinkData, i as isLinkItem } from './link-item-
|
|
5
|
+
export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-D6Q3TJL1.js';
|
|
6
|
+
export { C as CanvuLinkData, D as DEFAULT_LINK_CARD_SIZE, L as LINK_PLUGIN_KEY, b as buildLinkCardSvg, c as createLinkItem, g as getLinkData, i as isLinkItem } from './link-item-voRU0Up9.js';
|
|
7
7
|
|
|
8
8
|
type EncodeCanvasToBlobOptions = {
|
|
9
9
|
mimeType?: string;
|
package/dist/index.js
CHANGED
|
@@ -983,11 +983,178 @@ function createCustomShapeItem(id, bounds, content) {
|
|
|
983
983
|
};
|
|
984
984
|
}
|
|
985
985
|
|
|
986
|
+
// src/scene/link-item.ts
|
|
987
|
+
var LINK_PLUGIN_KEY = "canvuLink";
|
|
988
|
+
var DEFAULT_LINK_CARD_WIDTH = 320;
|
|
989
|
+
var DEFAULT_LINK_CARD_HEIGHT = 70;
|
|
990
|
+
var LINK_CARD_MIN_SCALE = 0.6;
|
|
991
|
+
var LINK_CARD_MAX_SCALE = 6;
|
|
992
|
+
var LINK_CARD_BORDER = "oklch(0.918 0.008 255)";
|
|
993
|
+
var LINK_CARD_BORDER_STRONG = "oklch(0.86 0.012 255)";
|
|
994
|
+
var LINK_CARD_ACCENT = "oklch(0.55 0.19 264)";
|
|
995
|
+
var LINK_CARD_ACCENT_DEEP = "oklch(0.46 0.18 264)";
|
|
996
|
+
var LINK_CARD_TITLE_COLOR = "oklch(0.26 0.022 265)";
|
|
997
|
+
var LINK_CARD_TEXT_COLOR = "oklch(0.55 0.022 265)";
|
|
998
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
999
|
+
var formatNumber = (value) => {
|
|
1000
|
+
const rounded = Math.round(value * 100) / 100;
|
|
1001
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
1002
|
+
};
|
|
1003
|
+
var escapeHtmlText = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1004
|
+
var getLinkHostname = (href) => {
|
|
1005
|
+
try {
|
|
1006
|
+
return new URL(href).hostname.replace(/^www\./, "");
|
|
1007
|
+
} catch {
|
|
1008
|
+
return href;
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
var buildLinkTextBand = (band) => {
|
|
1012
|
+
const lineHeight = band.height;
|
|
1013
|
+
const weight = band.fontWeight != null ? `font-weight:${band.fontWeight};` : "";
|
|
1014
|
+
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>`;
|
|
1015
|
+
};
|
|
1016
|
+
var getLinkProtocol = (href) => {
|
|
1017
|
+
try {
|
|
1018
|
+
return new URL(href).protocol;
|
|
1019
|
+
} catch {
|
|
1020
|
+
return "";
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
var getLinkInitial = (hostname) => {
|
|
1024
|
+
const first = hostname.trim().charAt(0).toUpperCase();
|
|
1025
|
+
return first || "L";
|
|
1026
|
+
};
|
|
1027
|
+
var getStableLinkIdSuffix = (value) => {
|
|
1028
|
+
let hash = 0;
|
|
1029
|
+
for (const char of value) {
|
|
1030
|
+
hash = hash * 31 + char.charCodeAt(0) >>> 0;
|
|
1031
|
+
}
|
|
1032
|
+
return hash.toString(36);
|
|
1033
|
+
};
|
|
1034
|
+
function buildLinkCardSvg(width, _height, link) {
|
|
1035
|
+
const cardWidth = Math.max(1, width);
|
|
1036
|
+
const scale = cardWidth / DEFAULT_LINK_CARD_WIDTH;
|
|
1037
|
+
const contentWidth = DEFAULT_LINK_CARD_WIDTH;
|
|
1038
|
+
const contentHeight = DEFAULT_LINK_CARD_HEIGHT;
|
|
1039
|
+
const padding = 14;
|
|
1040
|
+
const badgeSize = 42;
|
|
1041
|
+
const gap = 13;
|
|
1042
|
+
const buttonSize = 34;
|
|
1043
|
+
const textX = padding + badgeSize + gap;
|
|
1044
|
+
const textWidth = Math.max(1, contentWidth - textX - buttonSize - gap - padding);
|
|
1045
|
+
const hostname = getLinkHostname(link.href);
|
|
1046
|
+
const title = link.title?.trim() || hostname || "Link";
|
|
1047
|
+
const protocol = getLinkProtocol(link.href);
|
|
1048
|
+
const subtitle = hostname || link.href;
|
|
1049
|
+
const idSuffix = getStableLinkIdSuffix(`${hostname}:${link.href}`);
|
|
1050
|
+
const gradientId = `canvu-link-favicon-gradient-${idSuffix}`;
|
|
1051
|
+
const buttonX = contentWidth - padding - buttonSize;
|
|
1052
|
+
const buttonY = (contentHeight - buttonSize) / 2;
|
|
1053
|
+
const isSecure = protocol === "https:";
|
|
1054
|
+
const subtitleX = isSecure ? textX + 13 : textX;
|
|
1055
|
+
const subtitleWidth = isSecure ? textWidth - 13 : textWidth;
|
|
1056
|
+
return `
|
|
1057
|
+
<style>
|
|
1058
|
+
.canvu-link-card-root .canvu-link-card { transition: transform .18s ease, filter .18s ease, stroke .18s ease; }
|
|
1059
|
+
.canvu-link-card-root .canvu-link-open { opacity: 0; transform: translateX(-4px); transition: opacity .18s ease, transform .18s ease; }
|
|
1060
|
+
.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}; }
|
|
1061
|
+
.canvu-link-card-root:hover .canvu-link-open { opacity: 1; transform: translateX(0); }
|
|
1062
|
+
</style>
|
|
1063
|
+
<g class="canvu-link-card-root" transform="scale(${formatNumber(scale)})">
|
|
1064
|
+
<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))" />
|
|
1065
|
+
<defs>
|
|
1066
|
+
<linearGradient id="${gradientId}" x1="8" y1="48" x2="48" y2="8" gradientUnits="userSpaceOnUse">
|
|
1067
|
+
<stop stop-color="${LINK_CARD_ACCENT}" />
|
|
1068
|
+
<stop offset="1" stop-color="${LINK_CARD_ACCENT_DEEP}" />
|
|
1069
|
+
</linearGradient>
|
|
1070
|
+
</defs>
|
|
1071
|
+
<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="11" fill="url(#${gradientId})" />
|
|
1072
|
+
<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>
|
|
1073
|
+
${buildLinkTextBand({ x: textX, y: 16, width: textWidth, height: 19, text: title, fontSize: 14.5, color: LINK_CARD_TITLE_COLOR, fontWeight: 700 })}
|
|
1074
|
+
${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>` : ""}
|
|
1075
|
+
${buildLinkTextBand({ x: subtitleX, y: 36, width: subtitleWidth, height: 17, text: subtitle, fontSize: 12.5, color: LINK_CARD_TEXT_COLOR })}
|
|
1076
|
+
<g class="canvu-link-open" transform="translate(${formatNumber(buttonX)},${formatNumber(buttonY)})">
|
|
1077
|
+
<rect width="${formatNumber(buttonSize)}" height="${formatNumber(buttonSize)}" rx="10" fill="#ffffff" stroke="${LINK_CARD_BORDER}" stroke-width="1" />
|
|
1078
|
+
<g transform="translate(10,10)" stroke="${LINK_CARD_TEXT_COLOR}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none">
|
|
1079
|
+
<path d="M8 2 H14 V8" />
|
|
1080
|
+
<path d="M14 2 L7 9" />
|
|
1081
|
+
<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" />
|
|
1082
|
+
</g>
|
|
1083
|
+
</g>
|
|
1084
|
+
</g>
|
|
1085
|
+
`;
|
|
1086
|
+
}
|
|
1087
|
+
var isCanvuLinkData = (value) => {
|
|
1088
|
+
if (!value || typeof value !== "object") return false;
|
|
1089
|
+
const candidate = value;
|
|
1090
|
+
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
1091
|
+
};
|
|
1092
|
+
function getLinkData(item) {
|
|
1093
|
+
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
1094
|
+
return isCanvuLinkData(entry) ? entry : null;
|
|
1095
|
+
}
|
|
1096
|
+
function isLinkItem(item) {
|
|
1097
|
+
return getLinkData(item) != null;
|
|
1098
|
+
}
|
|
1099
|
+
function rebuildLinkItemSvg(item) {
|
|
1100
|
+
const link = getLinkData(item);
|
|
1101
|
+
if (!link) return item;
|
|
1102
|
+
const scale = clamp(
|
|
1103
|
+
item.bounds.width / DEFAULT_LINK_CARD_WIDTH,
|
|
1104
|
+
LINK_CARD_MIN_SCALE,
|
|
1105
|
+
LINK_CARD_MAX_SCALE
|
|
1106
|
+
);
|
|
1107
|
+
const width = DEFAULT_LINK_CARD_WIDTH * scale;
|
|
1108
|
+
const height = DEFAULT_LINK_CARD_HEIGHT * scale;
|
|
1109
|
+
const bounds = {
|
|
1110
|
+
...item.bounds,
|
|
1111
|
+
width,
|
|
1112
|
+
height
|
|
1113
|
+
};
|
|
1114
|
+
const customInnerSvg = buildLinkCardSvg(
|
|
1115
|
+
DEFAULT_LINK_CARD_WIDTH,
|
|
1116
|
+
DEFAULT_LINK_CARD_HEIGHT,
|
|
1117
|
+
link
|
|
1118
|
+
);
|
|
1119
|
+
return {
|
|
1120
|
+
...item,
|
|
1121
|
+
x: bounds.x,
|
|
1122
|
+
y: bounds.y,
|
|
1123
|
+
bounds,
|
|
1124
|
+
customIntrinsicSize: {
|
|
1125
|
+
width: DEFAULT_LINK_CARD_WIDTH,
|
|
1126
|
+
height: DEFAULT_LINK_CARD_HEIGHT
|
|
1127
|
+
},
|
|
1128
|
+
customInnerSvg,
|
|
1129
|
+
childrenSvg: buildLinkCardSvg(width, height, link)
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function createLinkItem(id, bounds, link) {
|
|
1133
|
+
const width = bounds.width || DEFAULT_LINK_CARD_WIDTH;
|
|
1134
|
+
const height = bounds.height || DEFAULT_LINK_CARD_HEIGHT;
|
|
1135
|
+
const item = createCustomShapeItem(
|
|
1136
|
+
id,
|
|
1137
|
+
{ ...bounds, width, height },
|
|
1138
|
+
{ render: (size) => buildLinkCardSvg(size.width, size.height, link) }
|
|
1139
|
+
);
|
|
1140
|
+
return rebuildLinkItemSvg({
|
|
1141
|
+
...item,
|
|
1142
|
+
pluginData: {
|
|
1143
|
+
...item.pluginData ?? {},
|
|
1144
|
+
[LINK_PLUGIN_KEY]: link
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
var DEFAULT_LINK_CARD_SIZE = {
|
|
1149
|
+
width: DEFAULT_LINK_CARD_WIDTH,
|
|
1150
|
+
height: DEFAULT_LINK_CARD_HEIGHT
|
|
1151
|
+
};
|
|
1152
|
+
|
|
986
1153
|
// src/scene/text-svg.ts
|
|
987
1154
|
function escapeSvgTextContent(s) {
|
|
988
1155
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
989
1156
|
}
|
|
990
|
-
function
|
|
1157
|
+
function escapeHtmlText2(s) {
|
|
991
1158
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
992
1159
|
}
|
|
993
1160
|
var DEFAULT_TEXT_FONT_SIZE = 18;
|
|
@@ -1095,9 +1262,9 @@ function buildTextFixedBoundsSvg(content, width, height, fillColor = "#1d1d1d",
|
|
|
1095
1262
|
const trimmed = content.trim();
|
|
1096
1263
|
const padTop = EDIT_TOP_PAD_RATIO * fontSize;
|
|
1097
1264
|
if (trimmed.length === 0) {
|
|
1098
|
-
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">${
|
|
1265
|
+
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>`;
|
|
1099
1266
|
}
|
|
1100
|
-
const body =
|
|
1267
|
+
const body = escapeHtmlText2(content);
|
|
1101
1268
|
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>`;
|
|
1102
1269
|
}
|
|
1103
1270
|
|
|
@@ -1695,6 +1862,9 @@ function rebuildItemSvg(item) {
|
|
|
1695
1862
|
)
|
|
1696
1863
|
};
|
|
1697
1864
|
}
|
|
1865
|
+
if (getLinkData(item)) {
|
|
1866
|
+
return rebuildLinkItemSvg(item);
|
|
1867
|
+
}
|
|
1698
1868
|
if (k === "custom" && item.customIntrinsicSize && item.customInnerSvg) {
|
|
1699
1869
|
const b = normalizeRect(item.bounds);
|
|
1700
1870
|
return {
|
|
@@ -2798,105 +2968,6 @@ function cloneVectorSceneItemsWithNewIds(items) {
|
|
|
2798
2968
|
return items.map(cloneVectorSceneItemWithNewId);
|
|
2799
2969
|
}
|
|
2800
2970
|
|
|
2801
|
-
// src/scene/link-item.ts
|
|
2802
|
-
var LINK_PLUGIN_KEY = "canvuLink";
|
|
2803
|
-
var DEFAULT_LINK_CARD_WIDTH = 360;
|
|
2804
|
-
var DEFAULT_LINK_CARD_HEIGHT = 132;
|
|
2805
|
-
var LINK_CARD_BORDER = "#e2e8f0";
|
|
2806
|
-
var LINK_CARD_ACCENT = "#2563eb";
|
|
2807
|
-
var LINK_CARD_TITLE_COLOR = "#0f172a";
|
|
2808
|
-
var LINK_CARD_TEXT_COLOR = "#475569";
|
|
2809
|
-
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
2810
|
-
var formatNumber = (value) => {
|
|
2811
|
-
const rounded = Math.round(value * 100) / 100;
|
|
2812
|
-
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
2813
|
-
};
|
|
2814
|
-
var escapeXmlAttribute = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2815
|
-
var escapeHtmlText2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
2816
|
-
var getLinkHostname = (href) => {
|
|
2817
|
-
try {
|
|
2818
|
-
return new URL(href).hostname.replace(/^www\./, "");
|
|
2819
|
-
} catch {
|
|
2820
|
-
return href;
|
|
2821
|
-
}
|
|
2822
|
-
};
|
|
2823
|
-
var buildLinkTextBand = (band) => {
|
|
2824
|
-
const lineHeight = band.fontSize * 1.3;
|
|
2825
|
-
const weight = band.fontWeight != null ? `font-weight:${band.fontWeight};` : "";
|
|
2826
|
-
const lineClampStyle = band.clampLines ? `display:-webkit-box;-webkit-line-clamp:${band.clampLines};-webkit-box-orient:vertical;` : "";
|
|
2827
|
-
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>`;
|
|
2828
|
-
};
|
|
2829
|
-
function buildLinkCardSvg(width, height, link) {
|
|
2830
|
-
const cardWidth = Math.max(1, width);
|
|
2831
|
-
const cardHeight = Math.max(1, height);
|
|
2832
|
-
const padding = 14;
|
|
2833
|
-
const badgeSize = clamp(Math.min(72, cardHeight - padding * 2), 28, 96);
|
|
2834
|
-
const textX = padding + badgeSize + 14;
|
|
2835
|
-
const textWidth = Math.max(1, cardWidth - textX - padding);
|
|
2836
|
-
const hostname = getLinkHostname(link.href);
|
|
2837
|
-
const title = link.title?.trim() || hostname || "Link";
|
|
2838
|
-
const description = link.description?.trim() || link.href;
|
|
2839
|
-
const titleY = padding;
|
|
2840
|
-
const titleHeight = clamp(cardHeight * 0.22, 18, 28);
|
|
2841
|
-
const hostY = titleY + titleHeight + 2;
|
|
2842
|
-
const hostHeight = 16;
|
|
2843
|
-
const descY = hostY + hostHeight + 4;
|
|
2844
|
-
const descHeight = Math.max(1, cardHeight - descY - padding);
|
|
2845
|
-
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>
|
|
2846
|
-
<rect x="${formatNumber(padding)}" y="${formatNumber(padding)}" width="${formatNumber(badgeSize)}" height="${formatNumber(badgeSize)}" rx="12" fill="#f8fafc" stroke="${LINK_CARD_BORDER}" stroke-width="1" />
|
|
2847
|
-
<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" />
|
|
2848
|
-
<g transform="translate(${formatNumber(padding + badgeSize / 2)},${formatNumber(padding + badgeSize / 2)})" stroke="${LINK_CARD_ACCENT}" stroke-width="2.4" stroke-linecap="round" fill="none">
|
|
2849
|
-
<path d="M-9 3 a6 6 0 0 1 0 -8 l4 -4 a6 6 0 0 1 8 8 l-2 2" />
|
|
2850
|
-
<path d="M9 -3 a6 6 0 0 1 0 8 l-4 4 a6 6 0 0 1 -8 -8 l2 -2" />
|
|
2851
|
-
</g>`;
|
|
2852
|
-
const externalIconX = cardWidth - padding - 16;
|
|
2853
|
-
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">
|
|
2854
|
-
<path d="M6 1 H11 V6" />
|
|
2855
|
-
<path d="M11 1 L4.5 7.5" />
|
|
2856
|
-
<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" />
|
|
2857
|
-
</g>`;
|
|
2858
|
-
return `
|
|
2859
|
-
<rect width="${formatNumber(cardWidth)}" height="${formatNumber(cardHeight)}" rx="16" fill="#ffffff" stroke="${LINK_CARD_BORDER}" stroke-width="1.5" />
|
|
2860
|
-
${badge}
|
|
2861
|
-
${externalIcon}
|
|
2862
|
-
${buildLinkTextBand({ x: textX, y: titleY, width: textWidth - 18, height: titleHeight, text: title, fontSize: 15, color: LINK_CARD_TITLE_COLOR, fontWeight: 700, clampLines: 1 })}
|
|
2863
|
-
${buildLinkTextBand({ x: textX, y: hostY, width: textWidth, height: hostHeight, text: hostname, fontSize: 12, color: LINK_CARD_ACCENT, clampLines: 1 })}
|
|
2864
|
-
${buildLinkTextBand({ x: textX, y: descY, width: textWidth, height: descHeight, text: description, fontSize: 12, color: LINK_CARD_TEXT_COLOR, clampLines: 2 })}
|
|
2865
|
-
`;
|
|
2866
|
-
}
|
|
2867
|
-
var isCanvuLinkData = (value) => {
|
|
2868
|
-
if (!value || typeof value !== "object") return false;
|
|
2869
|
-
const candidate = value;
|
|
2870
|
-
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
2871
|
-
};
|
|
2872
|
-
function getLinkData(item) {
|
|
2873
|
-
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
2874
|
-
return isCanvuLinkData(entry) ? entry : null;
|
|
2875
|
-
}
|
|
2876
|
-
function isLinkItem(item) {
|
|
2877
|
-
return getLinkData(item) != null;
|
|
2878
|
-
}
|
|
2879
|
-
function createLinkItem(id, bounds, link) {
|
|
2880
|
-
const width = bounds.width || DEFAULT_LINK_CARD_WIDTH;
|
|
2881
|
-
const height = bounds.height || DEFAULT_LINK_CARD_HEIGHT;
|
|
2882
|
-
const item = createCustomShapeItem(
|
|
2883
|
-
id,
|
|
2884
|
-
{ ...bounds, width, height },
|
|
2885
|
-
{ render: (size) => buildLinkCardSvg(size.width, size.height, link) }
|
|
2886
|
-
);
|
|
2887
|
-
return {
|
|
2888
|
-
...item,
|
|
2889
|
-
pluginData: {
|
|
2890
|
-
...item.pluginData ?? {},
|
|
2891
|
-
[LINK_PLUGIN_KEY]: link
|
|
2892
|
-
}
|
|
2893
|
-
};
|
|
2894
|
-
}
|
|
2895
|
-
var DEFAULT_LINK_CARD_SIZE = {
|
|
2896
|
-
width: DEFAULT_LINK_CARD_WIDTH,
|
|
2897
|
-
height: DEFAULT_LINK_CARD_HEIGHT
|
|
2898
|
-
};
|
|
2899
|
-
|
|
2900
2971
|
// src/scene/managed-images.ts
|
|
2901
2972
|
var MANAGED_KEY = "managed";
|
|
2902
2973
|
var STACK_GAP_WORLD = 16;
|