@bitovi/vybit 0.4.10 → 0.6.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 +1387 -354
- package/package.json +2 -1
- package/panel/dist/assets/DesignMode-BvwXaVHr.js +510 -0
- package/panel/dist/assets/index-BuY5a8tH.css +1 -0
- package/panel/dist/assets/index-CQNPp6e5.js +49 -0
- package/panel/dist/index.html +2 -2
- package/shared/types.ts +25 -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,48 @@ ${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);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
async function applyPreviewBatch(elements, pairs, serverOrigin) {
|
|
1519
|
+
const gen = ++previewGeneration;
|
|
1520
|
+
if (!previewState) {
|
|
1521
|
+
previewState = {
|
|
1522
|
+
elements,
|
|
1523
|
+
originalClasses: elements.map((n) => n.className)
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
const newClasses = pairs.map((p) => p.newClass).filter(Boolean);
|
|
1527
|
+
if (newClasses.length > 0) {
|
|
1528
|
+
try {
|
|
1529
|
+
const res = await fetch(`${serverOrigin}/css`, {
|
|
1530
|
+
method: "POST",
|
|
1531
|
+
headers: { "Content-Type": "application/json" },
|
|
1532
|
+
body: JSON.stringify({ classes: newClasses })
|
|
1533
|
+
});
|
|
1534
|
+
if (gen !== previewGeneration) return;
|
|
1535
|
+
const { css } = await res.json();
|
|
1536
|
+
if (gen !== previewGeneration) return;
|
|
1537
|
+
if (!previewStyleEl) {
|
|
1538
|
+
previewStyleEl = document.createElement("style");
|
|
1539
|
+
previewStyleEl.setAttribute("data-tw-preview", "");
|
|
1540
|
+
document.head.appendChild(previewStyleEl);
|
|
1541
|
+
}
|
|
1542
|
+
previewStyleEl.textContent = css;
|
|
1543
|
+
} catch {
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
if (gen !== previewGeneration) return;
|
|
1547
|
+
if (previewState) {
|
|
1548
|
+
for (let i = 0; i < previewState.elements.length; i++) {
|
|
1549
|
+
previewState.elements[i].className = previewState.originalClasses[i];
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
for (const node of elements) {
|
|
1553
|
+
for (const { oldClass, newClass } of pairs) {
|
|
1554
|
+
if (oldClass) node.classList.remove(oldClass);
|
|
1555
|
+
if (newClass) node.classList.add(newClass);
|
|
1556
|
+
}
|
|
1493
1557
|
}
|
|
1494
1558
|
}
|
|
1495
1559
|
function revertPreview() {
|
|
@@ -1548,6 +1612,7 @@ ${pad}</${tag}>`;
|
|
|
1548
1612
|
`;
|
|
1549
1613
|
const iframe = document.createElement("iframe");
|
|
1550
1614
|
iframe.src = panelUrl;
|
|
1615
|
+
iframe.allow = "microphone";
|
|
1551
1616
|
iframe.style.cssText = "width:100%; height:100%; border:none;";
|
|
1552
1617
|
host.appendChild(iframe);
|
|
1553
1618
|
this.shadowRoot.appendChild(host);
|
|
@@ -1618,6 +1683,7 @@ ${pad}</${tag}>`;
|
|
|
1618
1683
|
host.appendChild(handle);
|
|
1619
1684
|
const iframe = document.createElement("iframe");
|
|
1620
1685
|
iframe.src = panelUrl;
|
|
1686
|
+
iframe.allow = "microphone";
|
|
1621
1687
|
iframe.style.cssText = "flex:1; border:none; width:100%;";
|
|
1622
1688
|
host.appendChild(iframe);
|
|
1623
1689
|
const gripper = document.createElement("div");
|
|
@@ -1771,6 +1837,7 @@ ${pad}</${tag}>`;
|
|
|
1771
1837
|
host.appendChild(resizeHandle);
|
|
1772
1838
|
const iframe = document.createElement("iframe");
|
|
1773
1839
|
iframe.src = panelUrl;
|
|
1840
|
+
iframe.allow = "microphone";
|
|
1774
1841
|
iframe.style.cssText = "flex:1; border:none; height:100%;";
|
|
1775
1842
|
host.appendChild(iframe);
|
|
1776
1843
|
this.shadowRoot.appendChild(host);
|
|
@@ -1842,6 +1909,845 @@ ${pad}</${tag}>`;
|
|
|
1842
1909
|
}
|
|
1843
1910
|
};
|
|
1844
1911
|
|
|
1912
|
+
// node_modules/html-to-image/es/util.js
|
|
1913
|
+
function resolveUrl(url, baseUrl) {
|
|
1914
|
+
if (url.match(/^[a-z]+:\/\//i)) {
|
|
1915
|
+
return url;
|
|
1916
|
+
}
|
|
1917
|
+
if (url.match(/^\/\//)) {
|
|
1918
|
+
return window.location.protocol + url;
|
|
1919
|
+
}
|
|
1920
|
+
if (url.match(/^[a-z]+:/i)) {
|
|
1921
|
+
return url;
|
|
1922
|
+
}
|
|
1923
|
+
const doc = document.implementation.createHTMLDocument();
|
|
1924
|
+
const base = doc.createElement("base");
|
|
1925
|
+
const a = doc.createElement("a");
|
|
1926
|
+
doc.head.appendChild(base);
|
|
1927
|
+
doc.body.appendChild(a);
|
|
1928
|
+
if (baseUrl) {
|
|
1929
|
+
base.href = baseUrl;
|
|
1930
|
+
}
|
|
1931
|
+
a.href = url;
|
|
1932
|
+
return a.href;
|
|
1933
|
+
}
|
|
1934
|
+
var uuid = /* @__PURE__ */ (() => {
|
|
1935
|
+
let counter = 0;
|
|
1936
|
+
const random = () => (
|
|
1937
|
+
// eslint-disable-next-line no-bitwise
|
|
1938
|
+
`0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
|
|
1939
|
+
);
|
|
1940
|
+
return () => {
|
|
1941
|
+
counter += 1;
|
|
1942
|
+
return `u${random()}${counter}`;
|
|
1943
|
+
};
|
|
1944
|
+
})();
|
|
1945
|
+
function toArray(arrayLike) {
|
|
1946
|
+
const arr = [];
|
|
1947
|
+
for (let i = 0, l = arrayLike.length; i < l; i++) {
|
|
1948
|
+
arr.push(arrayLike[i]);
|
|
1949
|
+
}
|
|
1950
|
+
return arr;
|
|
1951
|
+
}
|
|
1952
|
+
var styleProps = null;
|
|
1953
|
+
function getStyleProperties(options = {}) {
|
|
1954
|
+
if (styleProps) {
|
|
1955
|
+
return styleProps;
|
|
1956
|
+
}
|
|
1957
|
+
if (options.includeStyleProperties) {
|
|
1958
|
+
styleProps = options.includeStyleProperties;
|
|
1959
|
+
return styleProps;
|
|
1960
|
+
}
|
|
1961
|
+
styleProps = toArray(window.getComputedStyle(document.documentElement));
|
|
1962
|
+
return styleProps;
|
|
1963
|
+
}
|
|
1964
|
+
function px(node, styleProperty) {
|
|
1965
|
+
const win = node.ownerDocument.defaultView || window;
|
|
1966
|
+
const val = win.getComputedStyle(node).getPropertyValue(styleProperty);
|
|
1967
|
+
return val ? parseFloat(val.replace("px", "")) : 0;
|
|
1968
|
+
}
|
|
1969
|
+
function getNodeWidth(node) {
|
|
1970
|
+
const leftBorder = px(node, "border-left-width");
|
|
1971
|
+
const rightBorder = px(node, "border-right-width");
|
|
1972
|
+
return node.clientWidth + leftBorder + rightBorder;
|
|
1973
|
+
}
|
|
1974
|
+
function getNodeHeight(node) {
|
|
1975
|
+
const topBorder = px(node, "border-top-width");
|
|
1976
|
+
const bottomBorder = px(node, "border-bottom-width");
|
|
1977
|
+
return node.clientHeight + topBorder + bottomBorder;
|
|
1978
|
+
}
|
|
1979
|
+
function getImageSize(targetNode, options = {}) {
|
|
1980
|
+
const width = options.width || getNodeWidth(targetNode);
|
|
1981
|
+
const height = options.height || getNodeHeight(targetNode);
|
|
1982
|
+
return { width, height };
|
|
1983
|
+
}
|
|
1984
|
+
function getPixelRatio() {
|
|
1985
|
+
let ratio;
|
|
1986
|
+
let FINAL_PROCESS;
|
|
1987
|
+
try {
|
|
1988
|
+
FINAL_PROCESS = process;
|
|
1989
|
+
} catch (e) {
|
|
1990
|
+
}
|
|
1991
|
+
const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
|
|
1992
|
+
if (val) {
|
|
1993
|
+
ratio = parseInt(val, 10);
|
|
1994
|
+
if (Number.isNaN(ratio)) {
|
|
1995
|
+
ratio = 1;
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return ratio || window.devicePixelRatio || 1;
|
|
1999
|
+
}
|
|
2000
|
+
var canvasDimensionLimit = 16384;
|
|
2001
|
+
function checkCanvasDimensions(canvas) {
|
|
2002
|
+
if (canvas.width > canvasDimensionLimit || canvas.height > canvasDimensionLimit) {
|
|
2003
|
+
if (canvas.width > canvasDimensionLimit && canvas.height > canvasDimensionLimit) {
|
|
2004
|
+
if (canvas.width > canvas.height) {
|
|
2005
|
+
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
2006
|
+
canvas.width = canvasDimensionLimit;
|
|
2007
|
+
} else {
|
|
2008
|
+
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
2009
|
+
canvas.height = canvasDimensionLimit;
|
|
2010
|
+
}
|
|
2011
|
+
} else if (canvas.width > canvasDimensionLimit) {
|
|
2012
|
+
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
2013
|
+
canvas.width = canvasDimensionLimit;
|
|
2014
|
+
} else {
|
|
2015
|
+
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
2016
|
+
canvas.height = canvasDimensionLimit;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
function createImage(url) {
|
|
2021
|
+
return new Promise((resolve, reject) => {
|
|
2022
|
+
const img = new Image();
|
|
2023
|
+
img.onload = () => {
|
|
2024
|
+
img.decode().then(() => {
|
|
2025
|
+
requestAnimationFrame(() => resolve(img));
|
|
2026
|
+
});
|
|
2027
|
+
};
|
|
2028
|
+
img.onerror = reject;
|
|
2029
|
+
img.crossOrigin = "anonymous";
|
|
2030
|
+
img.decoding = "async";
|
|
2031
|
+
img.src = url;
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
async function svgToDataURL(svg) {
|
|
2035
|
+
return Promise.resolve().then(() => new XMLSerializer().serializeToString(svg)).then(encodeURIComponent).then((html) => `data:image/svg+xml;charset=utf-8,${html}`);
|
|
2036
|
+
}
|
|
2037
|
+
async function nodeToDataURL(node, width, height) {
|
|
2038
|
+
const xmlns = "http://www.w3.org/2000/svg";
|
|
2039
|
+
const svg = document.createElementNS(xmlns, "svg");
|
|
2040
|
+
const foreignObject = document.createElementNS(xmlns, "foreignObject");
|
|
2041
|
+
svg.setAttribute("width", `${width}`);
|
|
2042
|
+
svg.setAttribute("height", `${height}`);
|
|
2043
|
+
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
2044
|
+
foreignObject.setAttribute("width", "100%");
|
|
2045
|
+
foreignObject.setAttribute("height", "100%");
|
|
2046
|
+
foreignObject.setAttribute("x", "0");
|
|
2047
|
+
foreignObject.setAttribute("y", "0");
|
|
2048
|
+
foreignObject.setAttribute("externalResourcesRequired", "true");
|
|
2049
|
+
svg.appendChild(foreignObject);
|
|
2050
|
+
foreignObject.appendChild(node);
|
|
2051
|
+
return svgToDataURL(svg);
|
|
2052
|
+
}
|
|
2053
|
+
var isInstanceOfElement = (node, instance) => {
|
|
2054
|
+
if (node instanceof instance)
|
|
2055
|
+
return true;
|
|
2056
|
+
const nodePrototype = Object.getPrototypeOf(node);
|
|
2057
|
+
if (nodePrototype === null)
|
|
2058
|
+
return false;
|
|
2059
|
+
return nodePrototype.constructor.name === instance.name || isInstanceOfElement(nodePrototype, instance);
|
|
2060
|
+
};
|
|
2061
|
+
|
|
2062
|
+
// node_modules/html-to-image/es/clone-pseudos.js
|
|
2063
|
+
function formatCSSText(style) {
|
|
2064
|
+
const content = style.getPropertyValue("content");
|
|
2065
|
+
return `${style.cssText} content: '${content.replace(/'|"/g, "")}';`;
|
|
2066
|
+
}
|
|
2067
|
+
function formatCSSProperties(style, options) {
|
|
2068
|
+
return getStyleProperties(options).map((name) => {
|
|
2069
|
+
const value = style.getPropertyValue(name);
|
|
2070
|
+
const priority = style.getPropertyPriority(name);
|
|
2071
|
+
return `${name}: ${value}${priority ? " !important" : ""};`;
|
|
2072
|
+
}).join(" ");
|
|
2073
|
+
}
|
|
2074
|
+
function getPseudoElementStyle(className, pseudo, style, options) {
|
|
2075
|
+
const selector = `.${className}:${pseudo}`;
|
|
2076
|
+
const cssText = style.cssText ? formatCSSText(style) : formatCSSProperties(style, options);
|
|
2077
|
+
return document.createTextNode(`${selector}{${cssText}}`);
|
|
2078
|
+
}
|
|
2079
|
+
function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
|
|
2080
|
+
const style = window.getComputedStyle(nativeNode, pseudo);
|
|
2081
|
+
const content = style.getPropertyValue("content");
|
|
2082
|
+
if (content === "" || content === "none") {
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
const className = uuid();
|
|
2086
|
+
try {
|
|
2087
|
+
clonedNode.className = `${clonedNode.className} ${className}`;
|
|
2088
|
+
} catch (err) {
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
const styleElement = document.createElement("style");
|
|
2092
|
+
styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));
|
|
2093
|
+
clonedNode.appendChild(styleElement);
|
|
2094
|
+
}
|
|
2095
|
+
function clonePseudoElements(nativeNode, clonedNode, options) {
|
|
2096
|
+
clonePseudoElement(nativeNode, clonedNode, ":before", options);
|
|
2097
|
+
clonePseudoElement(nativeNode, clonedNode, ":after", options);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// node_modules/html-to-image/es/mimes.js
|
|
2101
|
+
var WOFF = "application/font-woff";
|
|
2102
|
+
var JPEG = "image/jpeg";
|
|
2103
|
+
var mimes = {
|
|
2104
|
+
woff: WOFF,
|
|
2105
|
+
woff2: WOFF,
|
|
2106
|
+
ttf: "application/font-truetype",
|
|
2107
|
+
eot: "application/vnd.ms-fontobject",
|
|
2108
|
+
png: "image/png",
|
|
2109
|
+
jpg: JPEG,
|
|
2110
|
+
jpeg: JPEG,
|
|
2111
|
+
gif: "image/gif",
|
|
2112
|
+
tiff: "image/tiff",
|
|
2113
|
+
svg: "image/svg+xml",
|
|
2114
|
+
webp: "image/webp"
|
|
2115
|
+
};
|
|
2116
|
+
function getExtension(url) {
|
|
2117
|
+
const match = /\.([^./]*?)$/g.exec(url);
|
|
2118
|
+
return match ? match[1] : "";
|
|
2119
|
+
}
|
|
2120
|
+
function getMimeType(url) {
|
|
2121
|
+
const extension = getExtension(url).toLowerCase();
|
|
2122
|
+
return mimes[extension] || "";
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
// node_modules/html-to-image/es/dataurl.js
|
|
2126
|
+
function getContentFromDataUrl(dataURL) {
|
|
2127
|
+
return dataURL.split(/,/)[1];
|
|
2128
|
+
}
|
|
2129
|
+
function isDataUrl(url) {
|
|
2130
|
+
return url.search(/^(data:)/) !== -1;
|
|
2131
|
+
}
|
|
2132
|
+
function makeDataUrl(content, mimeType) {
|
|
2133
|
+
return `data:${mimeType};base64,${content}`;
|
|
2134
|
+
}
|
|
2135
|
+
async function fetchAsDataURL(url, init2, process2) {
|
|
2136
|
+
const res = await fetch(url, init2);
|
|
2137
|
+
if (res.status === 404) {
|
|
2138
|
+
throw new Error(`Resource "${res.url}" not found`);
|
|
2139
|
+
}
|
|
2140
|
+
const blob = await res.blob();
|
|
2141
|
+
return new Promise((resolve, reject) => {
|
|
2142
|
+
const reader = new FileReader();
|
|
2143
|
+
reader.onerror = reject;
|
|
2144
|
+
reader.onloadend = () => {
|
|
2145
|
+
try {
|
|
2146
|
+
resolve(process2({ res, result: reader.result }));
|
|
2147
|
+
} catch (error) {
|
|
2148
|
+
reject(error);
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
reader.readAsDataURL(blob);
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
var cache = {};
|
|
2155
|
+
function getCacheKey(url, contentType, includeQueryParams) {
|
|
2156
|
+
let key = url.replace(/\?.*/, "");
|
|
2157
|
+
if (includeQueryParams) {
|
|
2158
|
+
key = url;
|
|
2159
|
+
}
|
|
2160
|
+
if (/ttf|otf|eot|woff2?/i.test(key)) {
|
|
2161
|
+
key = key.replace(/.*\//, "");
|
|
2162
|
+
}
|
|
2163
|
+
return contentType ? `[${contentType}]${key}` : key;
|
|
2164
|
+
}
|
|
2165
|
+
async function resourceToDataURL(resourceUrl, contentType, options) {
|
|
2166
|
+
const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);
|
|
2167
|
+
if (cache[cacheKey] != null) {
|
|
2168
|
+
return cache[cacheKey];
|
|
2169
|
+
}
|
|
2170
|
+
if (options.cacheBust) {
|
|
2171
|
+
resourceUrl += (/\?/.test(resourceUrl) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
|
|
2172
|
+
}
|
|
2173
|
+
let dataURL;
|
|
2174
|
+
try {
|
|
2175
|
+
const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result }) => {
|
|
2176
|
+
if (!contentType) {
|
|
2177
|
+
contentType = res.headers.get("Content-Type") || "";
|
|
2178
|
+
}
|
|
2179
|
+
return getContentFromDataUrl(result);
|
|
2180
|
+
});
|
|
2181
|
+
dataURL = makeDataUrl(content, contentType);
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
dataURL = options.imagePlaceholder || "";
|
|
2184
|
+
let msg = `Failed to fetch resource: ${resourceUrl}`;
|
|
2185
|
+
if (error) {
|
|
2186
|
+
msg = typeof error === "string" ? error : error.message;
|
|
2187
|
+
}
|
|
2188
|
+
if (msg) {
|
|
2189
|
+
console.warn(msg);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
cache[cacheKey] = dataURL;
|
|
2193
|
+
return dataURL;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// node_modules/html-to-image/es/clone-node.js
|
|
2197
|
+
async function cloneCanvasElement(canvas) {
|
|
2198
|
+
const dataURL = canvas.toDataURL();
|
|
2199
|
+
if (dataURL === "data:,") {
|
|
2200
|
+
return canvas.cloneNode(false);
|
|
2201
|
+
}
|
|
2202
|
+
return createImage(dataURL);
|
|
2203
|
+
}
|
|
2204
|
+
async function cloneVideoElement(video, options) {
|
|
2205
|
+
if (video.currentSrc) {
|
|
2206
|
+
const canvas = document.createElement("canvas");
|
|
2207
|
+
const ctx = canvas.getContext("2d");
|
|
2208
|
+
canvas.width = video.clientWidth;
|
|
2209
|
+
canvas.height = video.clientHeight;
|
|
2210
|
+
ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
2211
|
+
const dataURL2 = canvas.toDataURL();
|
|
2212
|
+
return createImage(dataURL2);
|
|
2213
|
+
}
|
|
2214
|
+
const poster = video.poster;
|
|
2215
|
+
const contentType = getMimeType(poster);
|
|
2216
|
+
const dataURL = await resourceToDataURL(poster, contentType, options);
|
|
2217
|
+
return createImage(dataURL);
|
|
2218
|
+
}
|
|
2219
|
+
async function cloneIFrameElement(iframe, options) {
|
|
2220
|
+
var _a;
|
|
2221
|
+
try {
|
|
2222
|
+
if ((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.body) {
|
|
2223
|
+
return await cloneNode(iframe.contentDocument.body, options, true);
|
|
2224
|
+
}
|
|
2225
|
+
} catch (_b) {
|
|
2226
|
+
}
|
|
2227
|
+
return iframe.cloneNode(false);
|
|
2228
|
+
}
|
|
2229
|
+
async function cloneSingleNode(node, options) {
|
|
2230
|
+
if (isInstanceOfElement(node, HTMLCanvasElement)) {
|
|
2231
|
+
return cloneCanvasElement(node);
|
|
2232
|
+
}
|
|
2233
|
+
if (isInstanceOfElement(node, HTMLVideoElement)) {
|
|
2234
|
+
return cloneVideoElement(node, options);
|
|
2235
|
+
}
|
|
2236
|
+
if (isInstanceOfElement(node, HTMLIFrameElement)) {
|
|
2237
|
+
return cloneIFrameElement(node, options);
|
|
2238
|
+
}
|
|
2239
|
+
return node.cloneNode(isSVGElement(node));
|
|
2240
|
+
}
|
|
2241
|
+
var isSlotElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SLOT";
|
|
2242
|
+
var isSVGElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SVG";
|
|
2243
|
+
async function cloneChildren(nativeNode, clonedNode, options) {
|
|
2244
|
+
var _a, _b;
|
|
2245
|
+
if (isSVGElement(clonedNode)) {
|
|
2246
|
+
return clonedNode;
|
|
2247
|
+
}
|
|
2248
|
+
let children = [];
|
|
2249
|
+
if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {
|
|
2250
|
+
children = toArray(nativeNode.assignedNodes());
|
|
2251
|
+
} else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {
|
|
2252
|
+
children = toArray(nativeNode.contentDocument.body.childNodes);
|
|
2253
|
+
} else {
|
|
2254
|
+
children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
|
|
2255
|
+
}
|
|
2256
|
+
if (children.length === 0 || isInstanceOfElement(nativeNode, HTMLVideoElement)) {
|
|
2257
|
+
return clonedNode;
|
|
2258
|
+
}
|
|
2259
|
+
await children.reduce((deferred, child) => deferred.then(() => cloneNode(child, options)).then((clonedChild) => {
|
|
2260
|
+
if (clonedChild) {
|
|
2261
|
+
clonedNode.appendChild(clonedChild);
|
|
2262
|
+
}
|
|
2263
|
+
}), Promise.resolve());
|
|
2264
|
+
return clonedNode;
|
|
2265
|
+
}
|
|
2266
|
+
function cloneCSSStyle(nativeNode, clonedNode, options) {
|
|
2267
|
+
const targetStyle = clonedNode.style;
|
|
2268
|
+
if (!targetStyle) {
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
2272
|
+
if (sourceStyle.cssText) {
|
|
2273
|
+
targetStyle.cssText = sourceStyle.cssText;
|
|
2274
|
+
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
2275
|
+
} else {
|
|
2276
|
+
getStyleProperties(options).forEach((name) => {
|
|
2277
|
+
let value = sourceStyle.getPropertyValue(name);
|
|
2278
|
+
if (name === "font-size" && value.endsWith("px")) {
|
|
2279
|
+
const reducedFont = Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1;
|
|
2280
|
+
value = `${reducedFont}px`;
|
|
2281
|
+
}
|
|
2282
|
+
if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && name === "display" && value === "inline") {
|
|
2283
|
+
value = "block";
|
|
2284
|
+
}
|
|
2285
|
+
if (name === "d" && clonedNode.getAttribute("d")) {
|
|
2286
|
+
value = `path(${clonedNode.getAttribute("d")})`;
|
|
2287
|
+
}
|
|
2288
|
+
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
function cloneInputValue(nativeNode, clonedNode) {
|
|
2293
|
+
if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {
|
|
2294
|
+
clonedNode.innerHTML = nativeNode.value;
|
|
2295
|
+
}
|
|
2296
|
+
if (isInstanceOfElement(nativeNode, HTMLInputElement)) {
|
|
2297
|
+
clonedNode.setAttribute("value", nativeNode.value);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
function cloneSelectValue(nativeNode, clonedNode) {
|
|
2301
|
+
if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {
|
|
2302
|
+
const clonedSelect = clonedNode;
|
|
2303
|
+
const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute("value"));
|
|
2304
|
+
if (selectedOption) {
|
|
2305
|
+
selectedOption.setAttribute("selected", "");
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function decorate(nativeNode, clonedNode, options) {
|
|
2310
|
+
if (isInstanceOfElement(clonedNode, Element)) {
|
|
2311
|
+
cloneCSSStyle(nativeNode, clonedNode, options);
|
|
2312
|
+
clonePseudoElements(nativeNode, clonedNode, options);
|
|
2313
|
+
cloneInputValue(nativeNode, clonedNode);
|
|
2314
|
+
cloneSelectValue(nativeNode, clonedNode);
|
|
2315
|
+
}
|
|
2316
|
+
return clonedNode;
|
|
2317
|
+
}
|
|
2318
|
+
async function ensureSVGSymbols(clone, options) {
|
|
2319
|
+
const uses = clone.querySelectorAll ? clone.querySelectorAll("use") : [];
|
|
2320
|
+
if (uses.length === 0) {
|
|
2321
|
+
return clone;
|
|
2322
|
+
}
|
|
2323
|
+
const processedDefs = {};
|
|
2324
|
+
for (let i = 0; i < uses.length; i++) {
|
|
2325
|
+
const use = uses[i];
|
|
2326
|
+
const id = use.getAttribute("xlink:href");
|
|
2327
|
+
if (id) {
|
|
2328
|
+
const exist = clone.querySelector(id);
|
|
2329
|
+
const definition = document.querySelector(id);
|
|
2330
|
+
if (!exist && definition && !processedDefs[id]) {
|
|
2331
|
+
processedDefs[id] = await cloneNode(definition, options, true);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
const nodes = Object.values(processedDefs);
|
|
2336
|
+
if (nodes.length) {
|
|
2337
|
+
const ns = "http://www.w3.org/1999/xhtml";
|
|
2338
|
+
const svg = document.createElementNS(ns, "svg");
|
|
2339
|
+
svg.setAttribute("xmlns", ns);
|
|
2340
|
+
svg.style.position = "absolute";
|
|
2341
|
+
svg.style.width = "0";
|
|
2342
|
+
svg.style.height = "0";
|
|
2343
|
+
svg.style.overflow = "hidden";
|
|
2344
|
+
svg.style.display = "none";
|
|
2345
|
+
const defs = document.createElementNS(ns, "defs");
|
|
2346
|
+
svg.appendChild(defs);
|
|
2347
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
2348
|
+
defs.appendChild(nodes[i]);
|
|
2349
|
+
}
|
|
2350
|
+
clone.appendChild(svg);
|
|
2351
|
+
}
|
|
2352
|
+
return clone;
|
|
2353
|
+
}
|
|
2354
|
+
async function cloneNode(node, options, isRoot) {
|
|
2355
|
+
if (!isRoot && options.filter && !options.filter(node)) {
|
|
2356
|
+
return null;
|
|
2357
|
+
}
|
|
2358
|
+
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));
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
// node_modules/html-to-image/es/embed-resources.js
|
|
2362
|
+
var URL_REGEX = /url\((['"]?)([^'"]+?)\1\)/g;
|
|
2363
|
+
var URL_WITH_FORMAT_REGEX = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
|
|
2364
|
+
var FONT_SRC_REGEX = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
|
|
2365
|
+
function toRegex(url) {
|
|
2366
|
+
const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
|
2367
|
+
return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
|
|
2368
|
+
}
|
|
2369
|
+
function parseURLs(cssText) {
|
|
2370
|
+
const urls = [];
|
|
2371
|
+
cssText.replace(URL_REGEX, (raw, quotation, url) => {
|
|
2372
|
+
urls.push(url);
|
|
2373
|
+
return raw;
|
|
2374
|
+
});
|
|
2375
|
+
return urls.filter((url) => !isDataUrl(url));
|
|
2376
|
+
}
|
|
2377
|
+
async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {
|
|
2378
|
+
try {
|
|
2379
|
+
const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;
|
|
2380
|
+
const contentType = getMimeType(resourceURL);
|
|
2381
|
+
let dataURL;
|
|
2382
|
+
if (getContentFromUrl) {
|
|
2383
|
+
const content = await getContentFromUrl(resolvedURL);
|
|
2384
|
+
dataURL = makeDataUrl(content, contentType);
|
|
2385
|
+
} else {
|
|
2386
|
+
dataURL = await resourceToDataURL(resolvedURL, contentType, options);
|
|
2387
|
+
}
|
|
2388
|
+
return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);
|
|
2389
|
+
} catch (error) {
|
|
2390
|
+
}
|
|
2391
|
+
return cssText;
|
|
2392
|
+
}
|
|
2393
|
+
function filterPreferredFontFormat(str, { preferredFontFormat }) {
|
|
2394
|
+
return !preferredFontFormat ? str : str.replace(FONT_SRC_REGEX, (match) => {
|
|
2395
|
+
while (true) {
|
|
2396
|
+
const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match) || [];
|
|
2397
|
+
if (!format) {
|
|
2398
|
+
return "";
|
|
2399
|
+
}
|
|
2400
|
+
if (format === preferredFontFormat) {
|
|
2401
|
+
return `src: ${src};`;
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
function shouldEmbed(url) {
|
|
2407
|
+
return url.search(URL_REGEX) !== -1;
|
|
2408
|
+
}
|
|
2409
|
+
async function embedResources(cssText, baseUrl, options) {
|
|
2410
|
+
if (!shouldEmbed(cssText)) {
|
|
2411
|
+
return cssText;
|
|
2412
|
+
}
|
|
2413
|
+
const filteredCSSText = filterPreferredFontFormat(cssText, options);
|
|
2414
|
+
const urls = parseURLs(filteredCSSText);
|
|
2415
|
+
return urls.reduce((deferred, url) => deferred.then((css) => embed(css, url, baseUrl, options)), Promise.resolve(filteredCSSText));
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// node_modules/html-to-image/es/embed-images.js
|
|
2419
|
+
async function embedProp(propName, node, options) {
|
|
2420
|
+
var _a;
|
|
2421
|
+
const propValue = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propName);
|
|
2422
|
+
if (propValue) {
|
|
2423
|
+
const cssString = await embedResources(propValue, null, options);
|
|
2424
|
+
node.style.setProperty(propName, cssString, node.style.getPropertyPriority(propName));
|
|
2425
|
+
return true;
|
|
2426
|
+
}
|
|
2427
|
+
return false;
|
|
2428
|
+
}
|
|
2429
|
+
async function embedBackground(clonedNode, options) {
|
|
2430
|
+
;
|
|
2431
|
+
await embedProp("background", clonedNode, options) || await embedProp("background-image", clonedNode, options);
|
|
2432
|
+
await embedProp("mask", clonedNode, options) || await embedProp("-webkit-mask", clonedNode, options) || await embedProp("mask-image", clonedNode, options) || await embedProp("-webkit-mask-image", clonedNode, options);
|
|
2433
|
+
}
|
|
2434
|
+
async function embedImageNode(clonedNode, options) {
|
|
2435
|
+
const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);
|
|
2436
|
+
if (!(isImageElement && !isDataUrl(clonedNode.src)) && !(isInstanceOfElement(clonedNode, SVGImageElement) && !isDataUrl(clonedNode.href.baseVal))) {
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal;
|
|
2440
|
+
const dataURL = await resourceToDataURL(url, getMimeType(url), options);
|
|
2441
|
+
await new Promise((resolve, reject) => {
|
|
2442
|
+
clonedNode.onload = resolve;
|
|
2443
|
+
clonedNode.onerror = options.onImageErrorHandler ? (...attributes) => {
|
|
2444
|
+
try {
|
|
2445
|
+
resolve(options.onImageErrorHandler(...attributes));
|
|
2446
|
+
} catch (error) {
|
|
2447
|
+
reject(error);
|
|
2448
|
+
}
|
|
2449
|
+
} : reject;
|
|
2450
|
+
const image = clonedNode;
|
|
2451
|
+
if (image.decode) {
|
|
2452
|
+
image.decode = resolve;
|
|
2453
|
+
}
|
|
2454
|
+
if (image.loading === "lazy") {
|
|
2455
|
+
image.loading = "eager";
|
|
2456
|
+
}
|
|
2457
|
+
if (isImageElement) {
|
|
2458
|
+
clonedNode.srcset = "";
|
|
2459
|
+
clonedNode.src = dataURL;
|
|
2460
|
+
} else {
|
|
2461
|
+
clonedNode.href.baseVal = dataURL;
|
|
2462
|
+
}
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
async function embedChildren(clonedNode, options) {
|
|
2466
|
+
const children = toArray(clonedNode.childNodes);
|
|
2467
|
+
const deferreds = children.map((child) => embedImages(child, options));
|
|
2468
|
+
await Promise.all(deferreds).then(() => clonedNode);
|
|
2469
|
+
}
|
|
2470
|
+
async function embedImages(clonedNode, options) {
|
|
2471
|
+
if (isInstanceOfElement(clonedNode, Element)) {
|
|
2472
|
+
await embedBackground(clonedNode, options);
|
|
2473
|
+
await embedImageNode(clonedNode, options);
|
|
2474
|
+
await embedChildren(clonedNode, options);
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// node_modules/html-to-image/es/apply-style.js
|
|
2479
|
+
function applyStyle(node, options) {
|
|
2480
|
+
const { style } = node;
|
|
2481
|
+
if (options.backgroundColor) {
|
|
2482
|
+
style.backgroundColor = options.backgroundColor;
|
|
2483
|
+
}
|
|
2484
|
+
if (options.width) {
|
|
2485
|
+
style.width = `${options.width}px`;
|
|
2486
|
+
}
|
|
2487
|
+
if (options.height) {
|
|
2488
|
+
style.height = `${options.height}px`;
|
|
2489
|
+
}
|
|
2490
|
+
const manual = options.style;
|
|
2491
|
+
if (manual != null) {
|
|
2492
|
+
Object.keys(manual).forEach((key) => {
|
|
2493
|
+
style[key] = manual[key];
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
return node;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// node_modules/html-to-image/es/embed-webfonts.js
|
|
2500
|
+
var cssFetchCache = {};
|
|
2501
|
+
async function fetchCSS(url) {
|
|
2502
|
+
let cache2 = cssFetchCache[url];
|
|
2503
|
+
if (cache2 != null) {
|
|
2504
|
+
return cache2;
|
|
2505
|
+
}
|
|
2506
|
+
const res = await fetch(url);
|
|
2507
|
+
const cssText = await res.text();
|
|
2508
|
+
cache2 = { url, cssText };
|
|
2509
|
+
cssFetchCache[url] = cache2;
|
|
2510
|
+
return cache2;
|
|
2511
|
+
}
|
|
2512
|
+
async function embedFonts(data, options) {
|
|
2513
|
+
let cssText = data.cssText;
|
|
2514
|
+
const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
|
|
2515
|
+
const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
|
|
2516
|
+
const loadFonts = fontLocs.map(async (loc) => {
|
|
2517
|
+
let url = loc.replace(regexUrl, "$1");
|
|
2518
|
+
if (!url.startsWith("https://")) {
|
|
2519
|
+
url = new URL(url, data.url).href;
|
|
2520
|
+
}
|
|
2521
|
+
return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {
|
|
2522
|
+
cssText = cssText.replace(loc, `url(${result})`);
|
|
2523
|
+
return [loc, result];
|
|
2524
|
+
});
|
|
2525
|
+
});
|
|
2526
|
+
return Promise.all(loadFonts).then(() => cssText);
|
|
2527
|
+
}
|
|
2528
|
+
function parseCSS(source) {
|
|
2529
|
+
if (source == null) {
|
|
2530
|
+
return [];
|
|
2531
|
+
}
|
|
2532
|
+
const result = [];
|
|
2533
|
+
const commentsRegex = /(\/\*[\s\S]*?\*\/)/gi;
|
|
2534
|
+
let cssText = source.replace(commentsRegex, "");
|
|
2535
|
+
const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
|
|
2536
|
+
while (true) {
|
|
2537
|
+
const matches = keyframesRegex.exec(cssText);
|
|
2538
|
+
if (matches === null) {
|
|
2539
|
+
break;
|
|
2540
|
+
}
|
|
2541
|
+
result.push(matches[0]);
|
|
2542
|
+
}
|
|
2543
|
+
cssText = cssText.replace(keyframesRegex, "");
|
|
2544
|
+
const importRegex = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
|
|
2545
|
+
const combinedCSSRegex = "((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})";
|
|
2546
|
+
const unifiedRegex = new RegExp(combinedCSSRegex, "gi");
|
|
2547
|
+
while (true) {
|
|
2548
|
+
let matches = importRegex.exec(cssText);
|
|
2549
|
+
if (matches === null) {
|
|
2550
|
+
matches = unifiedRegex.exec(cssText);
|
|
2551
|
+
if (matches === null) {
|
|
2552
|
+
break;
|
|
2553
|
+
} else {
|
|
2554
|
+
importRegex.lastIndex = unifiedRegex.lastIndex;
|
|
2555
|
+
}
|
|
2556
|
+
} else {
|
|
2557
|
+
unifiedRegex.lastIndex = importRegex.lastIndex;
|
|
2558
|
+
}
|
|
2559
|
+
result.push(matches[0]);
|
|
2560
|
+
}
|
|
2561
|
+
return result;
|
|
2562
|
+
}
|
|
2563
|
+
async function getCSSRules(styleSheets, options) {
|
|
2564
|
+
const ret = [];
|
|
2565
|
+
const deferreds = [];
|
|
2566
|
+
styleSheets.forEach((sheet) => {
|
|
2567
|
+
if ("cssRules" in sheet) {
|
|
2568
|
+
try {
|
|
2569
|
+
toArray(sheet.cssRules || []).forEach((item, index) => {
|
|
2570
|
+
if (item.type === CSSRule.IMPORT_RULE) {
|
|
2571
|
+
let importIndex = index + 1;
|
|
2572
|
+
const url = item.href;
|
|
2573
|
+
const deferred = fetchCSS(url).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
2574
|
+
try {
|
|
2575
|
+
sheet.insertRule(rule, rule.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
|
|
2576
|
+
} catch (error) {
|
|
2577
|
+
console.error("Error inserting rule from remote css", {
|
|
2578
|
+
rule,
|
|
2579
|
+
error
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
})).catch((e) => {
|
|
2583
|
+
console.error("Error loading remote css", e.toString());
|
|
2584
|
+
});
|
|
2585
|
+
deferreds.push(deferred);
|
|
2586
|
+
}
|
|
2587
|
+
});
|
|
2588
|
+
} catch (e) {
|
|
2589
|
+
const inline2 = styleSheets.find((a) => a.href == null) || document.styleSheets[0];
|
|
2590
|
+
if (sheet.href != null) {
|
|
2591
|
+
deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
2592
|
+
inline2.insertRule(rule, inline2.cssRules.length);
|
|
2593
|
+
})).catch((err) => {
|
|
2594
|
+
console.error("Error loading remote stylesheet", err);
|
|
2595
|
+
}));
|
|
2596
|
+
}
|
|
2597
|
+
console.error("Error inlining remote css file", e);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
return Promise.all(deferreds).then(() => {
|
|
2602
|
+
styleSheets.forEach((sheet) => {
|
|
2603
|
+
if ("cssRules" in sheet) {
|
|
2604
|
+
try {
|
|
2605
|
+
toArray(sheet.cssRules || []).forEach((item) => {
|
|
2606
|
+
ret.push(item);
|
|
2607
|
+
});
|
|
2608
|
+
} catch (e) {
|
|
2609
|
+
console.error(`Error while reading CSS rules from ${sheet.href}`, e);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
return ret;
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
function getWebFontRules(cssRules) {
|
|
2617
|
+
return cssRules.filter((rule) => rule.type === CSSRule.FONT_FACE_RULE).filter((rule) => shouldEmbed(rule.style.getPropertyValue("src")));
|
|
2618
|
+
}
|
|
2619
|
+
async function parseWebFontRules(node, options) {
|
|
2620
|
+
if (node.ownerDocument == null) {
|
|
2621
|
+
throw new Error("Provided element is not within a Document");
|
|
2622
|
+
}
|
|
2623
|
+
const styleSheets = toArray(node.ownerDocument.styleSheets);
|
|
2624
|
+
const cssRules = await getCSSRules(styleSheets, options);
|
|
2625
|
+
return getWebFontRules(cssRules);
|
|
2626
|
+
}
|
|
2627
|
+
function normalizeFontFamily(font) {
|
|
2628
|
+
return font.trim().replace(/["']/g, "");
|
|
2629
|
+
}
|
|
2630
|
+
function getUsedFonts(node) {
|
|
2631
|
+
const fonts = /* @__PURE__ */ new Set();
|
|
2632
|
+
function traverse(node2) {
|
|
2633
|
+
const fontFamily = node2.style.fontFamily || getComputedStyle(node2).fontFamily;
|
|
2634
|
+
fontFamily.split(",").forEach((font) => {
|
|
2635
|
+
fonts.add(normalizeFontFamily(font));
|
|
2636
|
+
});
|
|
2637
|
+
Array.from(node2.children).forEach((child) => {
|
|
2638
|
+
if (child instanceof HTMLElement) {
|
|
2639
|
+
traverse(child);
|
|
2640
|
+
}
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
traverse(node);
|
|
2644
|
+
return fonts;
|
|
2645
|
+
}
|
|
2646
|
+
async function getWebFontCSS(node, options) {
|
|
2647
|
+
const rules = await parseWebFontRules(node, options);
|
|
2648
|
+
const usedFonts = getUsedFonts(node);
|
|
2649
|
+
const cssTexts = await Promise.all(rules.filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily))).map((rule) => {
|
|
2650
|
+
const baseUrl = rule.parentStyleSheet ? rule.parentStyleSheet.href : null;
|
|
2651
|
+
return embedResources(rule.cssText, baseUrl, options);
|
|
2652
|
+
}));
|
|
2653
|
+
return cssTexts.join("\n");
|
|
2654
|
+
}
|
|
2655
|
+
async function embedWebFonts(clonedNode, options) {
|
|
2656
|
+
const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
|
|
2657
|
+
if (cssText) {
|
|
2658
|
+
const styleNode = document.createElement("style");
|
|
2659
|
+
const sytleContent = document.createTextNode(cssText);
|
|
2660
|
+
styleNode.appendChild(sytleContent);
|
|
2661
|
+
if (clonedNode.firstChild) {
|
|
2662
|
+
clonedNode.insertBefore(styleNode, clonedNode.firstChild);
|
|
2663
|
+
} else {
|
|
2664
|
+
clonedNode.appendChild(styleNode);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
// node_modules/html-to-image/es/index.js
|
|
2670
|
+
async function toSvg(node, options = {}) {
|
|
2671
|
+
const { width, height } = getImageSize(node, options);
|
|
2672
|
+
const clonedNode = await cloneNode(node, options, true);
|
|
2673
|
+
await embedWebFonts(clonedNode, options);
|
|
2674
|
+
await embedImages(clonedNode, options);
|
|
2675
|
+
applyStyle(clonedNode, options);
|
|
2676
|
+
const datauri = await nodeToDataURL(clonedNode, width, height);
|
|
2677
|
+
return datauri;
|
|
2678
|
+
}
|
|
2679
|
+
async function toCanvas(node, options = {}) {
|
|
2680
|
+
const { width, height } = getImageSize(node, options);
|
|
2681
|
+
const svg = await toSvg(node, options);
|
|
2682
|
+
const img = await createImage(svg);
|
|
2683
|
+
const canvas = document.createElement("canvas");
|
|
2684
|
+
const context = canvas.getContext("2d");
|
|
2685
|
+
const ratio = options.pixelRatio || getPixelRatio();
|
|
2686
|
+
const canvasWidth = options.canvasWidth || width;
|
|
2687
|
+
const canvasHeight = options.canvasHeight || height;
|
|
2688
|
+
canvas.width = canvasWidth * ratio;
|
|
2689
|
+
canvas.height = canvasHeight * ratio;
|
|
2690
|
+
if (!options.skipAutoScale) {
|
|
2691
|
+
checkCanvasDimensions(canvas);
|
|
2692
|
+
}
|
|
2693
|
+
canvas.style.width = `${canvasWidth}`;
|
|
2694
|
+
canvas.style.height = `${canvasHeight}`;
|
|
2695
|
+
if (options.backgroundColor) {
|
|
2696
|
+
context.fillStyle = options.backgroundColor;
|
|
2697
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
2698
|
+
}
|
|
2699
|
+
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
2700
|
+
return canvas;
|
|
2701
|
+
}
|
|
2702
|
+
async function toPng(node, options = {}) {
|
|
2703
|
+
const canvas = await toCanvas(node, options);
|
|
2704
|
+
return canvas.toDataURL();
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
// overlay/src/screenshot.ts
|
|
2708
|
+
function areSiblings(nodes) {
|
|
2709
|
+
if (nodes.length === 0) return false;
|
|
2710
|
+
const parent = nodes[0].parentElement;
|
|
2711
|
+
return nodes.every((n) => n.parentElement === parent);
|
|
2712
|
+
}
|
|
2713
|
+
async function captureRegion(nodes) {
|
|
2714
|
+
if (nodes.length === 1) {
|
|
2715
|
+
const rect = nodes[0].getBoundingClientRect();
|
|
2716
|
+
const width2 = Math.round(rect.width);
|
|
2717
|
+
const height2 = Math.round(rect.height);
|
|
2718
|
+
const dataUrl = await toPng(nodes[0], { skipFonts: true, width: width2, height: height2 });
|
|
2719
|
+
return { dataUrl, width: width2, height: height2 };
|
|
2720
|
+
}
|
|
2721
|
+
const parent = nodes[0].parentElement;
|
|
2722
|
+
const rects = nodes.map((n) => n.getBoundingClientRect());
|
|
2723
|
+
const top = Math.min(...rects.map((r) => r.top));
|
|
2724
|
+
const left = Math.min(...rects.map((r) => r.left));
|
|
2725
|
+
const right = Math.max(...rects.map((r) => r.right));
|
|
2726
|
+
const bottom = Math.max(...rects.map((r) => r.bottom));
|
|
2727
|
+
const width = Math.round(right - left);
|
|
2728
|
+
const height = Math.round(bottom - top);
|
|
2729
|
+
const ghost = parent.cloneNode(false);
|
|
2730
|
+
ghost.style.padding = "0";
|
|
2731
|
+
ghost.style.border = "none";
|
|
2732
|
+
ghost.style.margin = "0";
|
|
2733
|
+
ghost.style.width = `${width}px`;
|
|
2734
|
+
for (const node of nodes) {
|
|
2735
|
+
ghost.appendChild(node.cloneNode(true));
|
|
2736
|
+
}
|
|
2737
|
+
ghost.style.position = "fixed";
|
|
2738
|
+
ghost.style.left = "0";
|
|
2739
|
+
ghost.style.top = "0";
|
|
2740
|
+
ghost.style.zIndex = "999999";
|
|
2741
|
+
ghost.style.pointerEvents = "none";
|
|
2742
|
+
document.body.appendChild(ghost);
|
|
2743
|
+
try {
|
|
2744
|
+
const dataUrl = await toPng(ghost, { skipFonts: true, width, height });
|
|
2745
|
+
return { dataUrl, width, height };
|
|
2746
|
+
} finally {
|
|
2747
|
+
ghost.remove();
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
1845
2751
|
// overlay/src/index.ts
|
|
1846
2752
|
var shadowRoot;
|
|
1847
2753
|
var shadowHost;
|
|
@@ -1852,7 +2758,7 @@ ${pad}</${tag}>`;
|
|
|
1852
2758
|
var currentTargetEl = null;
|
|
1853
2759
|
var currentBoundary = null;
|
|
1854
2760
|
var currentInstances = [];
|
|
1855
|
-
var
|
|
2761
|
+
var cachedNearGroups = null;
|
|
1856
2762
|
var hoverOutlineEl = null;
|
|
1857
2763
|
var hoverTooltipEl = null;
|
|
1858
2764
|
var lastHoveredEl = null;
|
|
@@ -1985,7 +2891,7 @@ ${pad}</${tag}>`;
|
|
|
1985
2891
|
align-self: stretch;
|
|
1986
2892
|
}
|
|
1987
2893
|
/* Base style for all buttons inside the toolbar */
|
|
1988
|
-
.draw-btn, .el-pick-btn, .el-add-btn {
|
|
2894
|
+
.draw-btn, .el-reselect-btn, .el-pick-btn, .el-add-btn {
|
|
1989
2895
|
background: transparent;
|
|
1990
2896
|
border: none;
|
|
1991
2897
|
border-radius: 0;
|
|
@@ -2009,10 +2915,59 @@ ${pad}</${tag}>`;
|
|
|
2009
2915
|
.el-pick-btn { gap: 3px; padding: 0 8px; font-size: 12px; font-weight: 600; letter-spacing: 0.01em; }
|
|
2010
2916
|
.el-pick-btn svg { opacity: 0.7; flex-shrink: 0; }
|
|
2011
2917
|
.el-add-btn { padding: 0 10px; font-size: 15px; font-weight: 400; }
|
|
2012
|
-
.
|
|
2918
|
+
.el-reselect-btn { padding: 0 9px; }
|
|
2919
|
+
.draw-btn:hover, .el-reselect-btn:hover, .el-pick-btn:hover, .el-add-btn:hover,
|
|
2013
2920
|
.el-pick-btn.open {
|
|
2014
2921
|
background: rgba(255,255,255,0.12);
|
|
2015
2922
|
}
|
|
2923
|
+
/* \u2500\u2500 Hover preview highlight (dashed, for group hover) \u2500\u2500 */
|
|
2924
|
+
.highlight-preview {
|
|
2925
|
+
position: fixed;
|
|
2926
|
+
pointer-events: none;
|
|
2927
|
+
border: 2px dashed #00848B;
|
|
2928
|
+
border-radius: 2px;
|
|
2929
|
+
box-sizing: border-box;
|
|
2930
|
+
z-index: 999998;
|
|
2931
|
+
}
|
|
2932
|
+
/* \u2500\u2500 Group picker popover (replaces instance picker) \u2500\u2500 */
|
|
2933
|
+
.el-group-empty {
|
|
2934
|
+
padding: 12px 14px;
|
|
2935
|
+
font-size: 11px;
|
|
2936
|
+
color: #687879;
|
|
2937
|
+
text-align: center;
|
|
2938
|
+
}
|
|
2939
|
+
.el-group-row {
|
|
2940
|
+
display: flex;
|
|
2941
|
+
align-items: center;
|
|
2942
|
+
gap: 8px;
|
|
2943
|
+
padding: 5px 12px;
|
|
2944
|
+
cursor: pointer;
|
|
2945
|
+
transition: background 0.1s;
|
|
2946
|
+
}
|
|
2947
|
+
.el-group-row:hover { background: rgba(0,132,139,0.05); }
|
|
2948
|
+
.el-group-row input[type=checkbox] {
|
|
2949
|
+
accent-color: #00848B;
|
|
2950
|
+
width: 13px;
|
|
2951
|
+
height: 13px;
|
|
2952
|
+
flex-shrink: 0;
|
|
2953
|
+
cursor: pointer;
|
|
2954
|
+
}
|
|
2955
|
+
.el-group-count {
|
|
2956
|
+
font-size: 11px;
|
|
2957
|
+
font-weight: 600;
|
|
2958
|
+
color: #334041;
|
|
2959
|
+
min-width: 20px;
|
|
2960
|
+
}
|
|
2961
|
+
.el-group-diff {
|
|
2962
|
+
flex: 1;
|
|
2963
|
+
font-size: 10px;
|
|
2964
|
+
font-family: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
|
|
2965
|
+
overflow: hidden;
|
|
2966
|
+
text-overflow: ellipsis;
|
|
2967
|
+
white-space: nowrap;
|
|
2968
|
+
}
|
|
2969
|
+
.el-group-diff .diff-add { color: #16a34a; }
|
|
2970
|
+
.el-group-diff .diff-rem { color: #dc2626; }
|
|
2016
2971
|
/* \u2500\u2500 Instance picker popover \u2500\u2500 */
|
|
2017
2972
|
.el-picker {
|
|
2018
2973
|
position: fixed;
|
|
@@ -2211,7 +3166,6 @@ ${pad}</${tag}>`;
|
|
|
2211
3166
|
var drawPopoverEl = null;
|
|
2212
3167
|
var pickerEl = null;
|
|
2213
3168
|
var pickerCloseHandler = null;
|
|
2214
|
-
var selectedInstanceIndices = /* @__PURE__ */ new Set();
|
|
2215
3169
|
function removeDrawButton() {
|
|
2216
3170
|
toolbarEl?.remove();
|
|
2217
3171
|
toolbarEl = null;
|
|
@@ -2281,7 +3235,7 @@ ${pad}</${tag}>`;
|
|
|
2281
3235
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
2282
3236
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
2283
3237
|
</svg>`;
|
|
2284
|
-
var
|
|
3238
|
+
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
3239
|
async function positionWithFlip(anchor, floating, placement = "top-start") {
|
|
2286
3240
|
const { x, y } = await computePosition2(anchor, floating, {
|
|
2287
3241
|
placement,
|
|
@@ -2292,14 +3246,23 @@ ${pad}</${tag}>`;
|
|
|
2292
3246
|
}
|
|
2293
3247
|
function showDrawButton(targetEl) {
|
|
2294
3248
|
removeDrawButton();
|
|
2295
|
-
const
|
|
2296
|
-
const instanceCount = allEquivalentNodes.length;
|
|
3249
|
+
const instanceCount = currentEquivalentNodes.length;
|
|
2297
3250
|
const toolbar = document.createElement("div");
|
|
2298
3251
|
toolbar.className = "el-toolbar";
|
|
2299
3252
|
toolbar.style.left = "0px";
|
|
2300
3253
|
toolbar.style.top = "0px";
|
|
2301
3254
|
shadowRoot.appendChild(toolbar);
|
|
2302
3255
|
toolbarEl = toolbar;
|
|
3256
|
+
const reselectBtn = document.createElement("button");
|
|
3257
|
+
reselectBtn.className = "el-reselect-btn";
|
|
3258
|
+
reselectBtn.innerHTML = RESELECT_SVG;
|
|
3259
|
+
reselectBtn.title = "Re-select element";
|
|
3260
|
+
toolbar.appendChild(reselectBtn);
|
|
3261
|
+
reselectBtn.addEventListener("click", (e) => {
|
|
3262
|
+
e.stopPropagation();
|
|
3263
|
+
clearHighlights();
|
|
3264
|
+
setSelectMode(true);
|
|
3265
|
+
});
|
|
2303
3266
|
const drawBtn = document.createElement("button");
|
|
2304
3267
|
drawBtn.className = "draw-btn";
|
|
2305
3268
|
drawBtn.innerHTML = PENCIL_SVG;
|
|
@@ -2316,69 +3279,47 @@ ${pad}</${tag}>`;
|
|
|
2316
3279
|
showDrawPopover(drawBtn);
|
|
2317
3280
|
}
|
|
2318
3281
|
});
|
|
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();
|
|
3282
|
+
const sep = document.createElement("div");
|
|
3283
|
+
sep.className = "el-toolbar-sep";
|
|
3284
|
+
toolbar.appendChild(sep);
|
|
3285
|
+
const addGroupBtn = document.createElement("button");
|
|
3286
|
+
addGroupBtn.className = "el-add-btn";
|
|
3287
|
+
addGroupBtn.textContent = `${instanceCount} +`;
|
|
3288
|
+
addGroupBtn.title = `${instanceCount} matching element${instanceCount !== 1 ? "s" : ""} selected \u2014 click to add similar`;
|
|
3289
|
+
toolbar.appendChild(addGroupBtn);
|
|
3290
|
+
addGroupBtn.addEventListener("click", (e) => {
|
|
3291
|
+
e.stopPropagation();
|
|
3292
|
+
drawPopoverEl?.remove();
|
|
3293
|
+
drawPopoverEl = null;
|
|
3294
|
+
if (pickerEl) {
|
|
3295
|
+
pickerEl.remove();
|
|
2361
3296
|
pickerEl = null;
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
3297
|
+
addGroupBtn.classList.remove("open");
|
|
3298
|
+
} else {
|
|
3299
|
+
addGroupBtn.classList.add("open");
|
|
3300
|
+
showGroupPicker(
|
|
3301
|
+
addGroupBtn,
|
|
3302
|
+
() => addGroupBtn.classList.remove("open"),
|
|
3303
|
+
(totalCount) => {
|
|
3304
|
+
addGroupBtn.textContent = `${totalCount} +`;
|
|
3305
|
+
addGroupBtn.title = `${totalCount} matching element${totalCount !== 1 ? "s" : ""} selected \u2014 click to add similar`;
|
|
3306
|
+
}
|
|
3307
|
+
);
|
|
3308
|
+
}
|
|
3309
|
+
});
|
|
2369
3310
|
positionWithFlip(targetEl, toolbar);
|
|
2370
3311
|
}
|
|
2371
|
-
function
|
|
3312
|
+
function showGroupPicker(anchorBtn, onClose, onCountChange) {
|
|
2372
3313
|
if (pickerCloseHandler) {
|
|
2373
3314
|
document.removeEventListener("click", pickerCloseHandler, { capture: true });
|
|
2374
3315
|
pickerCloseHandler = null;
|
|
2375
3316
|
}
|
|
2376
3317
|
pickerEl?.remove();
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
3318
|
+
if (!cachedNearGroups && currentTargetEl) {
|
|
3319
|
+
const exactSet = new Set(currentEquivalentNodes);
|
|
3320
|
+
cachedNearGroups = computeNearGroups(currentTargetEl, exactSet, shadowHost);
|
|
3321
|
+
}
|
|
3322
|
+
const groups = cachedNearGroups ?? [];
|
|
2382
3323
|
const picker = document.createElement("div");
|
|
2383
3324
|
picker.className = "el-picker";
|
|
2384
3325
|
picker.style.left = "0px";
|
|
@@ -2389,72 +3330,89 @@ ${pad}</${tag}>`;
|
|
|
2389
3330
|
header.className = "el-picker-header";
|
|
2390
3331
|
const title = document.createElement("span");
|
|
2391
3332
|
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);
|
|
3333
|
+
title.textContent = "Similar elements";
|
|
2401
3334
|
header.appendChild(title);
|
|
2402
|
-
header.appendChild(actions);
|
|
2403
3335
|
picker.appendChild(header);
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
3336
|
+
if (groups.length === 0) {
|
|
3337
|
+
const empty = document.createElement("div");
|
|
3338
|
+
empty.className = "el-group-empty";
|
|
3339
|
+
empty.textContent = "No similar elements found";
|
|
3340
|
+
picker.appendChild(empty);
|
|
3341
|
+
} else {
|
|
3342
|
+
let clearPreviewHighlights2 = function() {
|
|
3343
|
+
shadowRoot.querySelectorAll(".highlight-preview").forEach((el) => el.remove());
|
|
3344
|
+
}, updateSelection2 = function() {
|
|
3345
|
+
const allNodes = [...baseNodes];
|
|
3346
|
+
for (const idx of checkedGroups) {
|
|
3347
|
+
for (const el of groups[idx].elements) {
|
|
3348
|
+
if (!allNodes.includes(el)) allNodes.push(el);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
currentEquivalentNodes = allNodes;
|
|
3352
|
+
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
3353
|
+
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
3354
|
+
onCountChange(currentEquivalentNodes.length);
|
|
3355
|
+
if (currentTargetEl && currentBoundary) {
|
|
3356
|
+
sendTo("panel", {
|
|
3357
|
+
type: "ELEMENT_SELECTED",
|
|
3358
|
+
componentName: currentBoundary.componentName,
|
|
3359
|
+
instanceCount: currentEquivalentNodes.length,
|
|
3360
|
+
classes: typeof currentTargetEl.className === "string" ? currentTargetEl.className : "",
|
|
3361
|
+
tailwindConfig: tailwindConfigCache
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
};
|
|
3365
|
+
var clearPreviewHighlights = clearPreviewHighlights2, updateSelection = updateSelection2;
|
|
3366
|
+
const list = document.createElement("div");
|
|
3367
|
+
list.className = "el-picker-list";
|
|
3368
|
+
picker.appendChild(list);
|
|
3369
|
+
const checkedGroups = /* @__PURE__ */ new Set();
|
|
3370
|
+
const baseNodes = [...currentEquivalentNodes];
|
|
3371
|
+
groups.forEach((group, idx) => {
|
|
2412
3372
|
const row = document.createElement("label");
|
|
2413
|
-
row.className = "el-
|
|
3373
|
+
row.className = "el-group-row";
|
|
2414
3374
|
const cb = document.createElement("input");
|
|
2415
3375
|
cb.type = "checkbox";
|
|
2416
|
-
cb.checked =
|
|
3376
|
+
cb.checked = false;
|
|
2417
3377
|
cb.addEventListener("change", () => {
|
|
2418
|
-
if (cb.checked)
|
|
2419
|
-
else
|
|
2420
|
-
|
|
2421
|
-
updateApply();
|
|
3378
|
+
if (cb.checked) checkedGroups.add(idx);
|
|
3379
|
+
else checkedGroups.delete(idx);
|
|
3380
|
+
updateSelection2();
|
|
2422
3381
|
});
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
3382
|
+
const count = document.createElement("span");
|
|
3383
|
+
count.className = "el-group-count";
|
|
3384
|
+
count.textContent = `(${group.elements.length})`;
|
|
3385
|
+
const diff = document.createElement("span");
|
|
3386
|
+
diff.className = "el-group-diff";
|
|
3387
|
+
const parts = [];
|
|
3388
|
+
for (const a of group.added) parts.push(`<span class="diff-add">+${a}</span>`);
|
|
3389
|
+
for (const r of group.removed) parts.push(`<span class="diff-rem">-${r}</span>`);
|
|
3390
|
+
diff.innerHTML = parts.join(" ");
|
|
2430
3391
|
row.appendChild(cb);
|
|
2431
|
-
row.appendChild(
|
|
2432
|
-
row.appendChild(
|
|
3392
|
+
row.appendChild(count);
|
|
3393
|
+
row.appendChild(diff);
|
|
2433
3394
|
list.appendChild(row);
|
|
3395
|
+
row.addEventListener("mouseenter", () => {
|
|
3396
|
+
clearPreviewHighlights2();
|
|
3397
|
+
for (const el of group.elements) {
|
|
3398
|
+
const rect = el.getBoundingClientRect();
|
|
3399
|
+
const preview = document.createElement("div");
|
|
3400
|
+
preview.className = "highlight-preview";
|
|
3401
|
+
preview.style.top = `${rect.top - 3}px`;
|
|
3402
|
+
preview.style.left = `${rect.left - 3}px`;
|
|
3403
|
+
preview.style.width = `${rect.width + 6}px`;
|
|
3404
|
+
preview.style.height = `${rect.height + 6}px`;
|
|
3405
|
+
shadowRoot.appendChild(preview);
|
|
3406
|
+
}
|
|
3407
|
+
});
|
|
3408
|
+
row.addEventListener("mouseleave", () => {
|
|
3409
|
+
clearPreviewHighlights2();
|
|
3410
|
+
});
|
|
2434
3411
|
});
|
|
2435
3412
|
}
|
|
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();
|
|
3413
|
+
positionWithFlip(anchorBtn, picker);
|
|
2457
3414
|
const removePicker = () => {
|
|
3415
|
+
shadowRoot.querySelectorAll(".highlight-preview").forEach((el) => el.remove());
|
|
2458
3416
|
if (pickerCloseHandler) {
|
|
2459
3417
|
document.removeEventListener("click", pickerCloseHandler, { capture: true });
|
|
2460
3418
|
pickerCloseHandler = null;
|
|
@@ -2462,16 +3420,6 @@ ${pad}</${tag}>`;
|
|
|
2462
3420
|
pickerEl?.remove();
|
|
2463
3421
|
pickerEl = null;
|
|
2464
3422
|
};
|
|
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
3423
|
setTimeout(() => {
|
|
2476
3424
|
pickerCloseHandler = (e) => {
|
|
2477
3425
|
const path = e.composedPath();
|
|
@@ -2515,6 +3463,26 @@ ${pad}</${tag}>`;
|
|
|
2515
3463
|
});
|
|
2516
3464
|
popover.appendChild(row);
|
|
2517
3465
|
}
|
|
3466
|
+
const sep = document.createElement("div");
|
|
3467
|
+
sep.style.cssText = "height:1px;background:#DFE2E2;margin:4px 0;";
|
|
3468
|
+
popover.appendChild(sep);
|
|
3469
|
+
const screenshotHeader = document.createElement("div");
|
|
3470
|
+
screenshotHeader.className = "draw-popover-header";
|
|
3471
|
+
screenshotHeader.textContent = "Screenshot & Annotate";
|
|
3472
|
+
popover.appendChild(screenshotHeader);
|
|
3473
|
+
const screenshotRow = document.createElement("button");
|
|
3474
|
+
screenshotRow.className = "draw-popover-item";
|
|
3475
|
+
screenshotRow.innerHTML = `
|
|
3476
|
+
<span class="draw-popover-icon">\u{1F4F7}</span>
|
|
3477
|
+
<span class="draw-popover-label">Screenshot & Annotate</span>
|
|
3478
|
+
`;
|
|
3479
|
+
screenshotRow.addEventListener("click", (e) => {
|
|
3480
|
+
e.stopPropagation();
|
|
3481
|
+
drawPopoverEl?.remove();
|
|
3482
|
+
drawPopoverEl = null;
|
|
3483
|
+
handleCaptureScreenshot();
|
|
3484
|
+
});
|
|
3485
|
+
popover.appendChild(screenshotRow);
|
|
2518
3486
|
drawPopoverEl = popover;
|
|
2519
3487
|
shadowRoot.appendChild(popover);
|
|
2520
3488
|
positionWithFlip(anchorBtn, popover, "top-start");
|
|
@@ -2563,86 +3531,25 @@ ${pad}</${tag}>`;
|
|
|
2563
3531
|
e.preventDefault();
|
|
2564
3532
|
e.stopPropagation();
|
|
2565
3533
|
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
|
-
}
|
|
3534
|
+
const targetEl = target;
|
|
3535
|
+
const classString = typeof targetEl.className === "string" ? targetEl.className : "";
|
|
3536
|
+
const result = findExactMatches(targetEl, shadowHost);
|
|
3537
|
+
const componentName = result.componentName ?? targetEl.tagName.toLowerCase();
|
|
2603
3538
|
clearHighlights();
|
|
2604
|
-
const
|
|
2605
|
-
for (const node of newNodes) {
|
|
2606
|
-
equivalentNodes.push(node);
|
|
3539
|
+
for (const node of result.exactMatch) {
|
|
2607
3540
|
highlightElement(node);
|
|
2608
3541
|
}
|
|
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`);
|
|
3542
|
+
console.log(`[overlay] ${componentName} \u2014 ${result.exactMatch.length} exact matches highlighted`);
|
|
2621
3543
|
const config = await fetchTailwindConfig();
|
|
2622
|
-
|
|
2623
|
-
const classString = targetEl.className;
|
|
2624
|
-
if (typeof classString !== "string") return;
|
|
2625
|
-
currentEquivalentNodes = equivalentNodes;
|
|
3544
|
+
currentEquivalentNodes = result.exactMatch;
|
|
2626
3545
|
currentTargetEl = targetEl;
|
|
2627
3546
|
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
|
-
}
|
|
3547
|
+
cachedNearGroups = null;
|
|
3548
|
+
currentInstances = result.exactMatch.map((node, i) => ({
|
|
3549
|
+
index: i,
|
|
3550
|
+
label: (node.innerText || "").trim().slice(0, 40) || `#${i + 1}`,
|
|
3551
|
+
parent: node.parentElement?.tagName.toLowerCase() ?? ""
|
|
3552
|
+
}));
|
|
2646
3553
|
clearHoverPreview();
|
|
2647
3554
|
setSelectMode(false);
|
|
2648
3555
|
showDrawButton(targetEl);
|
|
@@ -2653,7 +3560,7 @@ ${pad}</${tag}>`;
|
|
|
2653
3560
|
sendTo("panel", {
|
|
2654
3561
|
type: "ELEMENT_SELECTED",
|
|
2655
3562
|
componentName,
|
|
2656
|
-
instanceCount:
|
|
3563
|
+
instanceCount: result.exactMatch.length,
|
|
2657
3564
|
classes: classString,
|
|
2658
3565
|
tailwindConfig: config
|
|
2659
3566
|
});
|
|
@@ -2664,7 +3571,6 @@ ${pad}</${tag}>`;
|
|
|
2664
3571
|
document.addEventListener("click", clickHandler, { capture: true });
|
|
2665
3572
|
document.addEventListener("mousemove", mouseMoveHandler, { passive: true });
|
|
2666
3573
|
} else {
|
|
2667
|
-
addingMode = false;
|
|
2668
3574
|
document.documentElement.style.cursor = "";
|
|
2669
3575
|
document.removeEventListener("click", clickHandler, { capture: true });
|
|
2670
3576
|
document.removeEventListener("mousemove", mouseMoveHandler);
|
|
@@ -2672,16 +3578,19 @@ ${pad}</${tag}>`;
|
|
|
2672
3578
|
}
|
|
2673
3579
|
sendTo("panel", { type: "SELECT_MODE_CHANGED", active: on });
|
|
2674
3580
|
}
|
|
3581
|
+
var PANEL_OPEN_KEY = "tw-inspector-panel-open";
|
|
2675
3582
|
function toggleInspect(btn) {
|
|
2676
3583
|
active = !active;
|
|
2677
3584
|
if (active) {
|
|
2678
3585
|
btn.classList.add("active");
|
|
3586
|
+
sessionStorage.setItem(PANEL_OPEN_KEY, "1");
|
|
2679
3587
|
const panelUrl = `${SERVER_ORIGIN}/panel`;
|
|
2680
3588
|
if (!activeContainer.isOpen()) {
|
|
2681
3589
|
activeContainer.open(panelUrl);
|
|
2682
3590
|
}
|
|
2683
3591
|
} else {
|
|
2684
3592
|
btn.classList.remove("active");
|
|
3593
|
+
sessionStorage.removeItem(PANEL_OPEN_KEY);
|
|
2685
3594
|
setSelectMode(false);
|
|
2686
3595
|
activeContainer.close();
|
|
2687
3596
|
revertPreview();
|
|
@@ -2710,7 +3619,8 @@ ${pad}</${tag}>`;
|
|
|
2710
3619
|
const wrapper = document.createElement("div");
|
|
2711
3620
|
wrapper.setAttribute("data-tw-design-canvas", "true");
|
|
2712
3621
|
wrapper.style.cssText = `
|
|
2713
|
-
|
|
3622
|
+
outline: 2px dashed #00848B;
|
|
3623
|
+
outline-offset: 2px;
|
|
2714
3624
|
border-radius: 6px;
|
|
2715
3625
|
background: #FAFBFB;
|
|
2716
3626
|
position: relative;
|
|
@@ -2724,6 +3634,7 @@ ${pad}</${tag}>`;
|
|
|
2724
3634
|
`;
|
|
2725
3635
|
const iframe = document.createElement("iframe");
|
|
2726
3636
|
iframe.src = `${SERVER_ORIGIN}/panel/?mode=design`;
|
|
3637
|
+
iframe.allow = "microphone";
|
|
2727
3638
|
iframe.style.cssText = `
|
|
2728
3639
|
width: 100%;
|
|
2729
3640
|
height: 100%;
|
|
@@ -2836,7 +3747,7 @@ ${pad}</${tag}>`;
|
|
|
2836
3747
|
default:
|
|
2837
3748
|
targetEl.insertAdjacentElement("beforebegin", wrapper);
|
|
2838
3749
|
}
|
|
2839
|
-
designCanvasWrappers.push(wrapper);
|
|
3750
|
+
designCanvasWrappers.push({ wrapper, replacedNodes: null, parent: null, anchor: null });
|
|
2840
3751
|
iframe.addEventListener("load", () => {
|
|
2841
3752
|
const contextMsg = {
|
|
2842
3753
|
type: "ELEMENT_CONTEXT",
|
|
@@ -2859,6 +3770,99 @@ ${pad}</${tag}>`;
|
|
|
2859
3770
|
setTimeout(trySend, 200);
|
|
2860
3771
|
});
|
|
2861
3772
|
}
|
|
3773
|
+
async function handleCaptureScreenshot() {
|
|
3774
|
+
if (!currentTargetEl || !currentBoundary) {
|
|
3775
|
+
showToast("Select an element first");
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
if (!areSiblings(currentEquivalentNodes)) {
|
|
3779
|
+
showToast("Screenshot & Annotate requires all selected elements to be siblings in the DOM.");
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
let screenshot;
|
|
3783
|
+
let screenshotWidth;
|
|
3784
|
+
let screenshotHeight;
|
|
3785
|
+
try {
|
|
3786
|
+
({ dataUrl: screenshot, width: screenshotWidth, height: screenshotHeight } = await captureRegion(currentEquivalentNodes));
|
|
3787
|
+
} catch (err) {
|
|
3788
|
+
showToast("Screenshot capture failed");
|
|
3789
|
+
console.error("[overlay] captureRegion error:", err);
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
const parent = currentEquivalentNodes[0].parentElement;
|
|
3793
|
+
const anchor = currentEquivalentNodes[0].nextSibling;
|
|
3794
|
+
const firstStyle = getComputedStyle(currentEquivalentNodes[0]);
|
|
3795
|
+
const lastStyle = getComputedStyle(currentEquivalentNodes[currentEquivalentNodes.length - 1]);
|
|
3796
|
+
const marginTop = firstStyle.marginTop;
|
|
3797
|
+
const marginBottom = lastStyle.marginBottom;
|
|
3798
|
+
const marginLeft = firstStyle.marginLeft;
|
|
3799
|
+
const marginRight = firstStyle.marginRight;
|
|
3800
|
+
const replacedNodes = [...currentEquivalentNodes];
|
|
3801
|
+
const targetEl = currentTargetEl;
|
|
3802
|
+
const boundary = currentBoundary;
|
|
3803
|
+
const instanceCount = currentEquivalentNodes.length;
|
|
3804
|
+
clearHighlights();
|
|
3805
|
+
for (const node of currentEquivalentNodes) {
|
|
3806
|
+
node.remove();
|
|
3807
|
+
}
|
|
3808
|
+
const PANEL_CHROME_HEIGHT = 40;
|
|
3809
|
+
const wrapper = document.createElement("div");
|
|
3810
|
+
wrapper.setAttribute("data-tw-design-canvas", "true");
|
|
3811
|
+
wrapper.style.cssText = `
|
|
3812
|
+
outline: 2px dashed #00848B;
|
|
3813
|
+
outline-offset: 2px;
|
|
3814
|
+
border-radius: 6px;
|
|
3815
|
+
background: #FAFBFB;
|
|
3816
|
+
position: relative;
|
|
3817
|
+
overflow: hidden;
|
|
3818
|
+
width: ${screenshotWidth}px;
|
|
3819
|
+
height: ${screenshotHeight + PANEL_CHROME_HEIGHT}px;
|
|
3820
|
+
min-width: 300px;
|
|
3821
|
+
margin-top: ${marginTop};
|
|
3822
|
+
margin-bottom: ${marginBottom};
|
|
3823
|
+
margin-left: ${marginLeft};
|
|
3824
|
+
margin-right: ${marginRight};
|
|
3825
|
+
box-shadow: 0 4px 24px rgba(0,0,0,0.15);
|
|
3826
|
+
box-sizing: border-box;
|
|
3827
|
+
`;
|
|
3828
|
+
const iframe = document.createElement("iframe");
|
|
3829
|
+
iframe.src = `${SERVER_ORIGIN}/panel/?mode=design`;
|
|
3830
|
+
iframe.style.cssText = `
|
|
3831
|
+
width: 100%;
|
|
3832
|
+
height: 100%;
|
|
3833
|
+
border: none;
|
|
3834
|
+
display: block;
|
|
3835
|
+
`;
|
|
3836
|
+
wrapper.appendChild(iframe);
|
|
3837
|
+
if (anchor) {
|
|
3838
|
+
parent.insertBefore(wrapper, anchor);
|
|
3839
|
+
} else {
|
|
3840
|
+
parent.appendChild(wrapper);
|
|
3841
|
+
}
|
|
3842
|
+
designCanvasWrappers.push({ wrapper, replacedNodes, parent, anchor });
|
|
3843
|
+
iframe.addEventListener("load", () => {
|
|
3844
|
+
const contextMsg = {
|
|
3845
|
+
type: "ELEMENT_CONTEXT",
|
|
3846
|
+
componentName: boundary.componentName,
|
|
3847
|
+
instanceCount,
|
|
3848
|
+
target: {
|
|
3849
|
+
tag: targetEl.tagName.toLowerCase(),
|
|
3850
|
+
classes: typeof targetEl.className === "string" ? targetEl.className : "",
|
|
3851
|
+
innerText: (targetEl.innerText || "").trim().slice(0, 60)
|
|
3852
|
+
},
|
|
3853
|
+
context: buildContext(targetEl, "", "", /* @__PURE__ */ new Map()),
|
|
3854
|
+
insertMode: "replace",
|
|
3855
|
+
screenshot
|
|
3856
|
+
};
|
|
3857
|
+
let attempts = 0;
|
|
3858
|
+
const trySend = () => {
|
|
3859
|
+
sendTo("design", contextMsg);
|
|
3860
|
+
attempts++;
|
|
3861
|
+
if (attempts < 5) setTimeout(trySend, 300);
|
|
3862
|
+
};
|
|
3863
|
+
setTimeout(trySend, 200);
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
2862
3866
|
function getDefaultContainer() {
|
|
2863
3867
|
try {
|
|
2864
3868
|
const stored = localStorage.getItem("tw-panel-container");
|
|
@@ -2909,6 +3913,8 @@ ${pad}</${tag}>`;
|
|
|
2909
3913
|
}
|
|
2910
3914
|
} else if (msg.type === "PATCH_PREVIEW" && currentEquivalentNodes.length > 0) {
|
|
2911
3915
|
applyPreview(currentEquivalentNodes, msg.oldClass, msg.newClass, SERVER_ORIGIN);
|
|
3916
|
+
} else if (msg.type === "PATCH_PREVIEW_BATCH" && currentEquivalentNodes.length > 0) {
|
|
3917
|
+
applyPreviewBatch(currentEquivalentNodes, msg.pairs, SERVER_ORIGIN);
|
|
2912
3918
|
} else if (msg.type === "PATCH_REVERT") {
|
|
2913
3919
|
revertPreview();
|
|
2914
3920
|
} else if (msg.type === "PATCH_STAGE" && currentTargetEl && currentBoundary) {
|
|
@@ -2958,8 +3964,11 @@ ${pad}</${tag}>`;
|
|
|
2958
3964
|
}
|
|
2959
3965
|
} else if (msg.type === "INSERT_DESIGN_CANVAS") {
|
|
2960
3966
|
injectDesignCanvas(msg.insertMode);
|
|
3967
|
+
} else if (msg.type === "CAPTURE_SCREENSHOT") {
|
|
3968
|
+
handleCaptureScreenshot();
|
|
2961
3969
|
} else if (msg.type === "DESIGN_SUBMITTED") {
|
|
2962
|
-
const
|
|
3970
|
+
const lastEntry = designCanvasWrappers[designCanvasWrappers.length - 1];
|
|
3971
|
+
const last = lastEntry?.wrapper;
|
|
2963
3972
|
if (last) {
|
|
2964
3973
|
const iframe = last.querySelector("iframe");
|
|
2965
3974
|
if (iframe && msg.image) {
|
|
@@ -2978,9 +3987,22 @@ ${pad}</${tag}>`;
|
|
|
2978
3987
|
last.appendChild(img);
|
|
2979
3988
|
}
|
|
2980
3989
|
}
|
|
3990
|
+
} else if (msg.type === "CLOSE_PANEL") {
|
|
3991
|
+
if (active) toggleInspect(btn);
|
|
2981
3992
|
} else if (msg.type === "DESIGN_CLOSE") {
|
|
2982
3993
|
const last = designCanvasWrappers.pop();
|
|
2983
|
-
if (last)
|
|
3994
|
+
if (last) {
|
|
3995
|
+
if (last.replacedNodes && last.parent) {
|
|
3996
|
+
for (const node of last.replacedNodes) {
|
|
3997
|
+
if (last.anchor) {
|
|
3998
|
+
last.parent.insertBefore(node, last.anchor);
|
|
3999
|
+
} else {
|
|
4000
|
+
last.parent.appendChild(node);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
last.wrapper.remove();
|
|
4005
|
+
}
|
|
2984
4006
|
}
|
|
2985
4007
|
});
|
|
2986
4008
|
window.addEventListener("resize", () => {
|
|
@@ -2988,13 +4010,24 @@ ${pad}</${tag}>`;
|
|
|
2988
4010
|
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
2989
4011
|
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
2990
4012
|
}
|
|
4013
|
+
if (toolbarEl && currentTargetEl) {
|
|
4014
|
+
positionWithFlip(currentTargetEl, toolbarEl);
|
|
4015
|
+
}
|
|
2991
4016
|
});
|
|
2992
4017
|
window.addEventListener("scroll", () => {
|
|
2993
4018
|
if (currentEquivalentNodes.length > 0) {
|
|
2994
4019
|
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
2995
4020
|
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
2996
4021
|
}
|
|
4022
|
+
if (toolbarEl && currentTargetEl) {
|
|
4023
|
+
positionWithFlip(currentTargetEl, toolbarEl);
|
|
4024
|
+
}
|
|
2997
4025
|
}, { capture: true, passive: true });
|
|
4026
|
+
if (sessionStorage.getItem(PANEL_OPEN_KEY) === "1") {
|
|
4027
|
+
active = true;
|
|
4028
|
+
btn.classList.add("active");
|
|
4029
|
+
activeContainer.open(`${SERVER_ORIGIN}/panel`);
|
|
4030
|
+
}
|
|
2998
4031
|
window.addEventListener("overlay-ws-connected", () => {
|
|
2999
4032
|
if (wasConnected) {
|
|
3000
4033
|
showToast("Reconnected");
|