@bitovi/vybit 0.4.10 → 0.5.0
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/overlay/dist/overlay.js +1338 -354
- package/package.json +2 -1
- package/panel/dist/assets/DesignMode-BAyjCFC8.js +510 -0
- package/panel/dist/assets/index-DVWn5Mcj.js +49 -0
- package/panel/dist/assets/index-_AiRfYt4.css +1 -0
- package/panel/dist/index.html +2 -2
- package/shared/types.ts +10 -2
- package/panel/dist/assets/index-2r5Abawu.js +0 -553
- package/panel/dist/assets/index-CLQ2EbNt.css +0 -1
package/overlay/dist/overlay.js
CHANGED
|
@@ -932,8 +932,8 @@
|
|
|
932
932
|
}
|
|
933
933
|
return getComputedStyle2(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode);
|
|
934
934
|
}
|
|
935
|
-
function getClippingElementAncestors(element,
|
|
936
|
-
const cachedResult =
|
|
935
|
+
function getClippingElementAncestors(element, cache2) {
|
|
936
|
+
const cachedResult = cache2.get(element);
|
|
937
937
|
if (cachedResult) {
|
|
938
938
|
return cachedResult;
|
|
939
939
|
}
|
|
@@ -955,7 +955,7 @@
|
|
|
955
955
|
}
|
|
956
956
|
currentNode = getParentNode(currentNode);
|
|
957
957
|
}
|
|
958
|
-
|
|
958
|
+
cache2.set(element, result);
|
|
959
959
|
return result;
|
|
960
960
|
}
|
|
961
961
|
function getClippingRect(_ref) {
|
|
@@ -1106,14 +1106,14 @@
|
|
|
1106
1106
|
var offset2 = offset;
|
|
1107
1107
|
var flip2 = flip;
|
|
1108
1108
|
var computePosition2 = (reference, floating, options) => {
|
|
1109
|
-
const
|
|
1109
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
1110
1110
|
const mergedOptions = {
|
|
1111
1111
|
platform,
|
|
1112
1112
|
...options
|
|
1113
1113
|
};
|
|
1114
1114
|
const platformWithCache = {
|
|
1115
1115
|
...mergedOptions.platform,
|
|
1116
|
-
_c:
|
|
1116
|
+
_c: cache2
|
|
1117
1117
|
};
|
|
1118
1118
|
return computePosition(reference, floating, {
|
|
1119
1119
|
...mergedOptions,
|
|
@@ -1218,138 +1218,159 @@
|
|
|
1218
1218
|
walk(rootFiber);
|
|
1219
1219
|
return results;
|
|
1220
1220
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
return
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1221
|
+
|
|
1222
|
+
// overlay/src/grouping.ts
|
|
1223
|
+
function cssEscape(cls) {
|
|
1224
|
+
if (typeof CSS !== "undefined" && CSS.escape) return CSS.escape(cls);
|
|
1225
|
+
return cls.replace(/([^\w-])/g, "\\$1");
|
|
1226
|
+
}
|
|
1227
|
+
function buildSelector(tag, classes) {
|
|
1228
|
+
return tag.toLowerCase() + classes.map((c) => `.${cssEscape(c)}`).join("");
|
|
1229
|
+
}
|
|
1230
|
+
function parseClassList(className) {
|
|
1231
|
+
if (typeof className !== "string") return [];
|
|
1232
|
+
return className.trim().split(/\s+/).filter(Boolean).sort();
|
|
1233
|
+
}
|
|
1234
|
+
function classDiff(refClasses, candidateClasses) {
|
|
1235
|
+
const added = [];
|
|
1236
|
+
const removed = [];
|
|
1237
|
+
for (const c of candidateClasses) {
|
|
1238
|
+
if (!refClasses.has(c)) added.push(c);
|
|
1239
|
+
}
|
|
1240
|
+
for (const c of refClasses) {
|
|
1241
|
+
if (!candidateClasses.has(c)) removed.push(c);
|
|
1242
|
+
}
|
|
1243
|
+
added.sort();
|
|
1244
|
+
removed.sort();
|
|
1245
|
+
return { added, removed };
|
|
1246
|
+
}
|
|
1247
|
+
function diffLabel(added, removed) {
|
|
1248
|
+
const parts = [];
|
|
1249
|
+
for (const a of added) parts.push(`+${a}`);
|
|
1250
|
+
for (const r of removed) parts.push(`-${r}`);
|
|
1251
|
+
return parts.join(" ");
|
|
1252
|
+
}
|
|
1253
|
+
var MAX_DIFF = 10;
|
|
1254
|
+
var MAX_CANDIDATES = 200;
|
|
1255
|
+
function findExactMatches(clickedEl, shadowHost2) {
|
|
1256
|
+
const classes = parseClassList(typeof clickedEl.className === "string" ? clickedEl.className : "");
|
|
1257
|
+
const tag = clickedEl.tagName;
|
|
1258
|
+
const fiber = getFiber(clickedEl);
|
|
1259
|
+
const boundary = fiber ? findComponentBoundary(fiber) : null;
|
|
1260
|
+
const componentName = boundary?.componentName ?? null;
|
|
1261
|
+
let exactMatches;
|
|
1262
|
+
if (boundary) {
|
|
1263
|
+
const rootFiber = getRootFiber();
|
|
1264
|
+
const allNodes = rootFiber ? collectComponentDOMNodes(rootFiber, boundary.componentType, tag) : [];
|
|
1265
|
+
exactMatches = allNodes.filter(
|
|
1266
|
+
(n) => n.tagName === tag && n.className === clickedEl.className
|
|
1267
|
+
);
|
|
1268
|
+
} else {
|
|
1269
|
+
if (classes.length === 0) {
|
|
1270
|
+
exactMatches = Array.from(
|
|
1271
|
+
document.querySelectorAll(tag.toLowerCase())
|
|
1272
|
+
).filter(
|
|
1273
|
+
(n) => (typeof n.className === "string" ? n.className.trim() : "") === "" && !isInShadowHost(n, shadowHost2)
|
|
1274
|
+
);
|
|
1275
|
+
} else {
|
|
1276
|
+
const selector = buildSelector(tag, classes);
|
|
1277
|
+
exactMatches = Array.from(
|
|
1278
|
+
document.querySelectorAll(selector)
|
|
1279
|
+
).filter(
|
|
1280
|
+
(n) => n.className === clickedEl.className && !isInShadowHost(n, shadowHost2)
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1265
1283
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
function findEquivalentDescendant(container, clicked, containerNode) {
|
|
1269
|
-
const chain = [];
|
|
1270
|
-
let el = clicked;
|
|
1271
|
-
while (el && el !== containerNode) {
|
|
1272
|
-
chain.unshift(el);
|
|
1273
|
-
el = el.parentElement;
|
|
1274
|
-
}
|
|
1275
|
-
if (chain.length === 0) return container;
|
|
1276
|
-
let node = container;
|
|
1277
|
-
for (const ancestor of chain) {
|
|
1278
|
-
const match = Array.from(node.children).find(
|
|
1279
|
-
(c) => c instanceof HTMLElement && c.tagName === ancestor.tagName && c.className === ancestor.className
|
|
1280
|
-
) ?? null;
|
|
1281
|
-
if (!match) return null;
|
|
1282
|
-
node = match;
|
|
1284
|
+
if (!exactMatches.includes(clickedEl)) {
|
|
1285
|
+
exactMatches.unshift(clickedEl);
|
|
1283
1286
|
}
|
|
1284
|
-
return
|
|
1287
|
+
return {
|
|
1288
|
+
exactMatch: exactMatches,
|
|
1289
|
+
nearGroups: [],
|
|
1290
|
+
// Not computed yet — lazy
|
|
1291
|
+
componentName
|
|
1292
|
+
};
|
|
1285
1293
|
}
|
|
1286
|
-
function
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1294
|
+
function computeNearGroups(clickedEl, exactMatchSet, shadowHost2) {
|
|
1295
|
+
const rawClassName = typeof clickedEl.className === "string" ? clickedEl.className : "";
|
|
1296
|
+
const classes = parseClassList(rawClassName);
|
|
1297
|
+
const tag = clickedEl.tagName;
|
|
1298
|
+
const refSet = new Set(classes);
|
|
1299
|
+
if (classes.length === 0) return [];
|
|
1300
|
+
const fiber = getFiber(clickedEl);
|
|
1301
|
+
const boundary = fiber ? findComponentBoundary(fiber) : null;
|
|
1302
|
+
let candidates;
|
|
1303
|
+
if (boundary) {
|
|
1304
|
+
const rootFiber = getRootFiber();
|
|
1305
|
+
candidates = rootFiber ? collectComponentDOMNodes(rootFiber, boundary.componentType, tag) : [];
|
|
1306
|
+
candidates = candidates.filter((n) => !exactMatchSet.has(n));
|
|
1307
|
+
} else {
|
|
1308
|
+
const seen = new Set(exactMatchSet);
|
|
1309
|
+
candidates = [];
|
|
1310
|
+
for (const cls of classes) {
|
|
1311
|
+
const sel = `${tag.toLowerCase()}.${cssEscape(cls)}`;
|
|
1312
|
+
for (const n of document.querySelectorAll(sel)) {
|
|
1313
|
+
if (!seen.has(n) && !isInShadowHost(n, shadowHost2)) {
|
|
1314
|
+
seen.add(n);
|
|
1315
|
+
candidates.push(n);
|
|
1316
|
+
if (candidates.length >= MAX_CANDIDATES) break;
|
|
1317
|
+
}
|
|
1299
1318
|
}
|
|
1300
|
-
|
|
1319
|
+
if (candidates.length >= MAX_CANDIDATES) break;
|
|
1301
1320
|
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
)[0];
|
|
1316
|
-
const outliers = containerNodes.filter((n) => n.className !== majorityClass);
|
|
1317
|
-
if (outliers.length > 1) {
|
|
1318
|
-
current = parent;
|
|
1319
|
-
continue;
|
|
1320
|
-
}
|
|
1321
|
-
const results = [];
|
|
1322
|
-
for (const sibContainer of containerNodes) {
|
|
1323
|
-
const equiv = findEquivalentDescendant(sibContainer, clickedNode, containerNode);
|
|
1324
|
-
if (equiv) results.push(equiv);
|
|
1325
|
-
}
|
|
1326
|
-
if (results.length > bestResult.length) {
|
|
1327
|
-
bestResult = results;
|
|
1328
|
-
}
|
|
1321
|
+
}
|
|
1322
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1323
|
+
for (const el of candidates) {
|
|
1324
|
+
const candidateClasses = new Set(parseClassList(typeof el.className === "string" ? el.className : ""));
|
|
1325
|
+
const { added, removed } = classDiff(refSet, candidateClasses);
|
|
1326
|
+
const totalDiff = added.length + removed.length;
|
|
1327
|
+
if (totalDiff === 0 || totalDiff > MAX_DIFF) continue;
|
|
1328
|
+
const key = `+${added.join(",")}|-${removed.join(",")}`;
|
|
1329
|
+
const existing = groupMap.get(key);
|
|
1330
|
+
if (existing) {
|
|
1331
|
+
existing.elements.push(el);
|
|
1332
|
+
} else {
|
|
1333
|
+
groupMap.set(key, { added, removed, elements: [el] });
|
|
1329
1334
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
);
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
)
|
|
1350
|
-
const
|
|
1351
|
-
|
|
1352
|
-
|
|
1335
|
+
}
|
|
1336
|
+
const groups = [];
|
|
1337
|
+
for (const [, g] of groupMap) {
|
|
1338
|
+
groups.push({
|
|
1339
|
+
label: diffLabel(g.added, g.removed),
|
|
1340
|
+
added: g.added,
|
|
1341
|
+
removed: g.removed,
|
|
1342
|
+
elements: g.elements
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
groups.sort((a, b) => {
|
|
1346
|
+
const diffA = a.added.length + a.removed.length;
|
|
1347
|
+
const diffB = b.added.length + b.removed.length;
|
|
1348
|
+
if (diffA !== diffB) return diffA - diffB;
|
|
1349
|
+
return b.elements.length - a.elements.length;
|
|
1350
|
+
});
|
|
1351
|
+
return groups;
|
|
1352
|
+
}
|
|
1353
|
+
function collectComponentDOMNodes(rootFiber, componentType, tagName) {
|
|
1354
|
+
const instances = findAllInstances(rootFiber, componentType);
|
|
1355
|
+
const results = [];
|
|
1356
|
+
for (const inst of instances) {
|
|
1357
|
+
collectHostNodes(inst.child, tagName, results);
|
|
1358
|
+
}
|
|
1359
|
+
return results;
|
|
1360
|
+
}
|
|
1361
|
+
function collectHostNodes(fiber, tagName, out) {
|
|
1362
|
+
if (!fiber) return;
|
|
1363
|
+
if (fiber.tag === 5 && fiber.stateNode instanceof HTMLElement) {
|
|
1364
|
+
if (fiber.stateNode.tagName === tagName) {
|
|
1365
|
+
out.push(fiber.stateNode);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
collectHostNodes(fiber.child, tagName, out);
|
|
1369
|
+
collectHostNodes(fiber.sibling, tagName, out);
|
|
1370
|
+
}
|
|
1371
|
+
function isInShadowHost(el, shadowHost2) {
|
|
1372
|
+
if (!shadowHost2) return false;
|
|
1373
|
+
return shadowHost2.contains(el);
|
|
1353
1374
|
}
|
|
1354
1375
|
|
|
1355
1376
|
// overlay/src/context.ts
|
|
@@ -1464,22 +1485,24 @@ ${pad}</${tag}>`;
|
|
|
1464
1485
|
originalClasses: elements.map((n) => n.className)
|
|
1465
1486
|
};
|
|
1466
1487
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1488
|
+
if (newClass) {
|
|
1489
|
+
try {
|
|
1490
|
+
const res = await fetch(`${serverOrigin}/css`, {
|
|
1491
|
+
method: "POST",
|
|
1492
|
+
headers: { "Content-Type": "application/json" },
|
|
1493
|
+
body: JSON.stringify({ classes: [newClass] })
|
|
1494
|
+
});
|
|
1495
|
+
if (gen !== previewGeneration) return;
|
|
1496
|
+
const { css } = await res.json();
|
|
1497
|
+
if (gen !== previewGeneration) return;
|
|
1498
|
+
if (!previewStyleEl) {
|
|
1499
|
+
previewStyleEl = document.createElement("style");
|
|
1500
|
+
previewStyleEl.setAttribute("data-tw-preview", "");
|
|
1501
|
+
document.head.appendChild(previewStyleEl);
|
|
1502
|
+
}
|
|
1503
|
+
previewStyleEl.textContent = css;
|
|
1504
|
+
} catch {
|
|
1505
|
+
}
|
|
1483
1506
|
}
|
|
1484
1507
|
if (gen !== previewGeneration) return;
|
|
1485
1508
|
if (previewState) {
|
|
@@ -1489,7 +1512,7 @@ ${pad}</${tag}>`;
|
|
|
1489
1512
|
}
|
|
1490
1513
|
for (const node of elements) {
|
|
1491
1514
|
if (oldClass) node.classList.remove(oldClass);
|
|
1492
|
-
node.classList.add(newClass);
|
|
1515
|
+
if (newClass) node.classList.add(newClass);
|
|
1493
1516
|
}
|
|
1494
1517
|
}
|
|
1495
1518
|
function revertPreview() {
|
|
@@ -1842,6 +1865,845 @@ ${pad}</${tag}>`;
|
|
|
1842
1865
|
}
|
|
1843
1866
|
};
|
|
1844
1867
|
|
|
1868
|
+
// node_modules/html-to-image/es/util.js
|
|
1869
|
+
function resolveUrl(url, baseUrl) {
|
|
1870
|
+
if (url.match(/^[a-z]+:\/\//i)) {
|
|
1871
|
+
return url;
|
|
1872
|
+
}
|
|
1873
|
+
if (url.match(/^\/\//)) {
|
|
1874
|
+
return window.location.protocol + url;
|
|
1875
|
+
}
|
|
1876
|
+
if (url.match(/^[a-z]+:/i)) {
|
|
1877
|
+
return url;
|
|
1878
|
+
}
|
|
1879
|
+
const doc = document.implementation.createHTMLDocument();
|
|
1880
|
+
const base = doc.createElement("base");
|
|
1881
|
+
const a = doc.createElement("a");
|
|
1882
|
+
doc.head.appendChild(base);
|
|
1883
|
+
doc.body.appendChild(a);
|
|
1884
|
+
if (baseUrl) {
|
|
1885
|
+
base.href = baseUrl;
|
|
1886
|
+
}
|
|
1887
|
+
a.href = url;
|
|
1888
|
+
return a.href;
|
|
1889
|
+
}
|
|
1890
|
+
var uuid = /* @__PURE__ */ (() => {
|
|
1891
|
+
let counter = 0;
|
|
1892
|
+
const random = () => (
|
|
1893
|
+
// eslint-disable-next-line no-bitwise
|
|
1894
|
+
`0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
|
|
1895
|
+
);
|
|
1896
|
+
return () => {
|
|
1897
|
+
counter += 1;
|
|
1898
|
+
return `u${random()}${counter}`;
|
|
1899
|
+
};
|
|
1900
|
+
})();
|
|
1901
|
+
function toArray(arrayLike) {
|
|
1902
|
+
const arr = [];
|
|
1903
|
+
for (let i = 0, l = arrayLike.length; i < l; i++) {
|
|
1904
|
+
arr.push(arrayLike[i]);
|
|
1905
|
+
}
|
|
1906
|
+
return arr;
|
|
1907
|
+
}
|
|
1908
|
+
var styleProps = null;
|
|
1909
|
+
function getStyleProperties(options = {}) {
|
|
1910
|
+
if (styleProps) {
|
|
1911
|
+
return styleProps;
|
|
1912
|
+
}
|
|
1913
|
+
if (options.includeStyleProperties) {
|
|
1914
|
+
styleProps = options.includeStyleProperties;
|
|
1915
|
+
return styleProps;
|
|
1916
|
+
}
|
|
1917
|
+
styleProps = toArray(window.getComputedStyle(document.documentElement));
|
|
1918
|
+
return styleProps;
|
|
1919
|
+
}
|
|
1920
|
+
function px(node, styleProperty) {
|
|
1921
|
+
const win = node.ownerDocument.defaultView || window;
|
|
1922
|
+
const val = win.getComputedStyle(node).getPropertyValue(styleProperty);
|
|
1923
|
+
return val ? parseFloat(val.replace("px", "")) : 0;
|
|
1924
|
+
}
|
|
1925
|
+
function getNodeWidth(node) {
|
|
1926
|
+
const leftBorder = px(node, "border-left-width");
|
|
1927
|
+
const rightBorder = px(node, "border-right-width");
|
|
1928
|
+
return node.clientWidth + leftBorder + rightBorder;
|
|
1929
|
+
}
|
|
1930
|
+
function getNodeHeight(node) {
|
|
1931
|
+
const topBorder = px(node, "border-top-width");
|
|
1932
|
+
const bottomBorder = px(node, "border-bottom-width");
|
|
1933
|
+
return node.clientHeight + topBorder + bottomBorder;
|
|
1934
|
+
}
|
|
1935
|
+
function getImageSize(targetNode, options = {}) {
|
|
1936
|
+
const width = options.width || getNodeWidth(targetNode);
|
|
1937
|
+
const height = options.height || getNodeHeight(targetNode);
|
|
1938
|
+
return { width, height };
|
|
1939
|
+
}
|
|
1940
|
+
function getPixelRatio() {
|
|
1941
|
+
let ratio;
|
|
1942
|
+
let FINAL_PROCESS;
|
|
1943
|
+
try {
|
|
1944
|
+
FINAL_PROCESS = process;
|
|
1945
|
+
} catch (e) {
|
|
1946
|
+
}
|
|
1947
|
+
const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
|
|
1948
|
+
if (val) {
|
|
1949
|
+
ratio = parseInt(val, 10);
|
|
1950
|
+
if (Number.isNaN(ratio)) {
|
|
1951
|
+
ratio = 1;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return ratio || window.devicePixelRatio || 1;
|
|
1955
|
+
}
|
|
1956
|
+
var canvasDimensionLimit = 16384;
|
|
1957
|
+
function checkCanvasDimensions(canvas) {
|
|
1958
|
+
if (canvas.width > canvasDimensionLimit || canvas.height > canvasDimensionLimit) {
|
|
1959
|
+
if (canvas.width > canvasDimensionLimit && canvas.height > canvasDimensionLimit) {
|
|
1960
|
+
if (canvas.width > canvas.height) {
|
|
1961
|
+
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
1962
|
+
canvas.width = canvasDimensionLimit;
|
|
1963
|
+
} else {
|
|
1964
|
+
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
1965
|
+
canvas.height = canvasDimensionLimit;
|
|
1966
|
+
}
|
|
1967
|
+
} else if (canvas.width > canvasDimensionLimit) {
|
|
1968
|
+
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
1969
|
+
canvas.width = canvasDimensionLimit;
|
|
1970
|
+
} else {
|
|
1971
|
+
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
1972
|
+
canvas.height = canvasDimensionLimit;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
function createImage(url) {
|
|
1977
|
+
return new Promise((resolve, reject) => {
|
|
1978
|
+
const img = new Image();
|
|
1979
|
+
img.onload = () => {
|
|
1980
|
+
img.decode().then(() => {
|
|
1981
|
+
requestAnimationFrame(() => resolve(img));
|
|
1982
|
+
});
|
|
1983
|
+
};
|
|
1984
|
+
img.onerror = reject;
|
|
1985
|
+
img.crossOrigin = "anonymous";
|
|
1986
|
+
img.decoding = "async";
|
|
1987
|
+
img.src = url;
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
async function svgToDataURL(svg) {
|
|
1991
|
+
return Promise.resolve().then(() => new XMLSerializer().serializeToString(svg)).then(encodeURIComponent).then((html) => `data:image/svg+xml;charset=utf-8,${html}`);
|
|
1992
|
+
}
|
|
1993
|
+
async function nodeToDataURL(node, width, height) {
|
|
1994
|
+
const xmlns = "http://www.w3.org/2000/svg";
|
|
1995
|
+
const svg = document.createElementNS(xmlns, "svg");
|
|
1996
|
+
const foreignObject = document.createElementNS(xmlns, "foreignObject");
|
|
1997
|
+
svg.setAttribute("width", `${width}`);
|
|
1998
|
+
svg.setAttribute("height", `${height}`);
|
|
1999
|
+
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
2000
|
+
foreignObject.setAttribute("width", "100%");
|
|
2001
|
+
foreignObject.setAttribute("height", "100%");
|
|
2002
|
+
foreignObject.setAttribute("x", "0");
|
|
2003
|
+
foreignObject.setAttribute("y", "0");
|
|
2004
|
+
foreignObject.setAttribute("externalResourcesRequired", "true");
|
|
2005
|
+
svg.appendChild(foreignObject);
|
|
2006
|
+
foreignObject.appendChild(node);
|
|
2007
|
+
return svgToDataURL(svg);
|
|
2008
|
+
}
|
|
2009
|
+
var isInstanceOfElement = (node, instance) => {
|
|
2010
|
+
if (node instanceof instance)
|
|
2011
|
+
return true;
|
|
2012
|
+
const nodePrototype = Object.getPrototypeOf(node);
|
|
2013
|
+
if (nodePrototype === null)
|
|
2014
|
+
return false;
|
|
2015
|
+
return nodePrototype.constructor.name === instance.name || isInstanceOfElement(nodePrototype, instance);
|
|
2016
|
+
};
|
|
2017
|
+
|
|
2018
|
+
// node_modules/html-to-image/es/clone-pseudos.js
|
|
2019
|
+
function formatCSSText(style) {
|
|
2020
|
+
const content = style.getPropertyValue("content");
|
|
2021
|
+
return `${style.cssText} content: '${content.replace(/'|"/g, "")}';`;
|
|
2022
|
+
}
|
|
2023
|
+
function formatCSSProperties(style, options) {
|
|
2024
|
+
return getStyleProperties(options).map((name) => {
|
|
2025
|
+
const value = style.getPropertyValue(name);
|
|
2026
|
+
const priority = style.getPropertyPriority(name);
|
|
2027
|
+
return `${name}: ${value}${priority ? " !important" : ""};`;
|
|
2028
|
+
}).join(" ");
|
|
2029
|
+
}
|
|
2030
|
+
function getPseudoElementStyle(className, pseudo, style, options) {
|
|
2031
|
+
const selector = `.${className}:${pseudo}`;
|
|
2032
|
+
const cssText = style.cssText ? formatCSSText(style) : formatCSSProperties(style, options);
|
|
2033
|
+
return document.createTextNode(`${selector}{${cssText}}`);
|
|
2034
|
+
}
|
|
2035
|
+
function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
|
|
2036
|
+
const style = window.getComputedStyle(nativeNode, pseudo);
|
|
2037
|
+
const content = style.getPropertyValue("content");
|
|
2038
|
+
if (content === "" || content === "none") {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
const className = uuid();
|
|
2042
|
+
try {
|
|
2043
|
+
clonedNode.className = `${clonedNode.className} ${className}`;
|
|
2044
|
+
} catch (err) {
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
const styleElement = document.createElement("style");
|
|
2048
|
+
styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));
|
|
2049
|
+
clonedNode.appendChild(styleElement);
|
|
2050
|
+
}
|
|
2051
|
+
function clonePseudoElements(nativeNode, clonedNode, options) {
|
|
2052
|
+
clonePseudoElement(nativeNode, clonedNode, ":before", options);
|
|
2053
|
+
clonePseudoElement(nativeNode, clonedNode, ":after", options);
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
// node_modules/html-to-image/es/mimes.js
|
|
2057
|
+
var WOFF = "application/font-woff";
|
|
2058
|
+
var JPEG = "image/jpeg";
|
|
2059
|
+
var mimes = {
|
|
2060
|
+
woff: WOFF,
|
|
2061
|
+
woff2: WOFF,
|
|
2062
|
+
ttf: "application/font-truetype",
|
|
2063
|
+
eot: "application/vnd.ms-fontobject",
|
|
2064
|
+
png: "image/png",
|
|
2065
|
+
jpg: JPEG,
|
|
2066
|
+
jpeg: JPEG,
|
|
2067
|
+
gif: "image/gif",
|
|
2068
|
+
tiff: "image/tiff",
|
|
2069
|
+
svg: "image/svg+xml",
|
|
2070
|
+
webp: "image/webp"
|
|
2071
|
+
};
|
|
2072
|
+
function getExtension(url) {
|
|
2073
|
+
const match = /\.([^./]*?)$/g.exec(url);
|
|
2074
|
+
return match ? match[1] : "";
|
|
2075
|
+
}
|
|
2076
|
+
function getMimeType(url) {
|
|
2077
|
+
const extension = getExtension(url).toLowerCase();
|
|
2078
|
+
return mimes[extension] || "";
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// node_modules/html-to-image/es/dataurl.js
|
|
2082
|
+
function getContentFromDataUrl(dataURL) {
|
|
2083
|
+
return dataURL.split(/,/)[1];
|
|
2084
|
+
}
|
|
2085
|
+
function isDataUrl(url) {
|
|
2086
|
+
return url.search(/^(data:)/) !== -1;
|
|
2087
|
+
}
|
|
2088
|
+
function makeDataUrl(content, mimeType) {
|
|
2089
|
+
return `data:${mimeType};base64,${content}`;
|
|
2090
|
+
}
|
|
2091
|
+
async function fetchAsDataURL(url, init2, process2) {
|
|
2092
|
+
const res = await fetch(url, init2);
|
|
2093
|
+
if (res.status === 404) {
|
|
2094
|
+
throw new Error(`Resource "${res.url}" not found`);
|
|
2095
|
+
}
|
|
2096
|
+
const blob = await res.blob();
|
|
2097
|
+
return new Promise((resolve, reject) => {
|
|
2098
|
+
const reader = new FileReader();
|
|
2099
|
+
reader.onerror = reject;
|
|
2100
|
+
reader.onloadend = () => {
|
|
2101
|
+
try {
|
|
2102
|
+
resolve(process2({ res, result: reader.result }));
|
|
2103
|
+
} catch (error) {
|
|
2104
|
+
reject(error);
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
reader.readAsDataURL(blob);
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
var cache = {};
|
|
2111
|
+
function getCacheKey(url, contentType, includeQueryParams) {
|
|
2112
|
+
let key = url.replace(/\?.*/, "");
|
|
2113
|
+
if (includeQueryParams) {
|
|
2114
|
+
key = url;
|
|
2115
|
+
}
|
|
2116
|
+
if (/ttf|otf|eot|woff2?/i.test(key)) {
|
|
2117
|
+
key = key.replace(/.*\//, "");
|
|
2118
|
+
}
|
|
2119
|
+
return contentType ? `[${contentType}]${key}` : key;
|
|
2120
|
+
}
|
|
2121
|
+
async function resourceToDataURL(resourceUrl, contentType, options) {
|
|
2122
|
+
const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);
|
|
2123
|
+
if (cache[cacheKey] != null) {
|
|
2124
|
+
return cache[cacheKey];
|
|
2125
|
+
}
|
|
2126
|
+
if (options.cacheBust) {
|
|
2127
|
+
resourceUrl += (/\?/.test(resourceUrl) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
|
|
2128
|
+
}
|
|
2129
|
+
let dataURL;
|
|
2130
|
+
try {
|
|
2131
|
+
const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result }) => {
|
|
2132
|
+
if (!contentType) {
|
|
2133
|
+
contentType = res.headers.get("Content-Type") || "";
|
|
2134
|
+
}
|
|
2135
|
+
return getContentFromDataUrl(result);
|
|
2136
|
+
});
|
|
2137
|
+
dataURL = makeDataUrl(content, contentType);
|
|
2138
|
+
} catch (error) {
|
|
2139
|
+
dataURL = options.imagePlaceholder || "";
|
|
2140
|
+
let msg = `Failed to fetch resource: ${resourceUrl}`;
|
|
2141
|
+
if (error) {
|
|
2142
|
+
msg = typeof error === "string" ? error : error.message;
|
|
2143
|
+
}
|
|
2144
|
+
if (msg) {
|
|
2145
|
+
console.warn(msg);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
cache[cacheKey] = dataURL;
|
|
2149
|
+
return dataURL;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
// node_modules/html-to-image/es/clone-node.js
|
|
2153
|
+
async function cloneCanvasElement(canvas) {
|
|
2154
|
+
const dataURL = canvas.toDataURL();
|
|
2155
|
+
if (dataURL === "data:,") {
|
|
2156
|
+
return canvas.cloneNode(false);
|
|
2157
|
+
}
|
|
2158
|
+
return createImage(dataURL);
|
|
2159
|
+
}
|
|
2160
|
+
async function cloneVideoElement(video, options) {
|
|
2161
|
+
if (video.currentSrc) {
|
|
2162
|
+
const canvas = document.createElement("canvas");
|
|
2163
|
+
const ctx = canvas.getContext("2d");
|
|
2164
|
+
canvas.width = video.clientWidth;
|
|
2165
|
+
canvas.height = video.clientHeight;
|
|
2166
|
+
ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
2167
|
+
const dataURL2 = canvas.toDataURL();
|
|
2168
|
+
return createImage(dataURL2);
|
|
2169
|
+
}
|
|
2170
|
+
const poster = video.poster;
|
|
2171
|
+
const contentType = getMimeType(poster);
|
|
2172
|
+
const dataURL = await resourceToDataURL(poster, contentType, options);
|
|
2173
|
+
return createImage(dataURL);
|
|
2174
|
+
}
|
|
2175
|
+
async function cloneIFrameElement(iframe, options) {
|
|
2176
|
+
var _a;
|
|
2177
|
+
try {
|
|
2178
|
+
if ((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.body) {
|
|
2179
|
+
return await cloneNode(iframe.contentDocument.body, options, true);
|
|
2180
|
+
}
|
|
2181
|
+
} catch (_b) {
|
|
2182
|
+
}
|
|
2183
|
+
return iframe.cloneNode(false);
|
|
2184
|
+
}
|
|
2185
|
+
async function cloneSingleNode(node, options) {
|
|
2186
|
+
if (isInstanceOfElement(node, HTMLCanvasElement)) {
|
|
2187
|
+
return cloneCanvasElement(node);
|
|
2188
|
+
}
|
|
2189
|
+
if (isInstanceOfElement(node, HTMLVideoElement)) {
|
|
2190
|
+
return cloneVideoElement(node, options);
|
|
2191
|
+
}
|
|
2192
|
+
if (isInstanceOfElement(node, HTMLIFrameElement)) {
|
|
2193
|
+
return cloneIFrameElement(node, options);
|
|
2194
|
+
}
|
|
2195
|
+
return node.cloneNode(isSVGElement(node));
|
|
2196
|
+
}
|
|
2197
|
+
var isSlotElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SLOT";
|
|
2198
|
+
var isSVGElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SVG";
|
|
2199
|
+
async function cloneChildren(nativeNode, clonedNode, options) {
|
|
2200
|
+
var _a, _b;
|
|
2201
|
+
if (isSVGElement(clonedNode)) {
|
|
2202
|
+
return clonedNode;
|
|
2203
|
+
}
|
|
2204
|
+
let children = [];
|
|
2205
|
+
if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {
|
|
2206
|
+
children = toArray(nativeNode.assignedNodes());
|
|
2207
|
+
} else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {
|
|
2208
|
+
children = toArray(nativeNode.contentDocument.body.childNodes);
|
|
2209
|
+
} else {
|
|
2210
|
+
children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
|
|
2211
|
+
}
|
|
2212
|
+
if (children.length === 0 || isInstanceOfElement(nativeNode, HTMLVideoElement)) {
|
|
2213
|
+
return clonedNode;
|
|
2214
|
+
}
|
|
2215
|
+
await children.reduce((deferred, child) => deferred.then(() => cloneNode(child, options)).then((clonedChild) => {
|
|
2216
|
+
if (clonedChild) {
|
|
2217
|
+
clonedNode.appendChild(clonedChild);
|
|
2218
|
+
}
|
|
2219
|
+
}), Promise.resolve());
|
|
2220
|
+
return clonedNode;
|
|
2221
|
+
}
|
|
2222
|
+
function cloneCSSStyle(nativeNode, clonedNode, options) {
|
|
2223
|
+
const targetStyle = clonedNode.style;
|
|
2224
|
+
if (!targetStyle) {
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
2228
|
+
if (sourceStyle.cssText) {
|
|
2229
|
+
targetStyle.cssText = sourceStyle.cssText;
|
|
2230
|
+
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
2231
|
+
} else {
|
|
2232
|
+
getStyleProperties(options).forEach((name) => {
|
|
2233
|
+
let value = sourceStyle.getPropertyValue(name);
|
|
2234
|
+
if (name === "font-size" && value.endsWith("px")) {
|
|
2235
|
+
const reducedFont = Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1;
|
|
2236
|
+
value = `${reducedFont}px`;
|
|
2237
|
+
}
|
|
2238
|
+
if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && name === "display" && value === "inline") {
|
|
2239
|
+
value = "block";
|
|
2240
|
+
}
|
|
2241
|
+
if (name === "d" && clonedNode.getAttribute("d")) {
|
|
2242
|
+
value = `path(${clonedNode.getAttribute("d")})`;
|
|
2243
|
+
}
|
|
2244
|
+
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
function cloneInputValue(nativeNode, clonedNode) {
|
|
2249
|
+
if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {
|
|
2250
|
+
clonedNode.innerHTML = nativeNode.value;
|
|
2251
|
+
}
|
|
2252
|
+
if (isInstanceOfElement(nativeNode, HTMLInputElement)) {
|
|
2253
|
+
clonedNode.setAttribute("value", nativeNode.value);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
function cloneSelectValue(nativeNode, clonedNode) {
|
|
2257
|
+
if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {
|
|
2258
|
+
const clonedSelect = clonedNode;
|
|
2259
|
+
const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute("value"));
|
|
2260
|
+
if (selectedOption) {
|
|
2261
|
+
selectedOption.setAttribute("selected", "");
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
function decorate(nativeNode, clonedNode, options) {
|
|
2266
|
+
if (isInstanceOfElement(clonedNode, Element)) {
|
|
2267
|
+
cloneCSSStyle(nativeNode, clonedNode, options);
|
|
2268
|
+
clonePseudoElements(nativeNode, clonedNode, options);
|
|
2269
|
+
cloneInputValue(nativeNode, clonedNode);
|
|
2270
|
+
cloneSelectValue(nativeNode, clonedNode);
|
|
2271
|
+
}
|
|
2272
|
+
return clonedNode;
|
|
2273
|
+
}
|
|
2274
|
+
async function ensureSVGSymbols(clone, options) {
|
|
2275
|
+
const uses = clone.querySelectorAll ? clone.querySelectorAll("use") : [];
|
|
2276
|
+
if (uses.length === 0) {
|
|
2277
|
+
return clone;
|
|
2278
|
+
}
|
|
2279
|
+
const processedDefs = {};
|
|
2280
|
+
for (let i = 0; i < uses.length; i++) {
|
|
2281
|
+
const use = uses[i];
|
|
2282
|
+
const id = use.getAttribute("xlink:href");
|
|
2283
|
+
if (id) {
|
|
2284
|
+
const exist = clone.querySelector(id);
|
|
2285
|
+
const definition = document.querySelector(id);
|
|
2286
|
+
if (!exist && definition && !processedDefs[id]) {
|
|
2287
|
+
processedDefs[id] = await cloneNode(definition, options, true);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
const nodes = Object.values(processedDefs);
|
|
2292
|
+
if (nodes.length) {
|
|
2293
|
+
const ns = "http://www.w3.org/1999/xhtml";
|
|
2294
|
+
const svg = document.createElementNS(ns, "svg");
|
|
2295
|
+
svg.setAttribute("xmlns", ns);
|
|
2296
|
+
svg.style.position = "absolute";
|
|
2297
|
+
svg.style.width = "0";
|
|
2298
|
+
svg.style.height = "0";
|
|
2299
|
+
svg.style.overflow = "hidden";
|
|
2300
|
+
svg.style.display = "none";
|
|
2301
|
+
const defs = document.createElementNS(ns, "defs");
|
|
2302
|
+
svg.appendChild(defs);
|
|
2303
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
2304
|
+
defs.appendChild(nodes[i]);
|
|
2305
|
+
}
|
|
2306
|
+
clone.appendChild(svg);
|
|
2307
|
+
}
|
|
2308
|
+
return clone;
|
|
2309
|
+
}
|
|
2310
|
+
async function cloneNode(node, options, isRoot) {
|
|
2311
|
+
if (!isRoot && options.filter && !options.filter(node)) {
|
|
2312
|
+
return null;
|
|
2313
|
+
}
|
|
2314
|
+
return Promise.resolve(node).then((clonedNode) => cloneSingleNode(clonedNode, options)).then((clonedNode) => cloneChildren(node, clonedNode, options)).then((clonedNode) => decorate(node, clonedNode, options)).then((clonedNode) => ensureSVGSymbols(clonedNode, options));
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// node_modules/html-to-image/es/embed-resources.js
|
|
2318
|
+
var URL_REGEX = /url\((['"]?)([^'"]+?)\1\)/g;
|
|
2319
|
+
var URL_WITH_FORMAT_REGEX = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
|
|
2320
|
+
var FONT_SRC_REGEX = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
|
|
2321
|
+
function toRegex(url) {
|
|
2322
|
+
const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
|
2323
|
+
return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
|
|
2324
|
+
}
|
|
2325
|
+
function parseURLs(cssText) {
|
|
2326
|
+
const urls = [];
|
|
2327
|
+
cssText.replace(URL_REGEX, (raw, quotation, url) => {
|
|
2328
|
+
urls.push(url);
|
|
2329
|
+
return raw;
|
|
2330
|
+
});
|
|
2331
|
+
return urls.filter((url) => !isDataUrl(url));
|
|
2332
|
+
}
|
|
2333
|
+
async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {
|
|
2334
|
+
try {
|
|
2335
|
+
const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;
|
|
2336
|
+
const contentType = getMimeType(resourceURL);
|
|
2337
|
+
let dataURL;
|
|
2338
|
+
if (getContentFromUrl) {
|
|
2339
|
+
const content = await getContentFromUrl(resolvedURL);
|
|
2340
|
+
dataURL = makeDataUrl(content, contentType);
|
|
2341
|
+
} else {
|
|
2342
|
+
dataURL = await resourceToDataURL(resolvedURL, contentType, options);
|
|
2343
|
+
}
|
|
2344
|
+
return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);
|
|
2345
|
+
} catch (error) {
|
|
2346
|
+
}
|
|
2347
|
+
return cssText;
|
|
2348
|
+
}
|
|
2349
|
+
function filterPreferredFontFormat(str, { preferredFontFormat }) {
|
|
2350
|
+
return !preferredFontFormat ? str : str.replace(FONT_SRC_REGEX, (match) => {
|
|
2351
|
+
while (true) {
|
|
2352
|
+
const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match) || [];
|
|
2353
|
+
if (!format) {
|
|
2354
|
+
return "";
|
|
2355
|
+
}
|
|
2356
|
+
if (format === preferredFontFormat) {
|
|
2357
|
+
return `src: ${src};`;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
function shouldEmbed(url) {
|
|
2363
|
+
return url.search(URL_REGEX) !== -1;
|
|
2364
|
+
}
|
|
2365
|
+
async function embedResources(cssText, baseUrl, options) {
|
|
2366
|
+
if (!shouldEmbed(cssText)) {
|
|
2367
|
+
return cssText;
|
|
2368
|
+
}
|
|
2369
|
+
const filteredCSSText = filterPreferredFontFormat(cssText, options);
|
|
2370
|
+
const urls = parseURLs(filteredCSSText);
|
|
2371
|
+
return urls.reduce((deferred, url) => deferred.then((css) => embed(css, url, baseUrl, options)), Promise.resolve(filteredCSSText));
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
// node_modules/html-to-image/es/embed-images.js
|
|
2375
|
+
async function embedProp(propName, node, options) {
|
|
2376
|
+
var _a;
|
|
2377
|
+
const propValue = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propName);
|
|
2378
|
+
if (propValue) {
|
|
2379
|
+
const cssString = await embedResources(propValue, null, options);
|
|
2380
|
+
node.style.setProperty(propName, cssString, node.style.getPropertyPriority(propName));
|
|
2381
|
+
return true;
|
|
2382
|
+
}
|
|
2383
|
+
return false;
|
|
2384
|
+
}
|
|
2385
|
+
async function embedBackground(clonedNode, options) {
|
|
2386
|
+
;
|
|
2387
|
+
await embedProp("background", clonedNode, options) || await embedProp("background-image", clonedNode, options);
|
|
2388
|
+
await embedProp("mask", clonedNode, options) || await embedProp("-webkit-mask", clonedNode, options) || await embedProp("mask-image", clonedNode, options) || await embedProp("-webkit-mask-image", clonedNode, options);
|
|
2389
|
+
}
|
|
2390
|
+
async function embedImageNode(clonedNode, options) {
|
|
2391
|
+
const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);
|
|
2392
|
+
if (!(isImageElement && !isDataUrl(clonedNode.src)) && !(isInstanceOfElement(clonedNode, SVGImageElement) && !isDataUrl(clonedNode.href.baseVal))) {
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal;
|
|
2396
|
+
const dataURL = await resourceToDataURL(url, getMimeType(url), options);
|
|
2397
|
+
await new Promise((resolve, reject) => {
|
|
2398
|
+
clonedNode.onload = resolve;
|
|
2399
|
+
clonedNode.onerror = options.onImageErrorHandler ? (...attributes) => {
|
|
2400
|
+
try {
|
|
2401
|
+
resolve(options.onImageErrorHandler(...attributes));
|
|
2402
|
+
} catch (error) {
|
|
2403
|
+
reject(error);
|
|
2404
|
+
}
|
|
2405
|
+
} : reject;
|
|
2406
|
+
const image = clonedNode;
|
|
2407
|
+
if (image.decode) {
|
|
2408
|
+
image.decode = resolve;
|
|
2409
|
+
}
|
|
2410
|
+
if (image.loading === "lazy") {
|
|
2411
|
+
image.loading = "eager";
|
|
2412
|
+
}
|
|
2413
|
+
if (isImageElement) {
|
|
2414
|
+
clonedNode.srcset = "";
|
|
2415
|
+
clonedNode.src = dataURL;
|
|
2416
|
+
} else {
|
|
2417
|
+
clonedNode.href.baseVal = dataURL;
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
async function embedChildren(clonedNode, options) {
|
|
2422
|
+
const children = toArray(clonedNode.childNodes);
|
|
2423
|
+
const deferreds = children.map((child) => embedImages(child, options));
|
|
2424
|
+
await Promise.all(deferreds).then(() => clonedNode);
|
|
2425
|
+
}
|
|
2426
|
+
async function embedImages(clonedNode, options) {
|
|
2427
|
+
if (isInstanceOfElement(clonedNode, Element)) {
|
|
2428
|
+
await embedBackground(clonedNode, options);
|
|
2429
|
+
await embedImageNode(clonedNode, options);
|
|
2430
|
+
await embedChildren(clonedNode, options);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// node_modules/html-to-image/es/apply-style.js
|
|
2435
|
+
function applyStyle(node, options) {
|
|
2436
|
+
const { style } = node;
|
|
2437
|
+
if (options.backgroundColor) {
|
|
2438
|
+
style.backgroundColor = options.backgroundColor;
|
|
2439
|
+
}
|
|
2440
|
+
if (options.width) {
|
|
2441
|
+
style.width = `${options.width}px`;
|
|
2442
|
+
}
|
|
2443
|
+
if (options.height) {
|
|
2444
|
+
style.height = `${options.height}px`;
|
|
2445
|
+
}
|
|
2446
|
+
const manual = options.style;
|
|
2447
|
+
if (manual != null) {
|
|
2448
|
+
Object.keys(manual).forEach((key) => {
|
|
2449
|
+
style[key] = manual[key];
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
return node;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
// node_modules/html-to-image/es/embed-webfonts.js
|
|
2456
|
+
var cssFetchCache = {};
|
|
2457
|
+
async function fetchCSS(url) {
|
|
2458
|
+
let cache2 = cssFetchCache[url];
|
|
2459
|
+
if (cache2 != null) {
|
|
2460
|
+
return cache2;
|
|
2461
|
+
}
|
|
2462
|
+
const res = await fetch(url);
|
|
2463
|
+
const cssText = await res.text();
|
|
2464
|
+
cache2 = { url, cssText };
|
|
2465
|
+
cssFetchCache[url] = cache2;
|
|
2466
|
+
return cache2;
|
|
2467
|
+
}
|
|
2468
|
+
async function embedFonts(data, options) {
|
|
2469
|
+
let cssText = data.cssText;
|
|
2470
|
+
const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
|
|
2471
|
+
const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
|
|
2472
|
+
const loadFonts = fontLocs.map(async (loc) => {
|
|
2473
|
+
let url = loc.replace(regexUrl, "$1");
|
|
2474
|
+
if (!url.startsWith("https://")) {
|
|
2475
|
+
url = new URL(url, data.url).href;
|
|
2476
|
+
}
|
|
2477
|
+
return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {
|
|
2478
|
+
cssText = cssText.replace(loc, `url(${result})`);
|
|
2479
|
+
return [loc, result];
|
|
2480
|
+
});
|
|
2481
|
+
});
|
|
2482
|
+
return Promise.all(loadFonts).then(() => cssText);
|
|
2483
|
+
}
|
|
2484
|
+
function parseCSS(source) {
|
|
2485
|
+
if (source == null) {
|
|
2486
|
+
return [];
|
|
2487
|
+
}
|
|
2488
|
+
const result = [];
|
|
2489
|
+
const commentsRegex = /(\/\*[\s\S]*?\*\/)/gi;
|
|
2490
|
+
let cssText = source.replace(commentsRegex, "");
|
|
2491
|
+
const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
|
|
2492
|
+
while (true) {
|
|
2493
|
+
const matches = keyframesRegex.exec(cssText);
|
|
2494
|
+
if (matches === null) {
|
|
2495
|
+
break;
|
|
2496
|
+
}
|
|
2497
|
+
result.push(matches[0]);
|
|
2498
|
+
}
|
|
2499
|
+
cssText = cssText.replace(keyframesRegex, "");
|
|
2500
|
+
const importRegex = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
|
|
2501
|
+
const combinedCSSRegex = "((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})";
|
|
2502
|
+
const unifiedRegex = new RegExp(combinedCSSRegex, "gi");
|
|
2503
|
+
while (true) {
|
|
2504
|
+
let matches = importRegex.exec(cssText);
|
|
2505
|
+
if (matches === null) {
|
|
2506
|
+
matches = unifiedRegex.exec(cssText);
|
|
2507
|
+
if (matches === null) {
|
|
2508
|
+
break;
|
|
2509
|
+
} else {
|
|
2510
|
+
importRegex.lastIndex = unifiedRegex.lastIndex;
|
|
2511
|
+
}
|
|
2512
|
+
} else {
|
|
2513
|
+
unifiedRegex.lastIndex = importRegex.lastIndex;
|
|
2514
|
+
}
|
|
2515
|
+
result.push(matches[0]);
|
|
2516
|
+
}
|
|
2517
|
+
return result;
|
|
2518
|
+
}
|
|
2519
|
+
async function getCSSRules(styleSheets, options) {
|
|
2520
|
+
const ret = [];
|
|
2521
|
+
const deferreds = [];
|
|
2522
|
+
styleSheets.forEach((sheet) => {
|
|
2523
|
+
if ("cssRules" in sheet) {
|
|
2524
|
+
try {
|
|
2525
|
+
toArray(sheet.cssRules || []).forEach((item, index) => {
|
|
2526
|
+
if (item.type === CSSRule.IMPORT_RULE) {
|
|
2527
|
+
let importIndex = index + 1;
|
|
2528
|
+
const url = item.href;
|
|
2529
|
+
const deferred = fetchCSS(url).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
2530
|
+
try {
|
|
2531
|
+
sheet.insertRule(rule, rule.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
|
|
2532
|
+
} catch (error) {
|
|
2533
|
+
console.error("Error inserting rule from remote css", {
|
|
2534
|
+
rule,
|
|
2535
|
+
error
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
})).catch((e) => {
|
|
2539
|
+
console.error("Error loading remote css", e.toString());
|
|
2540
|
+
});
|
|
2541
|
+
deferreds.push(deferred);
|
|
2542
|
+
}
|
|
2543
|
+
});
|
|
2544
|
+
} catch (e) {
|
|
2545
|
+
const inline2 = styleSheets.find((a) => a.href == null) || document.styleSheets[0];
|
|
2546
|
+
if (sheet.href != null) {
|
|
2547
|
+
deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
2548
|
+
inline2.insertRule(rule, inline2.cssRules.length);
|
|
2549
|
+
})).catch((err) => {
|
|
2550
|
+
console.error("Error loading remote stylesheet", err);
|
|
2551
|
+
}));
|
|
2552
|
+
}
|
|
2553
|
+
console.error("Error inlining remote css file", e);
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
});
|
|
2557
|
+
return Promise.all(deferreds).then(() => {
|
|
2558
|
+
styleSheets.forEach((sheet) => {
|
|
2559
|
+
if ("cssRules" in sheet) {
|
|
2560
|
+
try {
|
|
2561
|
+
toArray(sheet.cssRules || []).forEach((item) => {
|
|
2562
|
+
ret.push(item);
|
|
2563
|
+
});
|
|
2564
|
+
} catch (e) {
|
|
2565
|
+
console.error(`Error while reading CSS rules from ${sheet.href}`, e);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
});
|
|
2569
|
+
return ret;
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
function getWebFontRules(cssRules) {
|
|
2573
|
+
return cssRules.filter((rule) => rule.type === CSSRule.FONT_FACE_RULE).filter((rule) => shouldEmbed(rule.style.getPropertyValue("src")));
|
|
2574
|
+
}
|
|
2575
|
+
async function parseWebFontRules(node, options) {
|
|
2576
|
+
if (node.ownerDocument == null) {
|
|
2577
|
+
throw new Error("Provided element is not within a Document");
|
|
2578
|
+
}
|
|
2579
|
+
const styleSheets = toArray(node.ownerDocument.styleSheets);
|
|
2580
|
+
const cssRules = await getCSSRules(styleSheets, options);
|
|
2581
|
+
return getWebFontRules(cssRules);
|
|
2582
|
+
}
|
|
2583
|
+
function normalizeFontFamily(font) {
|
|
2584
|
+
return font.trim().replace(/["']/g, "");
|
|
2585
|
+
}
|
|
2586
|
+
function getUsedFonts(node) {
|
|
2587
|
+
const fonts = /* @__PURE__ */ new Set();
|
|
2588
|
+
function traverse(node2) {
|
|
2589
|
+
const fontFamily = node2.style.fontFamily || getComputedStyle(node2).fontFamily;
|
|
2590
|
+
fontFamily.split(",").forEach((font) => {
|
|
2591
|
+
fonts.add(normalizeFontFamily(font));
|
|
2592
|
+
});
|
|
2593
|
+
Array.from(node2.children).forEach((child) => {
|
|
2594
|
+
if (child instanceof HTMLElement) {
|
|
2595
|
+
traverse(child);
|
|
2596
|
+
}
|
|
2597
|
+
});
|
|
2598
|
+
}
|
|
2599
|
+
traverse(node);
|
|
2600
|
+
return fonts;
|
|
2601
|
+
}
|
|
2602
|
+
async function getWebFontCSS(node, options) {
|
|
2603
|
+
const rules = await parseWebFontRules(node, options);
|
|
2604
|
+
const usedFonts = getUsedFonts(node);
|
|
2605
|
+
const cssTexts = await Promise.all(rules.filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily))).map((rule) => {
|
|
2606
|
+
const baseUrl = rule.parentStyleSheet ? rule.parentStyleSheet.href : null;
|
|
2607
|
+
return embedResources(rule.cssText, baseUrl, options);
|
|
2608
|
+
}));
|
|
2609
|
+
return cssTexts.join("\n");
|
|
2610
|
+
}
|
|
2611
|
+
async function embedWebFonts(clonedNode, options) {
|
|
2612
|
+
const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
|
|
2613
|
+
if (cssText) {
|
|
2614
|
+
const styleNode = document.createElement("style");
|
|
2615
|
+
const sytleContent = document.createTextNode(cssText);
|
|
2616
|
+
styleNode.appendChild(sytleContent);
|
|
2617
|
+
if (clonedNode.firstChild) {
|
|
2618
|
+
clonedNode.insertBefore(styleNode, clonedNode.firstChild);
|
|
2619
|
+
} else {
|
|
2620
|
+
clonedNode.appendChild(styleNode);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
// node_modules/html-to-image/es/index.js
|
|
2626
|
+
async function toSvg(node, options = {}) {
|
|
2627
|
+
const { width, height } = getImageSize(node, options);
|
|
2628
|
+
const clonedNode = await cloneNode(node, options, true);
|
|
2629
|
+
await embedWebFonts(clonedNode, options);
|
|
2630
|
+
await embedImages(clonedNode, options);
|
|
2631
|
+
applyStyle(clonedNode, options);
|
|
2632
|
+
const datauri = await nodeToDataURL(clonedNode, width, height);
|
|
2633
|
+
return datauri;
|
|
2634
|
+
}
|
|
2635
|
+
async function toCanvas(node, options = {}) {
|
|
2636
|
+
const { width, height } = getImageSize(node, options);
|
|
2637
|
+
const svg = await toSvg(node, options);
|
|
2638
|
+
const img = await createImage(svg);
|
|
2639
|
+
const canvas = document.createElement("canvas");
|
|
2640
|
+
const context = canvas.getContext("2d");
|
|
2641
|
+
const ratio = options.pixelRatio || getPixelRatio();
|
|
2642
|
+
const canvasWidth = options.canvasWidth || width;
|
|
2643
|
+
const canvasHeight = options.canvasHeight || height;
|
|
2644
|
+
canvas.width = canvasWidth * ratio;
|
|
2645
|
+
canvas.height = canvasHeight * ratio;
|
|
2646
|
+
if (!options.skipAutoScale) {
|
|
2647
|
+
checkCanvasDimensions(canvas);
|
|
2648
|
+
}
|
|
2649
|
+
canvas.style.width = `${canvasWidth}`;
|
|
2650
|
+
canvas.style.height = `${canvasHeight}`;
|
|
2651
|
+
if (options.backgroundColor) {
|
|
2652
|
+
context.fillStyle = options.backgroundColor;
|
|
2653
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
2654
|
+
}
|
|
2655
|
+
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
2656
|
+
return canvas;
|
|
2657
|
+
}
|
|
2658
|
+
async function toPng(node, options = {}) {
|
|
2659
|
+
const canvas = await toCanvas(node, options);
|
|
2660
|
+
return canvas.toDataURL();
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
// overlay/src/screenshot.ts
|
|
2664
|
+
function areSiblings(nodes) {
|
|
2665
|
+
if (nodes.length === 0) return false;
|
|
2666
|
+
const parent = nodes[0].parentElement;
|
|
2667
|
+
return nodes.every((n) => n.parentElement === parent);
|
|
2668
|
+
}
|
|
2669
|
+
async function captureRegion(nodes) {
|
|
2670
|
+
if (nodes.length === 1) {
|
|
2671
|
+
const rect = nodes[0].getBoundingClientRect();
|
|
2672
|
+
const width2 = Math.round(rect.width);
|
|
2673
|
+
const height2 = Math.round(rect.height);
|
|
2674
|
+
const dataUrl = await toPng(nodes[0], { skipFonts: true, width: width2, height: height2 });
|
|
2675
|
+
return { dataUrl, width: width2, height: height2 };
|
|
2676
|
+
}
|
|
2677
|
+
const parent = nodes[0].parentElement;
|
|
2678
|
+
const rects = nodes.map((n) => n.getBoundingClientRect());
|
|
2679
|
+
const top = Math.min(...rects.map((r) => r.top));
|
|
2680
|
+
const left = Math.min(...rects.map((r) => r.left));
|
|
2681
|
+
const right = Math.max(...rects.map((r) => r.right));
|
|
2682
|
+
const bottom = Math.max(...rects.map((r) => r.bottom));
|
|
2683
|
+
const width = Math.round(right - left);
|
|
2684
|
+
const height = Math.round(bottom - top);
|
|
2685
|
+
const ghost = parent.cloneNode(false);
|
|
2686
|
+
ghost.style.padding = "0";
|
|
2687
|
+
ghost.style.border = "none";
|
|
2688
|
+
ghost.style.margin = "0";
|
|
2689
|
+
ghost.style.width = `${width}px`;
|
|
2690
|
+
for (const node of nodes) {
|
|
2691
|
+
ghost.appendChild(node.cloneNode(true));
|
|
2692
|
+
}
|
|
2693
|
+
ghost.style.position = "fixed";
|
|
2694
|
+
ghost.style.left = "0";
|
|
2695
|
+
ghost.style.top = "0";
|
|
2696
|
+
ghost.style.zIndex = "999999";
|
|
2697
|
+
ghost.style.pointerEvents = "none";
|
|
2698
|
+
document.body.appendChild(ghost);
|
|
2699
|
+
try {
|
|
2700
|
+
const dataUrl = await toPng(ghost, { skipFonts: true, width, height });
|
|
2701
|
+
return { dataUrl, width, height };
|
|
2702
|
+
} finally {
|
|
2703
|
+
ghost.remove();
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
|
|
1845
2707
|
// overlay/src/index.ts
|
|
1846
2708
|
var shadowRoot;
|
|
1847
2709
|
var shadowHost;
|
|
@@ -1852,7 +2714,7 @@ ${pad}</${tag}>`;
|
|
|
1852
2714
|
var currentTargetEl = null;
|
|
1853
2715
|
var currentBoundary = null;
|
|
1854
2716
|
var currentInstances = [];
|
|
1855
|
-
var
|
|
2717
|
+
var cachedNearGroups = null;
|
|
1856
2718
|
var hoverOutlineEl = null;
|
|
1857
2719
|
var hoverTooltipEl = null;
|
|
1858
2720
|
var lastHoveredEl = null;
|
|
@@ -1985,7 +2847,7 @@ ${pad}</${tag}>`;
|
|
|
1985
2847
|
align-self: stretch;
|
|
1986
2848
|
}
|
|
1987
2849
|
/* Base style for all buttons inside the toolbar */
|
|
1988
|
-
.draw-btn, .el-pick-btn, .el-add-btn {
|
|
2850
|
+
.draw-btn, .el-reselect-btn, .el-pick-btn, .el-add-btn {
|
|
1989
2851
|
background: transparent;
|
|
1990
2852
|
border: none;
|
|
1991
2853
|
border-radius: 0;
|
|
@@ -2009,10 +2871,59 @@ ${pad}</${tag}>`;
|
|
|
2009
2871
|
.el-pick-btn { gap: 3px; padding: 0 8px; font-size: 12px; font-weight: 600; letter-spacing: 0.01em; }
|
|
2010
2872
|
.el-pick-btn svg { opacity: 0.7; flex-shrink: 0; }
|
|
2011
2873
|
.el-add-btn { padding: 0 10px; font-size: 15px; font-weight: 400; }
|
|
2012
|
-
.
|
|
2874
|
+
.el-reselect-btn { padding: 0 9px; }
|
|
2875
|
+
.draw-btn:hover, .el-reselect-btn:hover, .el-pick-btn:hover, .el-add-btn:hover,
|
|
2013
2876
|
.el-pick-btn.open {
|
|
2014
2877
|
background: rgba(255,255,255,0.12);
|
|
2015
2878
|
}
|
|
2879
|
+
/* \u2500\u2500 Hover preview highlight (dashed, for group hover) \u2500\u2500 */
|
|
2880
|
+
.highlight-preview {
|
|
2881
|
+
position: fixed;
|
|
2882
|
+
pointer-events: none;
|
|
2883
|
+
border: 2px dashed #00848B;
|
|
2884
|
+
border-radius: 2px;
|
|
2885
|
+
box-sizing: border-box;
|
|
2886
|
+
z-index: 999998;
|
|
2887
|
+
}
|
|
2888
|
+
/* \u2500\u2500 Group picker popover (replaces instance picker) \u2500\u2500 */
|
|
2889
|
+
.el-group-empty {
|
|
2890
|
+
padding: 12px 14px;
|
|
2891
|
+
font-size: 11px;
|
|
2892
|
+
color: #687879;
|
|
2893
|
+
text-align: center;
|
|
2894
|
+
}
|
|
2895
|
+
.el-group-row {
|
|
2896
|
+
display: flex;
|
|
2897
|
+
align-items: center;
|
|
2898
|
+
gap: 8px;
|
|
2899
|
+
padding: 5px 12px;
|
|
2900
|
+
cursor: pointer;
|
|
2901
|
+
transition: background 0.1s;
|
|
2902
|
+
}
|
|
2903
|
+
.el-group-row:hover { background: rgba(0,132,139,0.05); }
|
|
2904
|
+
.el-group-row input[type=checkbox] {
|
|
2905
|
+
accent-color: #00848B;
|
|
2906
|
+
width: 13px;
|
|
2907
|
+
height: 13px;
|
|
2908
|
+
flex-shrink: 0;
|
|
2909
|
+
cursor: pointer;
|
|
2910
|
+
}
|
|
2911
|
+
.el-group-count {
|
|
2912
|
+
font-size: 11px;
|
|
2913
|
+
font-weight: 600;
|
|
2914
|
+
color: #334041;
|
|
2915
|
+
min-width: 20px;
|
|
2916
|
+
}
|
|
2917
|
+
.el-group-diff {
|
|
2918
|
+
flex: 1;
|
|
2919
|
+
font-size: 10px;
|
|
2920
|
+
font-family: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
|
|
2921
|
+
overflow: hidden;
|
|
2922
|
+
text-overflow: ellipsis;
|
|
2923
|
+
white-space: nowrap;
|
|
2924
|
+
}
|
|
2925
|
+
.el-group-diff .diff-add { color: #16a34a; }
|
|
2926
|
+
.el-group-diff .diff-rem { color: #dc2626; }
|
|
2016
2927
|
/* \u2500\u2500 Instance picker popover \u2500\u2500 */
|
|
2017
2928
|
.el-picker {
|
|
2018
2929
|
position: fixed;
|
|
@@ -2211,7 +3122,6 @@ ${pad}</${tag}>`;
|
|
|
2211
3122
|
var drawPopoverEl = null;
|
|
2212
3123
|
var pickerEl = null;
|
|
2213
3124
|
var pickerCloseHandler = null;
|
|
2214
|
-
var selectedInstanceIndices = /* @__PURE__ */ new Set();
|
|
2215
3125
|
function removeDrawButton() {
|
|
2216
3126
|
toolbarEl?.remove();
|
|
2217
3127
|
toolbarEl = null;
|
|
@@ -2281,7 +3191,7 @@ ${pad}</${tag}>`;
|
|
|
2281
3191
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
2282
3192
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
2283
3193
|
</svg>`;
|
|
2284
|
-
var
|
|
3194
|
+
var RESELECT_SVG = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 12l2.5 6 1.5-3 3-1.5z"/></svg>`;
|
|
2285
3195
|
async function positionWithFlip(anchor, floating, placement = "top-start") {
|
|
2286
3196
|
const { x, y } = await computePosition2(anchor, floating, {
|
|
2287
3197
|
placement,
|
|
@@ -2292,14 +3202,23 @@ ${pad}</${tag}>`;
|
|
|
2292
3202
|
}
|
|
2293
3203
|
function showDrawButton(targetEl) {
|
|
2294
3204
|
removeDrawButton();
|
|
2295
|
-
const
|
|
2296
|
-
const instanceCount = allEquivalentNodes.length;
|
|
3205
|
+
const instanceCount = currentEquivalentNodes.length;
|
|
2297
3206
|
const toolbar = document.createElement("div");
|
|
2298
3207
|
toolbar.className = "el-toolbar";
|
|
2299
3208
|
toolbar.style.left = "0px";
|
|
2300
3209
|
toolbar.style.top = "0px";
|
|
2301
3210
|
shadowRoot.appendChild(toolbar);
|
|
2302
3211
|
toolbarEl = toolbar;
|
|
3212
|
+
const reselectBtn = document.createElement("button");
|
|
3213
|
+
reselectBtn.className = "el-reselect-btn";
|
|
3214
|
+
reselectBtn.innerHTML = RESELECT_SVG;
|
|
3215
|
+
reselectBtn.title = "Re-select element";
|
|
3216
|
+
toolbar.appendChild(reselectBtn);
|
|
3217
|
+
reselectBtn.addEventListener("click", (e) => {
|
|
3218
|
+
e.stopPropagation();
|
|
3219
|
+
clearHighlights();
|
|
3220
|
+
setSelectMode(true);
|
|
3221
|
+
});
|
|
2303
3222
|
const drawBtn = document.createElement("button");
|
|
2304
3223
|
drawBtn.className = "draw-btn";
|
|
2305
3224
|
drawBtn.innerHTML = PENCIL_SVG;
|
|
@@ -2316,69 +3235,47 @@ ${pad}</${tag}>`;
|
|
|
2316
3235
|
showDrawPopover(drawBtn);
|
|
2317
3236
|
}
|
|
2318
3237
|
});
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
drawPopoverEl?.remove();
|
|
2334
|
-
drawPopoverEl = null;
|
|
2335
|
-
if (pickerEl) {
|
|
2336
|
-
pickerEl.remove();
|
|
2337
|
-
pickerEl = null;
|
|
2338
|
-
countBtn.classList.remove("open");
|
|
2339
|
-
} else {
|
|
2340
|
-
countBtn.classList.add("open");
|
|
2341
|
-
showInstancePicker(
|
|
2342
|
-
countBtn,
|
|
2343
|
-
() => countBtn.classList.remove("open"),
|
|
2344
|
-
(indices) => {
|
|
2345
|
-
currentEquivalentNodes = indices.map((i) => allEquivalentNodes[i]).filter(Boolean);
|
|
2346
|
-
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
2347
|
-
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
2348
|
-
updateCountBtn(currentEquivalentNodes.length);
|
|
2349
|
-
}
|
|
2350
|
-
);
|
|
2351
|
-
}
|
|
2352
|
-
});
|
|
2353
|
-
const addBtn = document.createElement("button");
|
|
2354
|
-
addBtn.className = "el-add-btn";
|
|
2355
|
-
addBtn.textContent = "+";
|
|
2356
|
-
addBtn.title = "Add a different element to selection";
|
|
2357
|
-
toolbar.appendChild(addBtn);
|
|
2358
|
-
addBtn.addEventListener("click", (e) => {
|
|
2359
|
-
e.stopPropagation();
|
|
2360
|
-
pickerEl?.remove();
|
|
3238
|
+
const sep = document.createElement("div");
|
|
3239
|
+
sep.className = "el-toolbar-sep";
|
|
3240
|
+
toolbar.appendChild(sep);
|
|
3241
|
+
const addGroupBtn = document.createElement("button");
|
|
3242
|
+
addGroupBtn.className = "el-add-btn";
|
|
3243
|
+
addGroupBtn.textContent = `${instanceCount} +`;
|
|
3244
|
+
addGroupBtn.title = `${instanceCount} matching element${instanceCount !== 1 ? "s" : ""} selected \u2014 click to add similar`;
|
|
3245
|
+
toolbar.appendChild(addGroupBtn);
|
|
3246
|
+
addGroupBtn.addEventListener("click", (e) => {
|
|
3247
|
+
e.stopPropagation();
|
|
3248
|
+
drawPopoverEl?.remove();
|
|
3249
|
+
drawPopoverEl = null;
|
|
3250
|
+
if (pickerEl) {
|
|
3251
|
+
pickerEl.remove();
|
|
2361
3252
|
pickerEl = null;
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
3253
|
+
addGroupBtn.classList.remove("open");
|
|
3254
|
+
} else {
|
|
3255
|
+
addGroupBtn.classList.add("open");
|
|
3256
|
+
showGroupPicker(
|
|
3257
|
+
addGroupBtn,
|
|
3258
|
+
() => addGroupBtn.classList.remove("open"),
|
|
3259
|
+
(totalCount) => {
|
|
3260
|
+
addGroupBtn.textContent = `${totalCount} +`;
|
|
3261
|
+
addGroupBtn.title = `${totalCount} matching element${totalCount !== 1 ? "s" : ""} selected \u2014 click to add similar`;
|
|
3262
|
+
}
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
});
|
|
2369
3266
|
positionWithFlip(targetEl, toolbar);
|
|
2370
3267
|
}
|
|
2371
|
-
function
|
|
3268
|
+
function showGroupPicker(anchorBtn, onClose, onCountChange) {
|
|
2372
3269
|
if (pickerCloseHandler) {
|
|
2373
3270
|
document.removeEventListener("click", pickerCloseHandler, { capture: true });
|
|
2374
3271
|
pickerCloseHandler = null;
|
|
2375
3272
|
}
|
|
2376
3273
|
pickerEl?.remove();
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
3274
|
+
if (!cachedNearGroups && currentTargetEl) {
|
|
3275
|
+
const exactSet = new Set(currentEquivalentNodes);
|
|
3276
|
+
cachedNearGroups = computeNearGroups(currentTargetEl, exactSet, shadowHost);
|
|
3277
|
+
}
|
|
3278
|
+
const groups = cachedNearGroups ?? [];
|
|
2382
3279
|
const picker = document.createElement("div");
|
|
2383
3280
|
picker.className = "el-picker";
|
|
2384
3281
|
picker.style.left = "0px";
|
|
@@ -2389,72 +3286,89 @@ ${pad}</${tag}>`;
|
|
|
2389
3286
|
header.className = "el-picker-header";
|
|
2390
3287
|
const title = document.createElement("span");
|
|
2391
3288
|
title.className = "el-picker-title";
|
|
2392
|
-
title.textContent =
|
|
2393
|
-
const actions = document.createElement("div");
|
|
2394
|
-
actions.className = "el-picker-actions";
|
|
2395
|
-
const allLink = document.createElement("a");
|
|
2396
|
-
allLink.textContent = "All";
|
|
2397
|
-
const noneLink = document.createElement("a");
|
|
2398
|
-
noneLink.textContent = "None";
|
|
2399
|
-
actions.appendChild(allLink);
|
|
2400
|
-
actions.appendChild(noneLink);
|
|
3289
|
+
title.textContent = "Similar elements";
|
|
2401
3290
|
header.appendChild(title);
|
|
2402
|
-
header.appendChild(actions);
|
|
2403
3291
|
picker.appendChild(header);
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
3292
|
+
if (groups.length === 0) {
|
|
3293
|
+
const empty = document.createElement("div");
|
|
3294
|
+
empty.className = "el-group-empty";
|
|
3295
|
+
empty.textContent = "No similar elements found";
|
|
3296
|
+
picker.appendChild(empty);
|
|
3297
|
+
} else {
|
|
3298
|
+
let clearPreviewHighlights2 = function() {
|
|
3299
|
+
shadowRoot.querySelectorAll(".highlight-preview").forEach((el) => el.remove());
|
|
3300
|
+
}, updateSelection2 = function() {
|
|
3301
|
+
const allNodes = [...baseNodes];
|
|
3302
|
+
for (const idx of checkedGroups) {
|
|
3303
|
+
for (const el of groups[idx].elements) {
|
|
3304
|
+
if (!allNodes.includes(el)) allNodes.push(el);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
currentEquivalentNodes = allNodes;
|
|
3308
|
+
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
3309
|
+
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
3310
|
+
onCountChange(currentEquivalentNodes.length);
|
|
3311
|
+
if (currentTargetEl && currentBoundary) {
|
|
3312
|
+
sendTo("panel", {
|
|
3313
|
+
type: "ELEMENT_SELECTED",
|
|
3314
|
+
componentName: currentBoundary.componentName,
|
|
3315
|
+
instanceCount: currentEquivalentNodes.length,
|
|
3316
|
+
classes: typeof currentTargetEl.className === "string" ? currentTargetEl.className : "",
|
|
3317
|
+
tailwindConfig: tailwindConfigCache
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
};
|
|
3321
|
+
var clearPreviewHighlights = clearPreviewHighlights2, updateSelection = updateSelection2;
|
|
3322
|
+
const list = document.createElement("div");
|
|
3323
|
+
list.className = "el-picker-list";
|
|
3324
|
+
picker.appendChild(list);
|
|
3325
|
+
const checkedGroups = /* @__PURE__ */ new Set();
|
|
3326
|
+
const baseNodes = [...currentEquivalentNodes];
|
|
3327
|
+
groups.forEach((group, idx) => {
|
|
2412
3328
|
const row = document.createElement("label");
|
|
2413
|
-
row.className = "el-
|
|
3329
|
+
row.className = "el-group-row";
|
|
2414
3330
|
const cb = document.createElement("input");
|
|
2415
3331
|
cb.type = "checkbox";
|
|
2416
|
-
cb.checked =
|
|
3332
|
+
cb.checked = false;
|
|
2417
3333
|
cb.addEventListener("change", () => {
|
|
2418
|
-
if (cb.checked)
|
|
2419
|
-
else
|
|
2420
|
-
|
|
2421
|
-
updateApply();
|
|
3334
|
+
if (cb.checked) checkedGroups.add(idx);
|
|
3335
|
+
else checkedGroups.delete(idx);
|
|
3336
|
+
updateSelection2();
|
|
2422
3337
|
});
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
3338
|
+
const count = document.createElement("span");
|
|
3339
|
+
count.className = "el-group-count";
|
|
3340
|
+
count.textContent = `(${group.elements.length})`;
|
|
3341
|
+
const diff = document.createElement("span");
|
|
3342
|
+
diff.className = "el-group-diff";
|
|
3343
|
+
const parts = [];
|
|
3344
|
+
for (const a of group.added) parts.push(`<span class="diff-add">+${a}</span>`);
|
|
3345
|
+
for (const r of group.removed) parts.push(`<span class="diff-rem">-${r}</span>`);
|
|
3346
|
+
diff.innerHTML = parts.join(" ");
|
|
2430
3347
|
row.appendChild(cb);
|
|
2431
|
-
row.appendChild(
|
|
2432
|
-
row.appendChild(
|
|
3348
|
+
row.appendChild(count);
|
|
3349
|
+
row.appendChild(diff);
|
|
2433
3350
|
list.appendChild(row);
|
|
3351
|
+
row.addEventListener("mouseenter", () => {
|
|
3352
|
+
clearPreviewHighlights2();
|
|
3353
|
+
for (const el of group.elements) {
|
|
3354
|
+
const rect = el.getBoundingClientRect();
|
|
3355
|
+
const preview = document.createElement("div");
|
|
3356
|
+
preview.className = "highlight-preview";
|
|
3357
|
+
preview.style.top = `${rect.top - 3}px`;
|
|
3358
|
+
preview.style.left = `${rect.left - 3}px`;
|
|
3359
|
+
preview.style.width = `${rect.width + 6}px`;
|
|
3360
|
+
preview.style.height = `${rect.height + 6}px`;
|
|
3361
|
+
shadowRoot.appendChild(preview);
|
|
3362
|
+
}
|
|
3363
|
+
});
|
|
3364
|
+
row.addEventListener("mouseleave", () => {
|
|
3365
|
+
clearPreviewHighlights2();
|
|
3366
|
+
});
|
|
2434
3367
|
});
|
|
2435
3368
|
}
|
|
2436
|
-
|
|
2437
|
-
allLink.addEventListener("click", () => {
|
|
2438
|
-
instances.forEach((_, i) => selected.add(i));
|
|
2439
|
-
renderRows();
|
|
2440
|
-
updateApply();
|
|
2441
|
-
});
|
|
2442
|
-
noneLink.addEventListener("click", () => {
|
|
2443
|
-
selected.clear();
|
|
2444
|
-
renderRows();
|
|
2445
|
-
updateApply();
|
|
2446
|
-
});
|
|
2447
|
-
const footer = document.createElement("div");
|
|
2448
|
-
footer.className = "el-picker-footer";
|
|
2449
|
-
const applyBtn = document.createElement("button");
|
|
2450
|
-
applyBtn.className = "el-picker-apply";
|
|
2451
|
-
footer.appendChild(applyBtn);
|
|
2452
|
-
picker.appendChild(footer);
|
|
2453
|
-
function updateApply() {
|
|
2454
|
-
applyBtn.textContent = `Apply (${selected.size} selected)`;
|
|
2455
|
-
}
|
|
2456
|
-
updateApply();
|
|
3369
|
+
positionWithFlip(anchorBtn, picker);
|
|
2457
3370
|
const removePicker = () => {
|
|
3371
|
+
shadowRoot.querySelectorAll(".highlight-preview").forEach((el) => el.remove());
|
|
2458
3372
|
if (pickerCloseHandler) {
|
|
2459
3373
|
document.removeEventListener("click", pickerCloseHandler, { capture: true });
|
|
2460
3374
|
pickerCloseHandler = null;
|
|
@@ -2462,16 +3376,6 @@ ${pad}</${tag}>`;
|
|
|
2462
3376
|
pickerEl?.remove();
|
|
2463
3377
|
pickerEl = null;
|
|
2464
3378
|
};
|
|
2465
|
-
applyBtn.addEventListener("click", (e) => {
|
|
2466
|
-
e.stopPropagation();
|
|
2467
|
-
const selectedIndices = [...selected];
|
|
2468
|
-
selectedInstanceIndices = new Set(selectedIndices);
|
|
2469
|
-
sendTo("panel", { type: "SELECT_MATCHING", indices: selectedIndices });
|
|
2470
|
-
onApply?.(selectedIndices);
|
|
2471
|
-
removePicker();
|
|
2472
|
-
onClose();
|
|
2473
|
-
});
|
|
2474
|
-
positionWithFlip(anchorBtn, picker);
|
|
2475
3379
|
setTimeout(() => {
|
|
2476
3380
|
pickerCloseHandler = (e) => {
|
|
2477
3381
|
const path = e.composedPath();
|
|
@@ -2515,6 +3419,26 @@ ${pad}</${tag}>`;
|
|
|
2515
3419
|
});
|
|
2516
3420
|
popover.appendChild(row);
|
|
2517
3421
|
}
|
|
3422
|
+
const sep = document.createElement("div");
|
|
3423
|
+
sep.style.cssText = "height:1px;background:#DFE2E2;margin:4px 0;";
|
|
3424
|
+
popover.appendChild(sep);
|
|
3425
|
+
const screenshotHeader = document.createElement("div");
|
|
3426
|
+
screenshotHeader.className = "draw-popover-header";
|
|
3427
|
+
screenshotHeader.textContent = "Screenshot & Annotate";
|
|
3428
|
+
popover.appendChild(screenshotHeader);
|
|
3429
|
+
const screenshotRow = document.createElement("button");
|
|
3430
|
+
screenshotRow.className = "draw-popover-item";
|
|
3431
|
+
screenshotRow.innerHTML = `
|
|
3432
|
+
<span class="draw-popover-icon">\u{1F4F7}</span>
|
|
3433
|
+
<span class="draw-popover-label">Screenshot & Annotate</span>
|
|
3434
|
+
`;
|
|
3435
|
+
screenshotRow.addEventListener("click", (e) => {
|
|
3436
|
+
e.stopPropagation();
|
|
3437
|
+
drawPopoverEl?.remove();
|
|
3438
|
+
drawPopoverEl = null;
|
|
3439
|
+
handleCaptureScreenshot();
|
|
3440
|
+
});
|
|
3441
|
+
popover.appendChild(screenshotRow);
|
|
2518
3442
|
drawPopoverEl = popover;
|
|
2519
3443
|
shadowRoot.appendChild(popover);
|
|
2520
3444
|
positionWithFlip(anchorBtn, popover, "top-start");
|
|
@@ -2563,86 +3487,25 @@ ${pad}</${tag}>`;
|
|
|
2563
3487
|
e.preventDefault();
|
|
2564
3488
|
e.stopPropagation();
|
|
2565
3489
|
const target = e.target;
|
|
2566
|
-
const
|
|
2567
|
-
const
|
|
2568
|
-
const
|
|
2569
|
-
const
|
|
2570
|
-
let componentName;
|
|
2571
|
-
if (hasFiber) {
|
|
2572
|
-
const rootFiber = getRootFiber();
|
|
2573
|
-
if (!rootFiber) {
|
|
2574
|
-
showToast("Could not find React root.");
|
|
2575
|
-
return;
|
|
2576
|
-
}
|
|
2577
|
-
const instances = findAllInstances(rootFiber, boundary.componentType);
|
|
2578
|
-
const path = getChildPath(boundary.componentFiber, fiber);
|
|
2579
|
-
for (const inst of instances) {
|
|
2580
|
-
const node = resolvePathToDOM(inst, path);
|
|
2581
|
-
if (node) newNodes.push(node);
|
|
2582
|
-
}
|
|
2583
|
-
componentName = boundary.componentName;
|
|
2584
|
-
} else {
|
|
2585
|
-
const targetEl2 = target;
|
|
2586
|
-
newNodes.push(...findDOMEquivalents(targetEl2));
|
|
2587
|
-
componentName = targetEl2.tagName.toLowerCase();
|
|
2588
|
-
}
|
|
2589
|
-
if (addingMode && currentEquivalentNodes.length > 0) {
|
|
2590
|
-
addingMode = false;
|
|
2591
|
-
const merged = [...currentEquivalentNodes];
|
|
2592
|
-
for (const n of newNodes) {
|
|
2593
|
-
if (!merged.includes(n)) merged.push(n);
|
|
2594
|
-
}
|
|
2595
|
-
clearHighlights();
|
|
2596
|
-
merged.forEach((n) => highlightElement(n));
|
|
2597
|
-
currentEquivalentNodes = merged;
|
|
2598
|
-
selectedInstanceIndices = /* @__PURE__ */ new Set();
|
|
2599
|
-
if (currentTargetEl) showDrawButton(currentTargetEl);
|
|
2600
|
-
sendTo("panel", { type: "ELEMENT_SELECTED", componentName: currentBoundary?.componentName ?? componentName, instanceCount: merged.length, classes: currentTargetEl?.className ?? "", tailwindConfig: await fetchTailwindConfig() });
|
|
2601
|
-
return;
|
|
2602
|
-
}
|
|
3490
|
+
const targetEl = target;
|
|
3491
|
+
const classString = typeof targetEl.className === "string" ? targetEl.className : "";
|
|
3492
|
+
const result = findExactMatches(targetEl, shadowHost);
|
|
3493
|
+
const componentName = result.componentName ?? targetEl.tagName.toLowerCase();
|
|
2603
3494
|
clearHighlights();
|
|
2604
|
-
const
|
|
2605
|
-
for (const node of newNodes) {
|
|
2606
|
-
equivalentNodes.push(node);
|
|
3495
|
+
for (const node of result.exactMatch) {
|
|
2607
3496
|
highlightElement(node);
|
|
2608
3497
|
}
|
|
2609
|
-
|
|
2610
|
-
const repeated = findInlineRepeatedNodes(fiber, boundary.componentFiber);
|
|
2611
|
-
if (repeated.length > 0) {
|
|
2612
|
-
clearHighlights();
|
|
2613
|
-
equivalentNodes.length = 0;
|
|
2614
|
-
for (const node of repeated) {
|
|
2615
|
-
equivalentNodes.push(node);
|
|
2616
|
-
highlightElement(node);
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
}
|
|
2620
|
-
console.log(`[overlay] ${componentName} \u2014 ${equivalentNodes.length} highlighted`);
|
|
3498
|
+
console.log(`[overlay] ${componentName} \u2014 ${result.exactMatch.length} exact matches highlighted`);
|
|
2621
3499
|
const config = await fetchTailwindConfig();
|
|
2622
|
-
|
|
2623
|
-
const classString = targetEl.className;
|
|
2624
|
-
if (typeof classString !== "string") return;
|
|
2625
|
-
currentEquivalentNodes = equivalentNodes;
|
|
3500
|
+
currentEquivalentNodes = result.exactMatch;
|
|
2626
3501
|
currentTargetEl = targetEl;
|
|
2627
3502
|
currentBoundary = { componentName };
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
const label = domNode ? (domNode.innerText || "").trim().slice(0, 40) || `#${i + 1}` : `#${i + 1}`;
|
|
2635
|
-
const parentFiber = inst.return;
|
|
2636
|
-
const parent = parentFiber?.type?.name ?? "";
|
|
2637
|
-
return { index: i, label, parent };
|
|
2638
|
-
});
|
|
2639
|
-
} else {
|
|
2640
|
-
currentInstances = equivalentNodes.map((node, i) => ({
|
|
2641
|
-
index: i,
|
|
2642
|
-
label: (node.innerText || "").trim().slice(0, 40) || `#${i + 1}`,
|
|
2643
|
-
parent: node.parentElement?.tagName.toLowerCase() ?? ""
|
|
2644
|
-
}));
|
|
2645
|
-
}
|
|
3503
|
+
cachedNearGroups = null;
|
|
3504
|
+
currentInstances = result.exactMatch.map((node, i) => ({
|
|
3505
|
+
index: i,
|
|
3506
|
+
label: (node.innerText || "").trim().slice(0, 40) || `#${i + 1}`,
|
|
3507
|
+
parent: node.parentElement?.tagName.toLowerCase() ?? ""
|
|
3508
|
+
}));
|
|
2646
3509
|
clearHoverPreview();
|
|
2647
3510
|
setSelectMode(false);
|
|
2648
3511
|
showDrawButton(targetEl);
|
|
@@ -2653,7 +3516,7 @@ ${pad}</${tag}>`;
|
|
|
2653
3516
|
sendTo("panel", {
|
|
2654
3517
|
type: "ELEMENT_SELECTED",
|
|
2655
3518
|
componentName,
|
|
2656
|
-
instanceCount:
|
|
3519
|
+
instanceCount: result.exactMatch.length,
|
|
2657
3520
|
classes: classString,
|
|
2658
3521
|
tailwindConfig: config
|
|
2659
3522
|
});
|
|
@@ -2664,7 +3527,6 @@ ${pad}</${tag}>`;
|
|
|
2664
3527
|
document.addEventListener("click", clickHandler, { capture: true });
|
|
2665
3528
|
document.addEventListener("mousemove", mouseMoveHandler, { passive: true });
|
|
2666
3529
|
} else {
|
|
2667
|
-
addingMode = false;
|
|
2668
3530
|
document.documentElement.style.cursor = "";
|
|
2669
3531
|
document.removeEventListener("click", clickHandler, { capture: true });
|
|
2670
3532
|
document.removeEventListener("mousemove", mouseMoveHandler);
|
|
@@ -2672,16 +3534,19 @@ ${pad}</${tag}>`;
|
|
|
2672
3534
|
}
|
|
2673
3535
|
sendTo("panel", { type: "SELECT_MODE_CHANGED", active: on });
|
|
2674
3536
|
}
|
|
3537
|
+
var PANEL_OPEN_KEY = "tw-inspector-panel-open";
|
|
2675
3538
|
function toggleInspect(btn) {
|
|
2676
3539
|
active = !active;
|
|
2677
3540
|
if (active) {
|
|
2678
3541
|
btn.classList.add("active");
|
|
3542
|
+
sessionStorage.setItem(PANEL_OPEN_KEY, "1");
|
|
2679
3543
|
const panelUrl = `${SERVER_ORIGIN}/panel`;
|
|
2680
3544
|
if (!activeContainer.isOpen()) {
|
|
2681
3545
|
activeContainer.open(panelUrl);
|
|
2682
3546
|
}
|
|
2683
3547
|
} else {
|
|
2684
3548
|
btn.classList.remove("active");
|
|
3549
|
+
sessionStorage.removeItem(PANEL_OPEN_KEY);
|
|
2685
3550
|
setSelectMode(false);
|
|
2686
3551
|
activeContainer.close();
|
|
2687
3552
|
revertPreview();
|
|
@@ -2710,7 +3575,8 @@ ${pad}</${tag}>`;
|
|
|
2710
3575
|
const wrapper = document.createElement("div");
|
|
2711
3576
|
wrapper.setAttribute("data-tw-design-canvas", "true");
|
|
2712
3577
|
wrapper.style.cssText = `
|
|
2713
|
-
|
|
3578
|
+
outline: 2px dashed #00848B;
|
|
3579
|
+
outline-offset: 2px;
|
|
2714
3580
|
border-radius: 6px;
|
|
2715
3581
|
background: #FAFBFB;
|
|
2716
3582
|
position: relative;
|
|
@@ -2836,7 +3702,7 @@ ${pad}</${tag}>`;
|
|
|
2836
3702
|
default:
|
|
2837
3703
|
targetEl.insertAdjacentElement("beforebegin", wrapper);
|
|
2838
3704
|
}
|
|
2839
|
-
designCanvasWrappers.push(wrapper);
|
|
3705
|
+
designCanvasWrappers.push({ wrapper, replacedNodes: null, parent: null, anchor: null });
|
|
2840
3706
|
iframe.addEventListener("load", () => {
|
|
2841
3707
|
const contextMsg = {
|
|
2842
3708
|
type: "ELEMENT_CONTEXT",
|
|
@@ -2859,6 +3725,99 @@ ${pad}</${tag}>`;
|
|
|
2859
3725
|
setTimeout(trySend, 200);
|
|
2860
3726
|
});
|
|
2861
3727
|
}
|
|
3728
|
+
async function handleCaptureScreenshot() {
|
|
3729
|
+
if (!currentTargetEl || !currentBoundary) {
|
|
3730
|
+
showToast("Select an element first");
|
|
3731
|
+
return;
|
|
3732
|
+
}
|
|
3733
|
+
if (!areSiblings(currentEquivalentNodes)) {
|
|
3734
|
+
showToast("Screenshot & Annotate requires all selected elements to be siblings in the DOM.");
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3737
|
+
let screenshot;
|
|
3738
|
+
let screenshotWidth;
|
|
3739
|
+
let screenshotHeight;
|
|
3740
|
+
try {
|
|
3741
|
+
({ dataUrl: screenshot, width: screenshotWidth, height: screenshotHeight } = await captureRegion(currentEquivalentNodes));
|
|
3742
|
+
} catch (err) {
|
|
3743
|
+
showToast("Screenshot capture failed");
|
|
3744
|
+
console.error("[overlay] captureRegion error:", err);
|
|
3745
|
+
return;
|
|
3746
|
+
}
|
|
3747
|
+
const parent = currentEquivalentNodes[0].parentElement;
|
|
3748
|
+
const anchor = currentEquivalentNodes[0].nextSibling;
|
|
3749
|
+
const firstStyle = getComputedStyle(currentEquivalentNodes[0]);
|
|
3750
|
+
const lastStyle = getComputedStyle(currentEquivalentNodes[currentEquivalentNodes.length - 1]);
|
|
3751
|
+
const marginTop = firstStyle.marginTop;
|
|
3752
|
+
const marginBottom = lastStyle.marginBottom;
|
|
3753
|
+
const marginLeft = firstStyle.marginLeft;
|
|
3754
|
+
const marginRight = firstStyle.marginRight;
|
|
3755
|
+
const replacedNodes = [...currentEquivalentNodes];
|
|
3756
|
+
const targetEl = currentTargetEl;
|
|
3757
|
+
const boundary = currentBoundary;
|
|
3758
|
+
const instanceCount = currentEquivalentNodes.length;
|
|
3759
|
+
clearHighlights();
|
|
3760
|
+
for (const node of currentEquivalentNodes) {
|
|
3761
|
+
node.remove();
|
|
3762
|
+
}
|
|
3763
|
+
const PANEL_CHROME_HEIGHT = 40;
|
|
3764
|
+
const wrapper = document.createElement("div");
|
|
3765
|
+
wrapper.setAttribute("data-tw-design-canvas", "true");
|
|
3766
|
+
wrapper.style.cssText = `
|
|
3767
|
+
outline: 2px dashed #00848B;
|
|
3768
|
+
outline-offset: 2px;
|
|
3769
|
+
border-radius: 6px;
|
|
3770
|
+
background: #FAFBFB;
|
|
3771
|
+
position: relative;
|
|
3772
|
+
overflow: hidden;
|
|
3773
|
+
width: ${screenshotWidth}px;
|
|
3774
|
+
height: ${screenshotHeight + PANEL_CHROME_HEIGHT}px;
|
|
3775
|
+
min-width: 300px;
|
|
3776
|
+
margin-top: ${marginTop};
|
|
3777
|
+
margin-bottom: ${marginBottom};
|
|
3778
|
+
margin-left: ${marginLeft};
|
|
3779
|
+
margin-right: ${marginRight};
|
|
3780
|
+
box-shadow: 0 4px 24px rgba(0,0,0,0.15);
|
|
3781
|
+
box-sizing: border-box;
|
|
3782
|
+
`;
|
|
3783
|
+
const iframe = document.createElement("iframe");
|
|
3784
|
+
iframe.src = `${SERVER_ORIGIN}/panel/?mode=design`;
|
|
3785
|
+
iframe.style.cssText = `
|
|
3786
|
+
width: 100%;
|
|
3787
|
+
height: 100%;
|
|
3788
|
+
border: none;
|
|
3789
|
+
display: block;
|
|
3790
|
+
`;
|
|
3791
|
+
wrapper.appendChild(iframe);
|
|
3792
|
+
if (anchor) {
|
|
3793
|
+
parent.insertBefore(wrapper, anchor);
|
|
3794
|
+
} else {
|
|
3795
|
+
parent.appendChild(wrapper);
|
|
3796
|
+
}
|
|
3797
|
+
designCanvasWrappers.push({ wrapper, replacedNodes, parent, anchor });
|
|
3798
|
+
iframe.addEventListener("load", () => {
|
|
3799
|
+
const contextMsg = {
|
|
3800
|
+
type: "ELEMENT_CONTEXT",
|
|
3801
|
+
componentName: boundary.componentName,
|
|
3802
|
+
instanceCount,
|
|
3803
|
+
target: {
|
|
3804
|
+
tag: targetEl.tagName.toLowerCase(),
|
|
3805
|
+
classes: typeof targetEl.className === "string" ? targetEl.className : "",
|
|
3806
|
+
innerText: (targetEl.innerText || "").trim().slice(0, 60)
|
|
3807
|
+
},
|
|
3808
|
+
context: buildContext(targetEl, "", "", /* @__PURE__ */ new Map()),
|
|
3809
|
+
insertMode: "replace",
|
|
3810
|
+
screenshot
|
|
3811
|
+
};
|
|
3812
|
+
let attempts = 0;
|
|
3813
|
+
const trySend = () => {
|
|
3814
|
+
sendTo("design", contextMsg);
|
|
3815
|
+
attempts++;
|
|
3816
|
+
if (attempts < 5) setTimeout(trySend, 300);
|
|
3817
|
+
};
|
|
3818
|
+
setTimeout(trySend, 200);
|
|
3819
|
+
});
|
|
3820
|
+
}
|
|
2862
3821
|
function getDefaultContainer() {
|
|
2863
3822
|
try {
|
|
2864
3823
|
const stored = localStorage.getItem("tw-panel-container");
|
|
@@ -2958,8 +3917,11 @@ ${pad}</${tag}>`;
|
|
|
2958
3917
|
}
|
|
2959
3918
|
} else if (msg.type === "INSERT_DESIGN_CANVAS") {
|
|
2960
3919
|
injectDesignCanvas(msg.insertMode);
|
|
3920
|
+
} else if (msg.type === "CAPTURE_SCREENSHOT") {
|
|
3921
|
+
handleCaptureScreenshot();
|
|
2961
3922
|
} else if (msg.type === "DESIGN_SUBMITTED") {
|
|
2962
|
-
const
|
|
3923
|
+
const lastEntry = designCanvasWrappers[designCanvasWrappers.length - 1];
|
|
3924
|
+
const last = lastEntry?.wrapper;
|
|
2963
3925
|
if (last) {
|
|
2964
3926
|
const iframe = last.querySelector("iframe");
|
|
2965
3927
|
if (iframe && msg.image) {
|
|
@@ -2980,7 +3942,18 @@ ${pad}</${tag}>`;
|
|
|
2980
3942
|
}
|
|
2981
3943
|
} else if (msg.type === "DESIGN_CLOSE") {
|
|
2982
3944
|
const last = designCanvasWrappers.pop();
|
|
2983
|
-
if (last)
|
|
3945
|
+
if (last) {
|
|
3946
|
+
if (last.replacedNodes && last.parent) {
|
|
3947
|
+
for (const node of last.replacedNodes) {
|
|
3948
|
+
if (last.anchor) {
|
|
3949
|
+
last.parent.insertBefore(node, last.anchor);
|
|
3950
|
+
} else {
|
|
3951
|
+
last.parent.appendChild(node);
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
last.wrapper.remove();
|
|
3956
|
+
}
|
|
2984
3957
|
}
|
|
2985
3958
|
});
|
|
2986
3959
|
window.addEventListener("resize", () => {
|
|
@@ -2988,13 +3961,24 @@ ${pad}</${tag}>`;
|
|
|
2988
3961
|
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
2989
3962
|
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
2990
3963
|
}
|
|
3964
|
+
if (toolbarEl && currentTargetEl) {
|
|
3965
|
+
positionWithFlip(currentTargetEl, toolbarEl);
|
|
3966
|
+
}
|
|
2991
3967
|
});
|
|
2992
3968
|
window.addEventListener("scroll", () => {
|
|
2993
3969
|
if (currentEquivalentNodes.length > 0) {
|
|
2994
3970
|
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
2995
3971
|
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
2996
3972
|
}
|
|
3973
|
+
if (toolbarEl && currentTargetEl) {
|
|
3974
|
+
positionWithFlip(currentTargetEl, toolbarEl);
|
|
3975
|
+
}
|
|
2997
3976
|
}, { capture: true, passive: true });
|
|
3977
|
+
if (sessionStorage.getItem(PANEL_OPEN_KEY) === "1") {
|
|
3978
|
+
active = true;
|
|
3979
|
+
btn.classList.add("active");
|
|
3980
|
+
activeContainer.open(`${SERVER_ORIGIN}/panel`);
|
|
3981
|
+
}
|
|
2998
3982
|
window.addEventListener("overlay-ws-connected", () => {
|
|
2999
3983
|
if (wasConnected) {
|
|
3000
3984
|
showToast("Reconnected");
|