@bitovi/vybit 0.6.0 → 0.8.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 +823 -737
- package/package.json +1 -1
- package/panel/dist/assets/{DesignMode-BvwXaVHr.js → DesignMode--Rr68C4a.js} +2 -2
- package/panel/dist/assets/index-BGv11THn.css +1 -0
- package/panel/dist/assets/index-DYasOvKi.js +49 -0
- package/panel/dist/index.html +4 -4
- package/server/tailwind-v3.ts +476 -146
- package/server/tailwind-v4.ts +228 -115
- package/shared/types.ts +13 -0
- package/panel/dist/assets/index-BuY5a8tH.css +0 -1
- package/panel/dist/assets/index-CQNPp6e5.js +0 -49
package/overlay/dist/overlay.js
CHANGED
|
@@ -1121,499 +1121,279 @@
|
|
|
1121
1121
|
});
|
|
1122
1122
|
};
|
|
1123
1123
|
|
|
1124
|
-
// overlay/src/
|
|
1125
|
-
var
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
connected = true;
|
|
1132
|
-
send({ type: "REGISTER", role: "overlay" });
|
|
1133
|
-
window.dispatchEvent(new CustomEvent("overlay-ws-connected"));
|
|
1134
|
-
});
|
|
1135
|
-
socket.addEventListener("close", () => {
|
|
1136
|
-
connected = false;
|
|
1137
|
-
socket = null;
|
|
1138
|
-
window.dispatchEvent(new CustomEvent("overlay-ws-disconnected"));
|
|
1139
|
-
setTimeout(() => connect(url), 3e3);
|
|
1140
|
-
});
|
|
1141
|
-
socket.addEventListener("message", (event) => {
|
|
1142
|
-
try {
|
|
1143
|
-
const data = JSON.parse(event.data);
|
|
1144
|
-
for (const handler of handlers) {
|
|
1145
|
-
handler(data);
|
|
1146
|
-
}
|
|
1147
|
-
} catch (err) {
|
|
1148
|
-
console.error("[tw-overlay] Failed to parse message:", err);
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1151
|
-
socket.addEventListener("error", (err) => {
|
|
1152
|
-
console.error("[tw-overlay] WebSocket error:", err);
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
function send(data) {
|
|
1156
|
-
if (connected && socket) {
|
|
1157
|
-
socket.send(JSON.stringify(data));
|
|
1158
|
-
} else {
|
|
1159
|
-
console.warn("[tw-overlay] Cannot send \u2014 not connected");
|
|
1124
|
+
// overlay/src/containers/ModalContainer.ts
|
|
1125
|
+
var STORAGE_KEY = "tw-modal-bounds";
|
|
1126
|
+
function loadBounds() {
|
|
1127
|
+
try {
|
|
1128
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
1129
|
+
if (stored) return JSON.parse(stored);
|
|
1130
|
+
} catch {
|
|
1160
1131
|
}
|
|
1132
|
+
return { top: 80, left: Math.max(0, window.innerWidth - 440), width: 400, height: 600 };
|
|
1161
1133
|
}
|
|
1162
|
-
function
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
send({ ...data, to: role });
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// overlay/src/fiber.ts
|
|
1170
|
-
function getFiber(domNode) {
|
|
1171
|
-
const key = Object.keys(domNode).find((k) => k.startsWith("__reactFiber$"));
|
|
1172
|
-
return key ? domNode[key] : null;
|
|
1173
|
-
}
|
|
1174
|
-
function findComponentBoundary(fiber) {
|
|
1175
|
-
let current = fiber.return;
|
|
1176
|
-
while (current) {
|
|
1177
|
-
if (typeof current.type === "function") {
|
|
1178
|
-
return {
|
|
1179
|
-
componentType: current.type,
|
|
1180
|
-
componentName: current.type.displayName || current.type.name || "Unknown",
|
|
1181
|
-
componentFiber: current
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
current = current.return;
|
|
1134
|
+
function saveBounds(bounds) {
|
|
1135
|
+
try {
|
|
1136
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(bounds));
|
|
1137
|
+
} catch {
|
|
1185
1138
|
}
|
|
1186
|
-
return null;
|
|
1187
1139
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
const el = document.getElementById(id);
|
|
1192
|
-
if (!el) continue;
|
|
1193
|
-
const key = Object.keys(el).find((k) => k.startsWith("__reactContainer$"));
|
|
1194
|
-
if (key) {
|
|
1195
|
-
const container = el[key];
|
|
1196
|
-
if (container?.stateNode?.current) {
|
|
1197
|
-
return container.stateNode.current;
|
|
1198
|
-
}
|
|
1199
|
-
return container;
|
|
1200
|
-
}
|
|
1140
|
+
var ModalContainer = class {
|
|
1141
|
+
constructor(shadowRoot2) {
|
|
1142
|
+
this.shadowRoot = shadowRoot2;
|
|
1201
1143
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1144
|
+
name = "modal";
|
|
1145
|
+
host = null;
|
|
1146
|
+
bounds = loadBounds();
|
|
1147
|
+
open(panelUrl) {
|
|
1148
|
+
if (this.host) return;
|
|
1149
|
+
this.bounds = loadBounds();
|
|
1150
|
+
const host = document.createElement("div");
|
|
1151
|
+
host.className = "container-modal";
|
|
1152
|
+
this.applyBounds(host);
|
|
1153
|
+
host.style.position = "fixed";
|
|
1154
|
+
host.style.zIndex = "999999";
|
|
1155
|
+
host.style.background = "#1e1e2e";
|
|
1156
|
+
host.style.borderRadius = "8px";
|
|
1157
|
+
host.style.boxShadow = "0 8px 32px rgba(0,0,0,0.4)";
|
|
1158
|
+
host.style.display = "flex";
|
|
1159
|
+
host.style.flexDirection = "column";
|
|
1160
|
+
host.style.overflow = "hidden";
|
|
1161
|
+
host.style.pointerEvents = "auto";
|
|
1162
|
+
const handle = document.createElement("div");
|
|
1163
|
+
handle.style.cssText = `
|
|
1164
|
+
height: 28px;
|
|
1165
|
+
background: #181825;
|
|
1166
|
+
cursor: move;
|
|
1167
|
+
display: flex;
|
|
1168
|
+
align-items: center;
|
|
1169
|
+
justify-content: center;
|
|
1170
|
+
flex-shrink: 0;
|
|
1171
|
+
user-select: none;
|
|
1172
|
+
`;
|
|
1173
|
+
handle.innerHTML = `<svg width="32" height="6" viewBox="0 0 32 6" fill="none"><rect x="0" y="0" width="32" height="2" rx="1" fill="#585b70"/><rect x="0" y="4" width="32" height="2" rx="1" fill="#585b70"/></svg>`;
|
|
1174
|
+
this.setupDrag(handle, host);
|
|
1175
|
+
host.appendChild(handle);
|
|
1176
|
+
const iframe = document.createElement("iframe");
|
|
1177
|
+
iframe.src = panelUrl;
|
|
1178
|
+
iframe.allow = "microphone";
|
|
1179
|
+
iframe.style.cssText = "flex:1; border:none; width:100%;";
|
|
1180
|
+
host.appendChild(iframe);
|
|
1181
|
+
const gripper = document.createElement("div");
|
|
1182
|
+
gripper.style.cssText = `
|
|
1183
|
+
position: absolute;
|
|
1184
|
+
bottom: 0;
|
|
1185
|
+
right: 0;
|
|
1186
|
+
width: 16px;
|
|
1187
|
+
height: 16px;
|
|
1188
|
+
cursor: nwse-resize;
|
|
1189
|
+
`;
|
|
1190
|
+
gripper.innerHTML = '<span style="position:absolute;bottom:2px;right:4px;color:#585b70;font-size:10px;">\u25E2</span>';
|
|
1191
|
+
this.setupResize(gripper, host, iframe);
|
|
1192
|
+
host.appendChild(gripper);
|
|
1193
|
+
this.shadowRoot.appendChild(host);
|
|
1194
|
+
this.host = host;
|
|
1205
1195
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
function walk(fiber) {
|
|
1211
|
-
if (!fiber) return;
|
|
1212
|
-
if (fiber.type === componentType) {
|
|
1213
|
-
results.push(fiber);
|
|
1196
|
+
close() {
|
|
1197
|
+
if (this.host) {
|
|
1198
|
+
this.host.remove();
|
|
1199
|
+
this.host = null;
|
|
1214
1200
|
}
|
|
1215
|
-
walk(fiber.child);
|
|
1216
|
-
walk(fiber.sibling);
|
|
1217
|
-
}
|
|
1218
|
-
walk(rootFiber);
|
|
1219
|
-
return results;
|
|
1220
|
-
}
|
|
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
1201
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1202
|
+
isOpen() {
|
|
1203
|
+
return this.host !== null;
|
|
1242
1204
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
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
|
-
}
|
|
1205
|
+
applyBounds(el) {
|
|
1206
|
+
el.style.top = `${this.bounds.top}px`;
|
|
1207
|
+
el.style.left = `${this.bounds.left}px`;
|
|
1208
|
+
el.style.width = `${this.bounds.width}px`;
|
|
1209
|
+
el.style.height = `${this.bounds.height}px`;
|
|
1283
1210
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1211
|
+
setupDrag(handle, host) {
|
|
1212
|
+
let startX = 0, startY = 0, startLeft = 0, startTop = 0;
|
|
1213
|
+
const onMove = (e) => {
|
|
1214
|
+
this.bounds.left = startLeft + (e.clientX - startX);
|
|
1215
|
+
this.bounds.top = startTop + (e.clientY - startY);
|
|
1216
|
+
host.style.left = `${this.bounds.left}px`;
|
|
1217
|
+
host.style.top = `${this.bounds.top}px`;
|
|
1218
|
+
};
|
|
1219
|
+
const onUp = () => {
|
|
1220
|
+
document.removeEventListener("mousemove", onMove);
|
|
1221
|
+
document.removeEventListener("mouseup", onUp);
|
|
1222
|
+
saveBounds(this.bounds);
|
|
1223
|
+
};
|
|
1224
|
+
handle.addEventListener("mousedown", (e) => {
|
|
1225
|
+
e.preventDefault();
|
|
1226
|
+
startX = e.clientX;
|
|
1227
|
+
startY = e.clientY;
|
|
1228
|
+
startLeft = this.bounds.left;
|
|
1229
|
+
startTop = this.bounds.top;
|
|
1230
|
+
document.addEventListener("mousemove", onMove);
|
|
1231
|
+
document.addEventListener("mouseup", onUp);
|
|
1232
|
+
});
|
|
1286
1233
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
if (candidates.length >= MAX_CANDIDATES) break;
|
|
1320
|
-
}
|
|
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] });
|
|
1334
|
-
}
|
|
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
|
|
1234
|
+
setupResize(gripper, host, iframe) {
|
|
1235
|
+
let startX = 0, startY = 0, startW = 0, startH = 0;
|
|
1236
|
+
const onMove = (e) => {
|
|
1237
|
+
this.bounds.width = Math.max(300, startW + (e.clientX - startX));
|
|
1238
|
+
this.bounds.height = Math.max(200, startH + (e.clientY - startY));
|
|
1239
|
+
host.style.width = `${this.bounds.width}px`;
|
|
1240
|
+
host.style.height = `${this.bounds.height}px`;
|
|
1241
|
+
};
|
|
1242
|
+
const onUp = () => {
|
|
1243
|
+
iframe.style.pointerEvents = "";
|
|
1244
|
+
document.removeEventListener("mousemove", onMove);
|
|
1245
|
+
document.removeEventListener("mouseup", onUp);
|
|
1246
|
+
saveBounds(this.bounds);
|
|
1247
|
+
};
|
|
1248
|
+
gripper.addEventListener("mousedown", (e) => {
|
|
1249
|
+
e.preventDefault();
|
|
1250
|
+
iframe.style.pointerEvents = "none";
|
|
1251
|
+
startX = e.clientX;
|
|
1252
|
+
startY = e.clientY;
|
|
1253
|
+
startW = this.bounds.width;
|
|
1254
|
+
startH = this.bounds.height;
|
|
1255
|
+
document.addEventListener("mousemove", onMove);
|
|
1256
|
+
document.addEventListener("mouseup", onUp);
|
|
1343
1257
|
});
|
|
1344
1258
|
}
|
|
1345
|
-
|
|
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);
|
|
1374
|
-
}
|
|
1259
|
+
};
|
|
1375
1260
|
|
|
1376
|
-
// overlay/src/
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
while (current && current !== document.documentElement) {
|
|
1381
|
-
ancestors.push(current);
|
|
1382
|
-
current = current.parentElement;
|
|
1383
|
-
}
|
|
1384
|
-
ancestors.reverse();
|
|
1385
|
-
return buildLevel(ancestors, 0, target, oldClass, newClass, originalClassMap, 0);
|
|
1386
|
-
}
|
|
1387
|
-
function buildLevel(ancestors, ancestorIndex, target, oldClass, newClass, originalClassMap, indent) {
|
|
1388
|
-
const el = ancestors[ancestorIndex];
|
|
1389
|
-
const pad = " ".repeat(indent);
|
|
1390
|
-
const tag = el.tagName.toLowerCase();
|
|
1391
|
-
let attrs = "";
|
|
1392
|
-
if (el.id) attrs += ` id="${el.id}"`;
|
|
1393
|
-
const originalClass = originalClassMap.get(el);
|
|
1394
|
-
const classStr = originalClass != null ? originalClass.trim() : typeof el.className === "string" ? el.className.trim() : "";
|
|
1395
|
-
if (classStr) {
|
|
1396
|
-
attrs += ` class="${classStr}"`;
|
|
1397
|
-
}
|
|
1398
|
-
const isTarget = el === target;
|
|
1399
|
-
if (isTarget) {
|
|
1400
|
-
const text = getInnerText(el);
|
|
1401
|
-
const textNode = text ? `
|
|
1402
|
-
${pad} ${text}` : "";
|
|
1403
|
-
return `${pad}<${tag}${attrs}> <!-- TARGET: change ${oldClass} \u2192 ${newClass} -->${textNode}
|
|
1404
|
-
${pad}</${tag}>`;
|
|
1261
|
+
// overlay/src/containers/PopoverContainer.ts
|
|
1262
|
+
var PopoverContainer = class {
|
|
1263
|
+
constructor(shadowRoot2) {
|
|
1264
|
+
this.shadowRoot = shadowRoot2;
|
|
1405
1265
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1266
|
+
name = "popover";
|
|
1267
|
+
host = null;
|
|
1268
|
+
open(panelUrl) {
|
|
1269
|
+
if (this.host) return;
|
|
1270
|
+
const host = document.createElement("div");
|
|
1271
|
+
host.className = "container-popover";
|
|
1272
|
+
host.style.cssText = `
|
|
1273
|
+
position: fixed;
|
|
1274
|
+
top: 0;
|
|
1275
|
+
right: 0;
|
|
1276
|
+
width: 400px;
|
|
1277
|
+
height: 100vh;
|
|
1278
|
+
z-index: 999999;
|
|
1279
|
+
background: #1e1e2e;
|
|
1280
|
+
box-shadow: -4px 0 24px rgba(0,0,0,0.3);
|
|
1281
|
+
pointer-events: auto;
|
|
1282
|
+
`;
|
|
1283
|
+
const iframe = document.createElement("iframe");
|
|
1284
|
+
iframe.src = panelUrl;
|
|
1285
|
+
iframe.allow = "microphone";
|
|
1286
|
+
iframe.style.cssText = "width:100%; height:100%; border:none;";
|
|
1287
|
+
host.appendChild(iframe);
|
|
1288
|
+
this.shadowRoot.appendChild(host);
|
|
1289
|
+
this.host = host;
|
|
1408
1290
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
if (relevantIndex === -1) {
|
|
1414
|
-
inner = buildLevel(ancestors, ancestorIndex + 1, target, oldClass, newClass, originalClassMap, indent + 1);
|
|
1415
|
-
} else {
|
|
1416
|
-
const start = Math.max(0, relevantIndex - 3);
|
|
1417
|
-
const end = Math.min(children.length - 1, relevantIndex + 3);
|
|
1418
|
-
if (start > 0) {
|
|
1419
|
-
inner += `${pad} ...
|
|
1420
|
-
`;
|
|
1421
|
-
}
|
|
1422
|
-
for (let i = start; i <= end; i++) {
|
|
1423
|
-
if (i === relevantIndex) {
|
|
1424
|
-
inner += buildLevel(ancestors, ancestorIndex + 1, target, oldClass, newClass, originalClassMap, indent + 1) + "\n";
|
|
1425
|
-
} else {
|
|
1426
|
-
inner += renderSiblingNode(children[i], indent + 1, originalClassMap) + "\n";
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
if (end < children.length - 1) {
|
|
1430
|
-
inner += `${pad} ...
|
|
1431
|
-
`;
|
|
1291
|
+
close() {
|
|
1292
|
+
if (this.host) {
|
|
1293
|
+
this.host.remove();
|
|
1294
|
+
this.host = null;
|
|
1432
1295
|
}
|
|
1433
1296
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1437
|
-
function renderSiblingNode(el, indent, originalClassMap) {
|
|
1438
|
-
const pad = " ".repeat(indent);
|
|
1439
|
-
const tag = el.tagName.toLowerCase();
|
|
1440
|
-
let attrs = "";
|
|
1441
|
-
if (el.id) attrs += ` id="${el.id}"`;
|
|
1442
|
-
const originalClass = originalClassMap.get(el);
|
|
1443
|
-
const classStr = originalClass != null ? originalClass.trim() : typeof el.className === "string" ? el.className.trim() : "";
|
|
1444
|
-
if (classStr) {
|
|
1445
|
-
attrs += ` class="${classStr}"`;
|
|
1446
|
-
}
|
|
1447
|
-
const text = getInnerText(el);
|
|
1448
|
-
if (!el.id && (!el.className || !el.className.trim()) && !text) {
|
|
1449
|
-
return `${pad}<${tag}>...</${tag}>`;
|
|
1450
|
-
}
|
|
1451
|
-
if (text) {
|
|
1452
|
-
return `${pad}<${tag}${attrs}>
|
|
1453
|
-
${pad} ${text}
|
|
1454
|
-
${pad}</${tag}>`;
|
|
1297
|
+
isOpen() {
|
|
1298
|
+
return this.host !== null;
|
|
1455
1299
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
// overlay/src/containers/PopupContainer.ts
|
|
1303
|
+
var PopupContainer = class {
|
|
1304
|
+
name = "popup";
|
|
1305
|
+
popup = null;
|
|
1306
|
+
open(panelUrl) {
|
|
1307
|
+
if (this.popup && !this.popup.closed) {
|
|
1308
|
+
this.popup.focus();
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
this.popup = window.open(panelUrl, "tw-panel", "popup,width=420,height=700");
|
|
1460
1312
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
let text = "";
|
|
1465
|
-
for (const node of Array.from(el.childNodes)) {
|
|
1466
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
1467
|
-
text += node.textContent || "";
|
|
1313
|
+
close() {
|
|
1314
|
+
if (this.popup && !this.popup.closed) {
|
|
1315
|
+
this.popup.close();
|
|
1468
1316
|
}
|
|
1317
|
+
this.popup = null;
|
|
1469
1318
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
}
|
|
1319
|
+
isOpen() {
|
|
1320
|
+
return this.popup !== null && !this.popup.closed;
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1474
1323
|
|
|
1475
|
-
// overlay/src/
|
|
1476
|
-
var
|
|
1477
|
-
var previewStyleEl = null;
|
|
1478
|
-
var committedStyleEl = null;
|
|
1479
|
-
var previewGeneration = 0;
|
|
1480
|
-
async function applyPreview(elements, oldClass, newClass, serverOrigin) {
|
|
1481
|
-
const gen = ++previewGeneration;
|
|
1482
|
-
if (!previewState) {
|
|
1483
|
-
previewState = {
|
|
1484
|
-
elements,
|
|
1485
|
-
originalClasses: elements.map((n) => n.className)
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
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
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
if (gen !== previewGeneration) return;
|
|
1508
|
-
if (previewState) {
|
|
1509
|
-
for (let i = 0; i < previewState.elements.length; i++) {
|
|
1510
|
-
previewState.elements[i].className = previewState.originalClasses[i];
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
for (const node of elements) {
|
|
1514
|
-
if (oldClass) node.classList.remove(oldClass);
|
|
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
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
function revertPreview() {
|
|
1560
|
-
previewGeneration++;
|
|
1561
|
-
if (previewState) {
|
|
1562
|
-
for (let i = 0; i < previewState.elements.length; i++) {
|
|
1563
|
-
previewState.elements[i].className = previewState.originalClasses[i];
|
|
1564
|
-
}
|
|
1565
|
-
previewState = null;
|
|
1566
|
-
}
|
|
1567
|
-
previewStyleEl?.remove();
|
|
1568
|
-
previewStyleEl = null;
|
|
1569
|
-
}
|
|
1570
|
-
function getPreviewState() {
|
|
1571
|
-
return previewState;
|
|
1572
|
-
}
|
|
1573
|
-
function commitPreview() {
|
|
1574
|
-
previewGeneration++;
|
|
1575
|
-
previewState = null;
|
|
1576
|
-
if (previewStyleEl) {
|
|
1577
|
-
const css = previewStyleEl.textContent || "";
|
|
1578
|
-
if (css) {
|
|
1579
|
-
if (!committedStyleEl) {
|
|
1580
|
-
committedStyleEl = document.createElement("style");
|
|
1581
|
-
committedStyleEl.setAttribute("data-tw-committed", "");
|
|
1582
|
-
document.head.appendChild(committedStyleEl);
|
|
1583
|
-
}
|
|
1584
|
-
committedStyleEl.textContent += css;
|
|
1585
|
-
}
|
|
1586
|
-
previewStyleEl.remove();
|
|
1587
|
-
previewStyleEl = null;
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
// overlay/src/containers/PopoverContainer.ts
|
|
1592
|
-
var PopoverContainer = class {
|
|
1324
|
+
// overlay/src/containers/SidebarContainer.ts
|
|
1325
|
+
var SidebarContainer = class {
|
|
1593
1326
|
constructor(shadowRoot2) {
|
|
1594
1327
|
this.shadowRoot = shadowRoot2;
|
|
1595
1328
|
}
|
|
1596
|
-
name = "
|
|
1329
|
+
name = "sidebar";
|
|
1597
1330
|
host = null;
|
|
1331
|
+
originalPadding = "";
|
|
1332
|
+
width = 380;
|
|
1333
|
+
pageWrapper = null;
|
|
1334
|
+
originalBodyOverflow = "";
|
|
1598
1335
|
open(panelUrl) {
|
|
1599
1336
|
if (this.host) return;
|
|
1337
|
+
this.originalBodyOverflow = document.body.style.overflow;
|
|
1338
|
+
const wrapper = document.createElement("div");
|
|
1339
|
+
wrapper.id = "tw-page-wrapper";
|
|
1340
|
+
wrapper.style.cssText = `
|
|
1341
|
+
position: fixed;
|
|
1342
|
+
top: 0;
|
|
1343
|
+
left: 0;
|
|
1344
|
+
right: ${this.width}px;
|
|
1345
|
+
bottom: 0;
|
|
1346
|
+
overflow: auto;
|
|
1347
|
+
-webkit-overflow-scrolling: touch;
|
|
1348
|
+
background: transparent;
|
|
1349
|
+
z-index: 0;
|
|
1350
|
+
`;
|
|
1351
|
+
const bodyChildren = Array.from(document.body.childNodes);
|
|
1352
|
+
for (const node of bodyChildren) {
|
|
1353
|
+
if (node.id === "tw-visual-editor-host") continue;
|
|
1354
|
+
wrapper.appendChild(node);
|
|
1355
|
+
}
|
|
1356
|
+
const shadowHost2 = document.getElementById("tw-visual-editor-host");
|
|
1357
|
+
if (shadowHost2 && shadowHost2.parentNode) {
|
|
1358
|
+
shadowHost2.parentNode.insertBefore(wrapper, shadowHost2);
|
|
1359
|
+
} else {
|
|
1360
|
+
document.body.appendChild(wrapper);
|
|
1361
|
+
}
|
|
1362
|
+
document.body.style.overflow = "hidden";
|
|
1363
|
+
this.pageWrapper = wrapper;
|
|
1600
1364
|
const host = document.createElement("div");
|
|
1601
|
-
host.className = "container-
|
|
1365
|
+
host.className = "container-sidebar";
|
|
1602
1366
|
host.style.cssText = `
|
|
1603
1367
|
position: fixed;
|
|
1604
1368
|
top: 0;
|
|
1605
1369
|
right: 0;
|
|
1606
|
-
width:
|
|
1370
|
+
width: ${this.width}px;
|
|
1607
1371
|
height: 100vh;
|
|
1608
1372
|
z-index: 999999;
|
|
1609
1373
|
background: #1e1e2e;
|
|
1610
1374
|
box-shadow: -4px 0 24px rgba(0,0,0,0.3);
|
|
1375
|
+
display: flex;
|
|
1611
1376
|
pointer-events: auto;
|
|
1612
1377
|
`;
|
|
1378
|
+
const resizeHandle = document.createElement("div");
|
|
1379
|
+
resizeHandle.style.cssText = `
|
|
1380
|
+
width: 6px;
|
|
1381
|
+
cursor: ew-resize;
|
|
1382
|
+
background: transparent;
|
|
1383
|
+
flex-shrink: 0;
|
|
1384
|
+
`;
|
|
1385
|
+
resizeHandle.addEventListener("mouseenter", () => {
|
|
1386
|
+
resizeHandle.style.background = "#45475a";
|
|
1387
|
+
});
|
|
1388
|
+
resizeHandle.addEventListener("mouseleave", () => {
|
|
1389
|
+
resizeHandle.style.background = "transparent";
|
|
1390
|
+
});
|
|
1391
|
+
this.setupResize(resizeHandle, host);
|
|
1392
|
+
host.appendChild(resizeHandle);
|
|
1613
1393
|
const iframe = document.createElement("iframe");
|
|
1614
1394
|
iframe.src = panelUrl;
|
|
1615
1395
|
iframe.allow = "microphone";
|
|
1616
|
-
iframe.style.cssText = "
|
|
1396
|
+
iframe.style.cssText = "flex:1; border:none; height:100%;";
|
|
1617
1397
|
host.appendChild(iframe);
|
|
1618
1398
|
this.shadowRoot.appendChild(host);
|
|
1619
1399
|
this.host = host;
|
|
@@ -1622,292 +1402,467 @@ ${pad}</${tag}>`;
|
|
|
1622
1402
|
if (this.host) {
|
|
1623
1403
|
this.host.remove();
|
|
1624
1404
|
this.host = null;
|
|
1405
|
+
if (this.pageWrapper) {
|
|
1406
|
+
const children = Array.from(this.pageWrapper.childNodes);
|
|
1407
|
+
const shadowHost2 = document.getElementById("tw-visual-editor-host");
|
|
1408
|
+
for (const node of children) {
|
|
1409
|
+
if (shadowHost2 && shadowHost2.parentNode) {
|
|
1410
|
+
shadowHost2.parentNode.insertBefore(node, shadowHost2);
|
|
1411
|
+
} else {
|
|
1412
|
+
document.body.appendChild(node);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
this.pageWrapper.remove();
|
|
1416
|
+
this.pageWrapper = null;
|
|
1417
|
+
}
|
|
1418
|
+
document.body.style.overflow = this.originalBodyOverflow || "";
|
|
1625
1419
|
}
|
|
1626
1420
|
}
|
|
1627
1421
|
isOpen() {
|
|
1628
1422
|
return this.host !== null;
|
|
1629
1423
|
}
|
|
1424
|
+
setupResize(handle, host) {
|
|
1425
|
+
let startX = 0, startW = 0;
|
|
1426
|
+
const onMove = (e) => {
|
|
1427
|
+
this.width = Math.max(280, startW - (e.clientX - startX));
|
|
1428
|
+
host.style.width = `${this.width}px`;
|
|
1429
|
+
document.documentElement.style.paddingRight = `${this.width}px`;
|
|
1430
|
+
};
|
|
1431
|
+
const onUp = () => {
|
|
1432
|
+
document.removeEventListener("mousemove", onMove);
|
|
1433
|
+
document.removeEventListener("mouseup", onUp);
|
|
1434
|
+
};
|
|
1435
|
+
handle.addEventListener("mousedown", (e) => {
|
|
1436
|
+
e.preventDefault();
|
|
1437
|
+
startX = e.clientX;
|
|
1438
|
+
startW = this.width;
|
|
1439
|
+
document.addEventListener("mousemove", onMove);
|
|
1440
|
+
document.addEventListener("mouseup", onUp);
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1630
1443
|
};
|
|
1631
1444
|
|
|
1632
|
-
// overlay/src/
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1445
|
+
// overlay/src/context.ts
|
|
1446
|
+
function buildContext(target, oldClass, newClass, originalClassMap) {
|
|
1447
|
+
const ancestors = [];
|
|
1448
|
+
let current = target;
|
|
1449
|
+
while (current && current !== document.documentElement) {
|
|
1450
|
+
ancestors.push(current);
|
|
1451
|
+
current = current.parentElement;
|
|
1639
1452
|
}
|
|
1640
|
-
|
|
1453
|
+
ancestors.reverse();
|
|
1454
|
+
return buildLevel(ancestors, 0, target, oldClass, newClass, originalClassMap, 0);
|
|
1641
1455
|
}
|
|
1642
|
-
function
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1456
|
+
function buildLevel(ancestors, ancestorIndex, target, oldClass, newClass, originalClassMap, indent) {
|
|
1457
|
+
const el = ancestors[ancestorIndex];
|
|
1458
|
+
const pad = " ".repeat(indent);
|
|
1459
|
+
const tag = el.tagName.toLowerCase();
|
|
1460
|
+
let attrs = "";
|
|
1461
|
+
if (el.id) attrs += ` id="${el.id}"`;
|
|
1462
|
+
const originalClass = originalClassMap.get(el);
|
|
1463
|
+
const classStr = originalClass != null ? originalClass.trim() : typeof el.className === "string" ? el.className.trim() : "";
|
|
1464
|
+
if (classStr) {
|
|
1465
|
+
attrs += ` class="${classStr}"`;
|
|
1646
1466
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1467
|
+
const isTarget = el === target;
|
|
1468
|
+
if (isTarget) {
|
|
1469
|
+
const text = getInnerText(el);
|
|
1470
|
+
const textNode = text ? `
|
|
1471
|
+
${pad} ${text}` : "";
|
|
1472
|
+
return `${pad}<${tag}${attrs}> <!-- TARGET: change ${oldClass} \u2192 ${newClass} -->${textNode}
|
|
1473
|
+
${pad}</${tag}>`;
|
|
1651
1474
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
bounds = loadBounds();
|
|
1655
|
-
open(panelUrl) {
|
|
1656
|
-
if (this.host) return;
|
|
1657
|
-
this.bounds = loadBounds();
|
|
1658
|
-
const host = document.createElement("div");
|
|
1659
|
-
host.className = "container-modal";
|
|
1660
|
-
this.applyBounds(host);
|
|
1661
|
-
host.style.position = "fixed";
|
|
1662
|
-
host.style.zIndex = "999999";
|
|
1663
|
-
host.style.background = "#1e1e2e";
|
|
1664
|
-
host.style.borderRadius = "8px";
|
|
1665
|
-
host.style.boxShadow = "0 8px 32px rgba(0,0,0,0.4)";
|
|
1666
|
-
host.style.display = "flex";
|
|
1667
|
-
host.style.flexDirection = "column";
|
|
1668
|
-
host.style.overflow = "hidden";
|
|
1669
|
-
host.style.pointerEvents = "auto";
|
|
1670
|
-
const handle = document.createElement("div");
|
|
1671
|
-
handle.style.cssText = `
|
|
1672
|
-
height: 28px;
|
|
1673
|
-
background: #181825;
|
|
1674
|
-
cursor: move;
|
|
1675
|
-
display: flex;
|
|
1676
|
-
align-items: center;
|
|
1677
|
-
justify-content: center;
|
|
1678
|
-
flex-shrink: 0;
|
|
1679
|
-
user-select: none;
|
|
1680
|
-
`;
|
|
1681
|
-
handle.innerHTML = '<span style="color:#585b70;font-size:11px;letter-spacing:2px;">\u22EF\u22EF\u22EF</span>';
|
|
1682
|
-
this.setupDrag(handle, host);
|
|
1683
|
-
host.appendChild(handle);
|
|
1684
|
-
const iframe = document.createElement("iframe");
|
|
1685
|
-
iframe.src = panelUrl;
|
|
1686
|
-
iframe.allow = "microphone";
|
|
1687
|
-
iframe.style.cssText = "flex:1; border:none; width:100%;";
|
|
1688
|
-
host.appendChild(iframe);
|
|
1689
|
-
const gripper = document.createElement("div");
|
|
1690
|
-
gripper.style.cssText = `
|
|
1691
|
-
position: absolute;
|
|
1692
|
-
bottom: 0;
|
|
1693
|
-
right: 0;
|
|
1694
|
-
width: 16px;
|
|
1695
|
-
height: 16px;
|
|
1696
|
-
cursor: nwse-resize;
|
|
1697
|
-
`;
|
|
1698
|
-
gripper.innerHTML = '<span style="position:absolute;bottom:2px;right:4px;color:#585b70;font-size:10px;">\u25E2</span>';
|
|
1699
|
-
this.setupResize(gripper, host, iframe);
|
|
1700
|
-
host.appendChild(gripper);
|
|
1701
|
-
this.shadowRoot.appendChild(host);
|
|
1702
|
-
this.host = host;
|
|
1475
|
+
if (ancestorIndex >= ancestors.length - 1) {
|
|
1476
|
+
return `${pad}<${tag}${attrs} />`;
|
|
1703
1477
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1478
|
+
const nextAncestor = ancestors[ancestorIndex + 1];
|
|
1479
|
+
const children = Array.from(el.children);
|
|
1480
|
+
const relevantIndex = children.indexOf(nextAncestor);
|
|
1481
|
+
let inner = "";
|
|
1482
|
+
if (relevantIndex === -1) {
|
|
1483
|
+
inner = buildLevel(ancestors, ancestorIndex + 1, target, oldClass, newClass, originalClassMap, indent + 1);
|
|
1484
|
+
} else {
|
|
1485
|
+
const start = Math.max(0, relevantIndex - 3);
|
|
1486
|
+
const end = Math.min(children.length - 1, relevantIndex + 3);
|
|
1487
|
+
if (start > 0) {
|
|
1488
|
+
inner += `${pad} ...
|
|
1489
|
+
`;
|
|
1490
|
+
}
|
|
1491
|
+
for (let i = start; i <= end; i++) {
|
|
1492
|
+
if (i === relevantIndex) {
|
|
1493
|
+
inner += buildLevel(ancestors, ancestorIndex + 1, target, oldClass, newClass, originalClassMap, indent + 1) + "\n";
|
|
1494
|
+
} else {
|
|
1495
|
+
inner += renderSiblingNode(children[i], indent + 1, originalClassMap) + "\n";
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
if (end < children.length - 1) {
|
|
1499
|
+
inner += `${pad} ...
|
|
1500
|
+
`;
|
|
1708
1501
|
}
|
|
1709
1502
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1503
|
+
return `${pad}<${tag}${attrs}>
|
|
1504
|
+
${inner}${pad}</${tag}>`;
|
|
1505
|
+
}
|
|
1506
|
+
function renderSiblingNode(el, indent, originalClassMap) {
|
|
1507
|
+
const pad = " ".repeat(indent);
|
|
1508
|
+
const tag = el.tagName.toLowerCase();
|
|
1509
|
+
let attrs = "";
|
|
1510
|
+
if (el.id) attrs += ` id="${el.id}"`;
|
|
1511
|
+
const originalClass = originalClassMap.get(el);
|
|
1512
|
+
const classStr = originalClass != null ? originalClass.trim() : typeof el.className === "string" ? el.className.trim() : "";
|
|
1513
|
+
if (classStr) {
|
|
1514
|
+
attrs += ` class="${classStr}"`;
|
|
1712
1515
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
el.style.width = `${this.bounds.width}px`;
|
|
1717
|
-
el.style.height = `${this.bounds.height}px`;
|
|
1516
|
+
const text = getInnerText(el);
|
|
1517
|
+
if (!el.id && (!el.className || !el.className.trim()) && !text) {
|
|
1518
|
+
return `${pad}<${tag}>...</${tag}>`;
|
|
1718
1519
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
this.bounds.top = startTop + (e.clientY - startY);
|
|
1724
|
-
host.style.left = `${this.bounds.left}px`;
|
|
1725
|
-
host.style.top = `${this.bounds.top}px`;
|
|
1726
|
-
};
|
|
1727
|
-
const onUp = () => {
|
|
1728
|
-
document.removeEventListener("mousemove", onMove);
|
|
1729
|
-
document.removeEventListener("mouseup", onUp);
|
|
1730
|
-
saveBounds(this.bounds);
|
|
1731
|
-
};
|
|
1732
|
-
handle.addEventListener("mousedown", (e) => {
|
|
1733
|
-
e.preventDefault();
|
|
1734
|
-
startX = e.clientX;
|
|
1735
|
-
startY = e.clientY;
|
|
1736
|
-
startLeft = this.bounds.left;
|
|
1737
|
-
startTop = this.bounds.top;
|
|
1738
|
-
document.addEventListener("mousemove", onMove);
|
|
1739
|
-
document.addEventListener("mouseup", onUp);
|
|
1740
|
-
});
|
|
1520
|
+
if (text) {
|
|
1521
|
+
return `${pad}<${tag}${attrs}>
|
|
1522
|
+
${pad} ${text}
|
|
1523
|
+
${pad}</${tag}>`;
|
|
1741
1524
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
this.bounds.height = Math.max(200, startH + (e.clientY - startY));
|
|
1747
|
-
host.style.width = `${this.bounds.width}px`;
|
|
1748
|
-
host.style.height = `${this.bounds.height}px`;
|
|
1749
|
-
};
|
|
1750
|
-
const onUp = () => {
|
|
1751
|
-
iframe.style.pointerEvents = "";
|
|
1752
|
-
document.removeEventListener("mousemove", onMove);
|
|
1753
|
-
document.removeEventListener("mouseup", onUp);
|
|
1754
|
-
saveBounds(this.bounds);
|
|
1755
|
-
};
|
|
1756
|
-
gripper.addEventListener("mousedown", (e) => {
|
|
1757
|
-
e.preventDefault();
|
|
1758
|
-
iframe.style.pointerEvents = "none";
|
|
1759
|
-
startX = e.clientX;
|
|
1760
|
-
startY = e.clientY;
|
|
1761
|
-
startW = this.bounds.width;
|
|
1762
|
-
startH = this.bounds.height;
|
|
1763
|
-
document.addEventListener("mousemove", onMove);
|
|
1764
|
-
document.addEventListener("mouseup", onUp);
|
|
1765
|
-
});
|
|
1525
|
+
if (el.children.length > 0) {
|
|
1526
|
+
return `${pad}<${tag}${attrs}>
|
|
1527
|
+
${pad} ...
|
|
1528
|
+
${pad}</${tag}>`;
|
|
1766
1529
|
}
|
|
1767
|
-
|
|
1530
|
+
return `${pad}<${tag}${attrs} />`;
|
|
1531
|
+
}
|
|
1532
|
+
function getInnerText(el) {
|
|
1533
|
+
let text = "";
|
|
1534
|
+
for (const node of Array.from(el.childNodes)) {
|
|
1535
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
1536
|
+
text += node.textContent || "";
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
text = text.trim();
|
|
1540
|
+
if (text.length > 60) text = text.slice(0, 57) + "...";
|
|
1541
|
+
return text;
|
|
1542
|
+
}
|
|
1768
1543
|
|
|
1769
|
-
// overlay/src/
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1544
|
+
// overlay/src/fiber.ts
|
|
1545
|
+
function getFiber(domNode) {
|
|
1546
|
+
const key = Object.keys(domNode).find((k) => k.startsWith("__reactFiber$"));
|
|
1547
|
+
return key ? domNode[key] : null;
|
|
1548
|
+
}
|
|
1549
|
+
function findComponentBoundary(fiber) {
|
|
1550
|
+
let current = fiber.return;
|
|
1551
|
+
while (current) {
|
|
1552
|
+
if (typeof current.type === "function") {
|
|
1553
|
+
return {
|
|
1554
|
+
componentType: current.type,
|
|
1555
|
+
componentName: current.type.displayName || current.type.name || "Unknown",
|
|
1556
|
+
componentFiber: current
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
current = current.return;
|
|
1773
1560
|
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
left: 0;
|
|
1789
|
-
right: ${this.width}px;
|
|
1790
|
-
bottom: 0;
|
|
1791
|
-
overflow: auto;
|
|
1792
|
-
-webkit-overflow-scrolling: touch;
|
|
1793
|
-
background: transparent;
|
|
1794
|
-
z-index: 0;
|
|
1795
|
-
`;
|
|
1796
|
-
const bodyChildren = Array.from(document.body.childNodes);
|
|
1797
|
-
for (const node of bodyChildren) {
|
|
1798
|
-
if (node.id === "tw-visual-editor-host") continue;
|
|
1799
|
-
wrapper.appendChild(node);
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
function getRootFiber() {
|
|
1564
|
+
const candidateIds = ["root", "app", "__next"];
|
|
1565
|
+
for (const id of candidateIds) {
|
|
1566
|
+
const el = document.getElementById(id);
|
|
1567
|
+
if (!el) continue;
|
|
1568
|
+
const key = Object.keys(el).find((k) => k.startsWith("__reactContainer$"));
|
|
1569
|
+
if (key) {
|
|
1570
|
+
const container = el[key];
|
|
1571
|
+
if (container?.stateNode?.current) {
|
|
1572
|
+
return container.stateNode.current;
|
|
1573
|
+
}
|
|
1574
|
+
return container;
|
|
1800
1575
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1576
|
+
}
|
|
1577
|
+
const reactRoot = document.querySelector("[data-reactroot]");
|
|
1578
|
+
if (reactRoot) {
|
|
1579
|
+
return getFiber(reactRoot);
|
|
1580
|
+
}
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1583
|
+
function findAllInstances(rootFiber, componentType) {
|
|
1584
|
+
const results = [];
|
|
1585
|
+
function walk(fiber) {
|
|
1586
|
+
if (!fiber) return;
|
|
1587
|
+
if (fiber.type === componentType) {
|
|
1588
|
+
results.push(fiber);
|
|
1589
|
+
}
|
|
1590
|
+
walk(fiber.child);
|
|
1591
|
+
walk(fiber.sibling);
|
|
1592
|
+
}
|
|
1593
|
+
walk(rootFiber);
|
|
1594
|
+
return results;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// overlay/src/grouping.ts
|
|
1598
|
+
function cssEscape(cls) {
|
|
1599
|
+
if (typeof CSS !== "undefined" && CSS.escape) return CSS.escape(cls);
|
|
1600
|
+
return cls.replace(/([^\w-])/g, "\\$1");
|
|
1601
|
+
}
|
|
1602
|
+
function buildSelector(tag, classes) {
|
|
1603
|
+
return tag.toLowerCase() + classes.map((c) => `.${cssEscape(c)}`).join("");
|
|
1604
|
+
}
|
|
1605
|
+
function parseClassList(className) {
|
|
1606
|
+
if (typeof className !== "string") return [];
|
|
1607
|
+
return className.trim().split(/\s+/).filter(Boolean).sort();
|
|
1608
|
+
}
|
|
1609
|
+
function classDiff(refClasses, candidateClasses) {
|
|
1610
|
+
const added = [];
|
|
1611
|
+
const removed = [];
|
|
1612
|
+
for (const c of candidateClasses) {
|
|
1613
|
+
if (!refClasses.has(c)) added.push(c);
|
|
1614
|
+
}
|
|
1615
|
+
for (const c of refClasses) {
|
|
1616
|
+
if (!candidateClasses.has(c)) removed.push(c);
|
|
1617
|
+
}
|
|
1618
|
+
added.sort();
|
|
1619
|
+
removed.sort();
|
|
1620
|
+
return { added, removed };
|
|
1621
|
+
}
|
|
1622
|
+
function diffLabel(added, removed) {
|
|
1623
|
+
const parts = [];
|
|
1624
|
+
for (const a of added) parts.push(`+${a}`);
|
|
1625
|
+
for (const r of removed) parts.push(`-${r}`);
|
|
1626
|
+
return parts.join(" ");
|
|
1627
|
+
}
|
|
1628
|
+
var MAX_DIFF = 10;
|
|
1629
|
+
var MAX_CANDIDATES = 200;
|
|
1630
|
+
function findExactMatches(clickedEl, shadowHost2) {
|
|
1631
|
+
const classes = parseClassList(typeof clickedEl.className === "string" ? clickedEl.className : "");
|
|
1632
|
+
const tag = clickedEl.tagName;
|
|
1633
|
+
const fiber = getFiber(clickedEl);
|
|
1634
|
+
const boundary = fiber ? findComponentBoundary(fiber) : null;
|
|
1635
|
+
const componentName = boundary?.componentName ?? null;
|
|
1636
|
+
let exactMatches;
|
|
1637
|
+
if (boundary) {
|
|
1638
|
+
const rootFiber = getRootFiber();
|
|
1639
|
+
const allNodes = rootFiber ? collectComponentDOMNodes(rootFiber, boundary.componentType, tag) : [];
|
|
1640
|
+
exactMatches = allNodes.filter(
|
|
1641
|
+
(n) => n.tagName === tag && n.className === clickedEl.className
|
|
1642
|
+
);
|
|
1643
|
+
} else {
|
|
1644
|
+
if (classes.length === 0) {
|
|
1645
|
+
exactMatches = Array.from(
|
|
1646
|
+
document.querySelectorAll(tag.toLowerCase())
|
|
1647
|
+
).filter(
|
|
1648
|
+
(n) => (typeof n.className === "string" ? n.className.trim() : "") === "" && !isInShadowHost(n, shadowHost2)
|
|
1649
|
+
);
|
|
1804
1650
|
} else {
|
|
1805
|
-
|
|
1651
|
+
const selector = buildSelector(tag, classes);
|
|
1652
|
+
exactMatches = Array.from(
|
|
1653
|
+
document.querySelectorAll(selector)
|
|
1654
|
+
).filter(
|
|
1655
|
+
(n) => n.className === clickedEl.className && !isInShadowHost(n, shadowHost2)
|
|
1656
|
+
);
|
|
1806
1657
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1658
|
+
}
|
|
1659
|
+
if (!exactMatches.includes(clickedEl)) {
|
|
1660
|
+
exactMatches.unshift(clickedEl);
|
|
1661
|
+
}
|
|
1662
|
+
return {
|
|
1663
|
+
exactMatch: exactMatches,
|
|
1664
|
+
nearGroups: [],
|
|
1665
|
+
// Not computed yet — lazy
|
|
1666
|
+
componentName
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
function computeNearGroups(clickedEl, exactMatchSet, shadowHost2) {
|
|
1670
|
+
const rawClassName = typeof clickedEl.className === "string" ? clickedEl.className : "";
|
|
1671
|
+
const classes = parseClassList(rawClassName);
|
|
1672
|
+
const tag = clickedEl.tagName;
|
|
1673
|
+
const refSet = new Set(classes);
|
|
1674
|
+
if (classes.length === 0) return [];
|
|
1675
|
+
const fiber = getFiber(clickedEl);
|
|
1676
|
+
const boundary = fiber ? findComponentBoundary(fiber) : null;
|
|
1677
|
+
let candidates;
|
|
1678
|
+
if (boundary) {
|
|
1679
|
+
const rootFiber = getRootFiber();
|
|
1680
|
+
candidates = rootFiber ? collectComponentDOMNodes(rootFiber, boundary.componentType, tag) : [];
|
|
1681
|
+
candidates = candidates.filter((n) => !exactMatchSet.has(n));
|
|
1682
|
+
} else {
|
|
1683
|
+
const seen = new Set(exactMatchSet);
|
|
1684
|
+
candidates = [];
|
|
1685
|
+
for (const cls of classes) {
|
|
1686
|
+
const sel = `${tag.toLowerCase()}.${cssEscape(cls)}`;
|
|
1687
|
+
for (const n of document.querySelectorAll(sel)) {
|
|
1688
|
+
if (!seen.has(n) && !isInShadowHost(n, shadowHost2)) {
|
|
1689
|
+
seen.add(n);
|
|
1690
|
+
candidates.push(n);
|
|
1691
|
+
if (candidates.length >= MAX_CANDIDATES) break;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
if (candidates.length >= MAX_CANDIDATES) break;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1698
|
+
for (const el of candidates) {
|
|
1699
|
+
const candidateClasses = new Set(parseClassList(typeof el.className === "string" ? el.className : ""));
|
|
1700
|
+
const { added, removed } = classDiff(refSet, candidateClasses);
|
|
1701
|
+
const totalDiff = added.length + removed.length;
|
|
1702
|
+
if (totalDiff === 0 || totalDiff > MAX_DIFF) continue;
|
|
1703
|
+
const key = `+${added.join(",")}|-${removed.join(",")}`;
|
|
1704
|
+
const existing = groupMap.get(key);
|
|
1705
|
+
if (existing) {
|
|
1706
|
+
existing.elements.push(el);
|
|
1707
|
+
} else {
|
|
1708
|
+
groupMap.set(key, { added, removed, elements: [el] });
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
const groups = [];
|
|
1712
|
+
for (const [, g] of groupMap) {
|
|
1713
|
+
groups.push({
|
|
1714
|
+
label: diffLabel(g.added, g.removed),
|
|
1715
|
+
added: g.added,
|
|
1716
|
+
removed: g.removed,
|
|
1717
|
+
elements: g.elements
|
|
1835
1718
|
});
|
|
1836
|
-
this.setupResize(resizeHandle, host);
|
|
1837
|
-
host.appendChild(resizeHandle);
|
|
1838
|
-
const iframe = document.createElement("iframe");
|
|
1839
|
-
iframe.src = panelUrl;
|
|
1840
|
-
iframe.allow = "microphone";
|
|
1841
|
-
iframe.style.cssText = "flex:1; border:none; height:100%;";
|
|
1842
|
-
host.appendChild(iframe);
|
|
1843
|
-
this.shadowRoot.appendChild(host);
|
|
1844
|
-
this.host = host;
|
|
1845
1719
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1720
|
+
groups.sort((a, b) => {
|
|
1721
|
+
const diffA = a.added.length + a.removed.length;
|
|
1722
|
+
const diffB = b.added.length + b.removed.length;
|
|
1723
|
+
if (diffA !== diffB) return diffA - diffB;
|
|
1724
|
+
return b.elements.length - a.elements.length;
|
|
1725
|
+
});
|
|
1726
|
+
return groups;
|
|
1727
|
+
}
|
|
1728
|
+
function collectComponentDOMNodes(rootFiber, componentType, tagName) {
|
|
1729
|
+
const instances = findAllInstances(rootFiber, componentType);
|
|
1730
|
+
const results = [];
|
|
1731
|
+
for (const inst of instances) {
|
|
1732
|
+
collectHostNodes(inst.child, tagName, results);
|
|
1733
|
+
}
|
|
1734
|
+
return results;
|
|
1735
|
+
}
|
|
1736
|
+
function collectHostNodes(fiber, tagName, out) {
|
|
1737
|
+
if (!fiber) return;
|
|
1738
|
+
if (fiber.tag === 5 && fiber.stateNode instanceof HTMLElement) {
|
|
1739
|
+
if (fiber.stateNode.tagName === tagName) {
|
|
1740
|
+
out.push(fiber.stateNode);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
collectHostNodes(fiber.child, tagName, out);
|
|
1744
|
+
collectHostNodes(fiber.sibling, tagName, out);
|
|
1745
|
+
}
|
|
1746
|
+
function isInShadowHost(el, shadowHost2) {
|
|
1747
|
+
if (!shadowHost2) return false;
|
|
1748
|
+
return shadowHost2.contains(el);
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
// overlay/src/patcher.ts
|
|
1752
|
+
var previewState = null;
|
|
1753
|
+
var previewStyleEl = null;
|
|
1754
|
+
var committedStyleEl = null;
|
|
1755
|
+
var previewGeneration = 0;
|
|
1756
|
+
async function applyPreview(elements, oldClass, newClass, serverOrigin) {
|
|
1757
|
+
const gen = ++previewGeneration;
|
|
1758
|
+
if (!previewState) {
|
|
1759
|
+
previewState = {
|
|
1760
|
+
elements,
|
|
1761
|
+
originalClasses: elements.map((n) => n.className)
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
if (newClass) {
|
|
1765
|
+
try {
|
|
1766
|
+
const res = await fetch(`${serverOrigin}/css`, {
|
|
1767
|
+
method: "POST",
|
|
1768
|
+
headers: { "Content-Type": "application/json" },
|
|
1769
|
+
body: JSON.stringify({ classes: [newClass] })
|
|
1770
|
+
});
|
|
1771
|
+
if (gen !== previewGeneration) return;
|
|
1772
|
+
const { css } = await res.json();
|
|
1773
|
+
if (gen !== previewGeneration) return;
|
|
1774
|
+
if (!previewStyleEl) {
|
|
1775
|
+
previewStyleEl = document.createElement("style");
|
|
1776
|
+
previewStyleEl.setAttribute("data-tw-preview", "");
|
|
1777
|
+
document.head.appendChild(previewStyleEl);
|
|
1862
1778
|
}
|
|
1863
|
-
|
|
1779
|
+
previewStyleEl.textContent = css;
|
|
1780
|
+
} catch {
|
|
1864
1781
|
}
|
|
1865
1782
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1783
|
+
if (gen !== previewGeneration) return;
|
|
1784
|
+
if (previewState) {
|
|
1785
|
+
for (let i = 0; i < previewState.elements.length; i++) {
|
|
1786
|
+
previewState.elements[i].className = previewState.originalClasses[i];
|
|
1787
|
+
}
|
|
1868
1788
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1789
|
+
for (const node of elements) {
|
|
1790
|
+
if (oldClass) node.classList.remove(oldClass);
|
|
1791
|
+
if (newClass) node.classList.add(newClass);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
async function applyPreviewBatch(elements, pairs, serverOrigin) {
|
|
1795
|
+
const gen = ++previewGeneration;
|
|
1796
|
+
if (!previewState) {
|
|
1797
|
+
previewState = {
|
|
1798
|
+
elements,
|
|
1799
|
+
originalClasses: elements.map((n) => n.className)
|
|
1879
1800
|
};
|
|
1880
|
-
handle.addEventListener("mousedown", (e) => {
|
|
1881
|
-
e.preventDefault();
|
|
1882
|
-
startX = e.clientX;
|
|
1883
|
-
startW = this.width;
|
|
1884
|
-
document.addEventListener("mousemove", onMove);
|
|
1885
|
-
document.addEventListener("mouseup", onUp);
|
|
1886
|
-
});
|
|
1887
1801
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1802
|
+
const newClasses = pairs.map((p) => p.newClass).filter(Boolean);
|
|
1803
|
+
if (newClasses.length > 0) {
|
|
1804
|
+
try {
|
|
1805
|
+
const res = await fetch(`${serverOrigin}/css`, {
|
|
1806
|
+
method: "POST",
|
|
1807
|
+
headers: { "Content-Type": "application/json" },
|
|
1808
|
+
body: JSON.stringify({ classes: newClasses })
|
|
1809
|
+
});
|
|
1810
|
+
if (gen !== previewGeneration) return;
|
|
1811
|
+
const { css } = await res.json();
|
|
1812
|
+
if (gen !== previewGeneration) return;
|
|
1813
|
+
if (!previewStyleEl) {
|
|
1814
|
+
previewStyleEl = document.createElement("style");
|
|
1815
|
+
previewStyleEl.setAttribute("data-tw-preview", "");
|
|
1816
|
+
document.head.appendChild(previewStyleEl);
|
|
1817
|
+
}
|
|
1818
|
+
previewStyleEl.textContent = css;
|
|
1819
|
+
} catch {
|
|
1898
1820
|
}
|
|
1899
|
-
this.popup = window.open(panelUrl, "tw-panel", "popup,width=420,height=700");
|
|
1900
1821
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1822
|
+
if (gen !== previewGeneration) return;
|
|
1823
|
+
if (previewState) {
|
|
1824
|
+
for (let i = 0; i < previewState.elements.length; i++) {
|
|
1825
|
+
previewState.elements[i].className = previewState.originalClasses[i];
|
|
1904
1826
|
}
|
|
1905
|
-
this.popup = null;
|
|
1906
1827
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1828
|
+
for (const node of elements) {
|
|
1829
|
+
for (const { oldClass, newClass } of pairs) {
|
|
1830
|
+
if (oldClass) node.classList.remove(oldClass);
|
|
1831
|
+
if (newClass) node.classList.add(newClass);
|
|
1832
|
+
}
|
|
1909
1833
|
}
|
|
1910
|
-
}
|
|
1834
|
+
}
|
|
1835
|
+
function revertPreview() {
|
|
1836
|
+
previewGeneration++;
|
|
1837
|
+
if (previewState) {
|
|
1838
|
+
for (let i = 0; i < previewState.elements.length; i++) {
|
|
1839
|
+
previewState.elements[i].className = previewState.originalClasses[i];
|
|
1840
|
+
}
|
|
1841
|
+
previewState = null;
|
|
1842
|
+
}
|
|
1843
|
+
previewStyleEl?.remove();
|
|
1844
|
+
previewStyleEl = null;
|
|
1845
|
+
}
|
|
1846
|
+
function getPreviewState() {
|
|
1847
|
+
return previewState;
|
|
1848
|
+
}
|
|
1849
|
+
function commitPreview() {
|
|
1850
|
+
previewGeneration++;
|
|
1851
|
+
previewState = null;
|
|
1852
|
+
if (previewStyleEl) {
|
|
1853
|
+
const css = previewStyleEl.textContent || "";
|
|
1854
|
+
if (css) {
|
|
1855
|
+
if (!committedStyleEl) {
|
|
1856
|
+
committedStyleEl = document.createElement("style");
|
|
1857
|
+
committedStyleEl.setAttribute("data-tw-committed", "");
|
|
1858
|
+
document.head.appendChild(committedStyleEl);
|
|
1859
|
+
}
|
|
1860
|
+
committedStyleEl.textContent += css;
|
|
1861
|
+
}
|
|
1862
|
+
previewStyleEl.remove();
|
|
1863
|
+
previewStyleEl = null;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1911
1866
|
|
|
1912
1867
|
// node_modules/html-to-image/es/util.js
|
|
1913
1868
|
function resolveUrl(url, baseUrl) {
|
|
@@ -2748,6 +2703,51 @@ ${pad}</${tag}>`;
|
|
|
2748
2703
|
}
|
|
2749
2704
|
}
|
|
2750
2705
|
|
|
2706
|
+
// overlay/src/ws.ts
|
|
2707
|
+
var socket = null;
|
|
2708
|
+
var connected = false;
|
|
2709
|
+
var handlers = [];
|
|
2710
|
+
function connect(url = "ws://localhost:3333") {
|
|
2711
|
+
socket = new WebSocket(url);
|
|
2712
|
+
socket.addEventListener("open", () => {
|
|
2713
|
+
connected = true;
|
|
2714
|
+
send({ type: "REGISTER", role: "overlay" });
|
|
2715
|
+
window.dispatchEvent(new CustomEvent("overlay-ws-connected"));
|
|
2716
|
+
});
|
|
2717
|
+
socket.addEventListener("close", () => {
|
|
2718
|
+
connected = false;
|
|
2719
|
+
socket = null;
|
|
2720
|
+
window.dispatchEvent(new CustomEvent("overlay-ws-disconnected"));
|
|
2721
|
+
setTimeout(() => connect(url), 3e3);
|
|
2722
|
+
});
|
|
2723
|
+
socket.addEventListener("message", (event) => {
|
|
2724
|
+
try {
|
|
2725
|
+
const data = JSON.parse(event.data);
|
|
2726
|
+
for (const handler of handlers) {
|
|
2727
|
+
handler(data);
|
|
2728
|
+
}
|
|
2729
|
+
} catch (err) {
|
|
2730
|
+
console.error("[tw-overlay] Failed to parse message:", err);
|
|
2731
|
+
}
|
|
2732
|
+
});
|
|
2733
|
+
socket.addEventListener("error", (err) => {
|
|
2734
|
+
console.error("[tw-overlay] WebSocket error:", err);
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
function send(data) {
|
|
2738
|
+
if (connected && socket) {
|
|
2739
|
+
socket.send(JSON.stringify(data));
|
|
2740
|
+
} else {
|
|
2741
|
+
console.warn("[tw-overlay] Cannot send \u2014 not connected");
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
function onMessage(handler) {
|
|
2745
|
+
handlers.push(handler);
|
|
2746
|
+
}
|
|
2747
|
+
function sendTo(role, data) {
|
|
2748
|
+
send({ ...data, to: role });
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
2751
|
// overlay/src/index.ts
|
|
2752
2752
|
var shadowRoot;
|
|
2753
2753
|
var shadowHost;
|
|
@@ -2930,11 +2930,40 @@ ${pad}</${tag}>`;
|
|
|
2930
2930
|
z-index: 999998;
|
|
2931
2931
|
}
|
|
2932
2932
|
/* \u2500\u2500 Group picker popover (replaces instance picker) \u2500\u2500 */
|
|
2933
|
+
.el-group-exact {
|
|
2934
|
+
display: flex;
|
|
2935
|
+
align-items: center;
|
|
2936
|
+
gap: 8px;
|
|
2937
|
+
padding: 8px 12px;
|
|
2938
|
+
font-size: 11px;
|
|
2939
|
+
color: #A0ABAB;
|
|
2940
|
+
}
|
|
2941
|
+
.el-group-exact .el-count-chip {
|
|
2942
|
+
display: inline-flex;
|
|
2943
|
+
align-items: center;
|
|
2944
|
+
justify-content: center;
|
|
2945
|
+
min-width: 20px;
|
|
2946
|
+
padding: 1px 6px;
|
|
2947
|
+
font-size: 10px;
|
|
2948
|
+
font-weight: 600;
|
|
2949
|
+
color: #fff;
|
|
2950
|
+
background: #00848B;
|
|
2951
|
+
border-radius: 9999px;
|
|
2952
|
+
}
|
|
2953
|
+
.el-group-divider {
|
|
2954
|
+
padding: 6px 12px 4px;
|
|
2955
|
+
font-size: 10px;
|
|
2956
|
+
font-weight: 700;
|
|
2957
|
+
text-transform: uppercase;
|
|
2958
|
+
letter-spacing: 0.05em;
|
|
2959
|
+
color: #687879;
|
|
2960
|
+
border-top: 1px solid #DFE2E2;
|
|
2961
|
+
}
|
|
2933
2962
|
.el-group-empty {
|
|
2934
2963
|
padding: 12px 14px;
|
|
2935
2964
|
font-size: 11px;
|
|
2936
2965
|
color: #687879;
|
|
2937
|
-
text-align:
|
|
2966
|
+
text-align: left;
|
|
2938
2967
|
}
|
|
2939
2968
|
.el-group-row {
|
|
2940
2969
|
display: flex;
|
|
@@ -3231,11 +3260,8 @@ ${pad}</${tag}>`;
|
|
|
3231
3260
|
const label = boundary?.componentName ?? target.tagName.toLowerCase();
|
|
3232
3261
|
showHoverPreview(target, label);
|
|
3233
3262
|
}
|
|
3234
|
-
var PENCIL_SVG = `<svg width="14" height="14" viewBox="0 0
|
|
3235
|
-
|
|
3236
|
-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
3237
|
-
</svg>`;
|
|
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>`;
|
|
3263
|
+
var PENCIL_SVG = `<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M15,0H1C0.448,0,0,0.448,0,1v9c0,0.552,0.448,1,1,1h2.882l-1.776,3.553c-0.247,0.494-0.047,1.095,0.447,1.342C2.696,15.966,2.849,16,2.999,16c0.367,0,0.72-0.202,0.896-0.553L4.618,14h6.764l0.724,1.447C12.281,15.798,12.634,16,13.001,16c0.15,0,0.303-0.034,0.446-0.105c0.494-0.247,0.694-0.848,0.447-1.342L12.118,11H15c0.552,0,1-0.448,1-1V1C16,0.448,15.552,0,15,0z M5.618,12l0.5-1h3.764l0.5,1H5.618z M14,9H2V2h12V9z"/></svg>`;
|
|
3264
|
+
var RESELECT_SVG = `<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M14,0H2C.895,0,0,.895,0,2V14c0,1.105,.895,2,2,2H6c.552,0,1-.448,1-1h0c0-.552-.448-1-1-1H2V2H14V6c0,.552,.448,1,1,1h0c.552,0,1-.448,1-1V2c0-1.105-.895-2-2-2Z"/><path d="M12.043,10.629l2.578-.644c.268-.068,.43-.339,.362-.607-.043-.172-.175-.308-.345-.358l-7-2c-.175-.051-.363-.002-.492,.126-.128,.129-.177,.317-.126,.492l2,7c.061,.214,.257,.362,.48,.362h.009c.226-.004,.421-.16,.476-.379l.644-2.578,3.664,3.664c.397,.384,1.03,.373,1.414-.025,.374-.388,.374-1.002,0-1.389l-3.664-3.664Z"/></svg>`;
|
|
3239
3265
|
async function positionWithFlip(anchor, floating, placement = "top-start") {
|
|
3240
3266
|
const { x, y } = await computePosition2(anchor, floating, {
|
|
3241
3267
|
placement,
|
|
@@ -3311,7 +3337,9 @@ ${pad}</${tag}>`;
|
|
|
3311
3337
|
}
|
|
3312
3338
|
function showGroupPicker(anchorBtn, onClose, onCountChange) {
|
|
3313
3339
|
if (pickerCloseHandler) {
|
|
3314
|
-
document.removeEventListener("click", pickerCloseHandler, {
|
|
3340
|
+
document.removeEventListener("click", pickerCloseHandler, {
|
|
3341
|
+
capture: true
|
|
3342
|
+
});
|
|
3315
3343
|
pickerCloseHandler = null;
|
|
3316
3344
|
}
|
|
3317
3345
|
pickerEl?.remove();
|
|
@@ -3330,13 +3358,28 @@ ${pad}</${tag}>`;
|
|
|
3330
3358
|
header.className = "el-picker-header";
|
|
3331
3359
|
const title = document.createElement("span");
|
|
3332
3360
|
title.className = "el-picker-title";
|
|
3333
|
-
title.textContent = "
|
|
3361
|
+
title.textContent = "Selection";
|
|
3334
3362
|
header.appendChild(title);
|
|
3335
3363
|
picker.appendChild(header);
|
|
3364
|
+
const exactCount = currentEquivalentNodes.length;
|
|
3365
|
+
const exactRow = document.createElement("div");
|
|
3366
|
+
exactRow.className = "el-group-exact";
|
|
3367
|
+
const chip = document.createElement("span");
|
|
3368
|
+
chip.className = "el-count-chip";
|
|
3369
|
+
chip.textContent = String(exactCount);
|
|
3370
|
+
exactRow.appendChild(chip);
|
|
3371
|
+
const exactLabel = document.createElement("span");
|
|
3372
|
+
exactLabel.textContent = `exact match${exactCount !== 1 ? "es" : ""} selected`;
|
|
3373
|
+
exactRow.appendChild(exactLabel);
|
|
3374
|
+
picker.appendChild(exactRow);
|
|
3375
|
+
const divider = document.createElement("div");
|
|
3376
|
+
divider.className = "el-group-divider";
|
|
3377
|
+
divider.textContent = "Similar";
|
|
3378
|
+
picker.appendChild(divider);
|
|
3336
3379
|
if (groups.length === 0) {
|
|
3337
3380
|
const empty = document.createElement("div");
|
|
3338
3381
|
empty.className = "el-group-empty";
|
|
3339
|
-
empty.textContent = "No similar elements found";
|
|
3382
|
+
empty.textContent = "No additional similar elements found";
|
|
3340
3383
|
picker.appendChild(empty);
|
|
3341
3384
|
} else {
|
|
3342
3385
|
let clearPreviewHighlights2 = function() {
|
|
@@ -3385,8 +3428,10 @@ ${pad}</${tag}>`;
|
|
|
3385
3428
|
const diff = document.createElement("span");
|
|
3386
3429
|
diff.className = "el-group-diff";
|
|
3387
3430
|
const parts = [];
|
|
3388
|
-
for (const a of group.added)
|
|
3389
|
-
|
|
3431
|
+
for (const a of group.added)
|
|
3432
|
+
parts.push(`<span class="diff-add">+${a}</span>`);
|
|
3433
|
+
for (const r of group.removed)
|
|
3434
|
+
parts.push(`<span class="diff-rem">-${r}</span>`);
|
|
3390
3435
|
diff.innerHTML = parts.join(" ");
|
|
3391
3436
|
row.appendChild(cb);
|
|
3392
3437
|
row.appendChild(count);
|
|
@@ -3414,7 +3459,9 @@ ${pad}</${tag}>`;
|
|
|
3414
3459
|
const removePicker = () => {
|
|
3415
3460
|
shadowRoot.querySelectorAll(".highlight-preview").forEach((el) => el.remove());
|
|
3416
3461
|
if (pickerCloseHandler) {
|
|
3417
|
-
document.removeEventListener("click", pickerCloseHandler, {
|
|
3462
|
+
document.removeEventListener("click", pickerCloseHandler, {
|
|
3463
|
+
capture: true
|
|
3464
|
+
});
|
|
3418
3465
|
pickerCloseHandler = null;
|
|
3419
3466
|
}
|
|
3420
3467
|
pickerEl?.remove();
|
|
@@ -3473,7 +3520,7 @@ ${pad}</${tag}>`;
|
|
|
3473
3520
|
const screenshotRow = document.createElement("button");
|
|
3474
3521
|
screenshotRow.className = "draw-popover-item";
|
|
3475
3522
|
screenshotRow.innerHTML = `
|
|
3476
|
-
<span class="draw-popover-icon"
|
|
3523
|
+
<span class="draw-popover-icon"><svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><rect x="4" y="4" width="8" height="8" rx="1" ry="1"/><path d="M2,2H6V0H2C.895,0,0,.895,0,2V6H2V2Z"/><path d="M14,0h-4V2h4V6h2V2c0-1.105-.895-2-2-2Z"/><path d="M14,14h-4v2h4c1.105,0,2-.895,2-2v-4h-2v4Z"/><path d="M2,10H0v4c0,1.105,.895,2,2,2H6v-2H2v-4Z"/></svg></span>
|
|
3477
3524
|
<span class="draw-popover-label">Screenshot & Annotate</span>
|
|
3478
3525
|
`;
|
|
3479
3526
|
screenshotRow.addEventListener("click", (e) => {
|
|
@@ -3527,7 +3574,10 @@ ${pad}</${tag}>`;
|
|
|
3527
3574
|
async function clickHandler(e) {
|
|
3528
3575
|
const composed = e.composedPath();
|
|
3529
3576
|
if (composed.some((el) => el === shadowHost)) return;
|
|
3530
|
-
if (composed.some(
|
|
3577
|
+
if (composed.some(
|
|
3578
|
+
(el) => el instanceof HTMLElement && el.hasAttribute("data-tw-design-canvas")
|
|
3579
|
+
))
|
|
3580
|
+
return;
|
|
3531
3581
|
e.preventDefault();
|
|
3532
3582
|
e.stopPropagation();
|
|
3533
3583
|
const target = e.target;
|
|
@@ -3539,7 +3589,6 @@ ${pad}</${tag}>`;
|
|
|
3539
3589
|
for (const node of result.exactMatch) {
|
|
3540
3590
|
highlightElement(node);
|
|
3541
3591
|
}
|
|
3542
|
-
console.log(`[overlay] ${componentName} \u2014 ${result.exactMatch.length} exact matches highlighted`);
|
|
3543
3592
|
const config = await fetchTailwindConfig();
|
|
3544
3593
|
currentEquivalentNodes = result.exactMatch;
|
|
3545
3594
|
currentTargetEl = targetEl;
|
|
@@ -3747,7 +3796,12 @@ ${pad}</${tag}>`;
|
|
|
3747
3796
|
default:
|
|
3748
3797
|
targetEl.insertAdjacentElement("beforebegin", wrapper);
|
|
3749
3798
|
}
|
|
3750
|
-
designCanvasWrappers.push({
|
|
3799
|
+
designCanvasWrappers.push({
|
|
3800
|
+
wrapper,
|
|
3801
|
+
replacedNodes: null,
|
|
3802
|
+
parent: null,
|
|
3803
|
+
anchor: null
|
|
3804
|
+
});
|
|
3751
3805
|
iframe.addEventListener("load", () => {
|
|
3752
3806
|
const contextMsg = {
|
|
3753
3807
|
type: "ELEMENT_CONTEXT",
|
|
@@ -3776,23 +3830,36 @@ ${pad}</${tag}>`;
|
|
|
3776
3830
|
return;
|
|
3777
3831
|
}
|
|
3778
3832
|
if (!areSiblings(currentEquivalentNodes)) {
|
|
3779
|
-
showToast(
|
|
3833
|
+
showToast(
|
|
3834
|
+
"Screenshot & Annotate requires all selected elements to be siblings in the DOM."
|
|
3835
|
+
);
|
|
3780
3836
|
return;
|
|
3781
3837
|
}
|
|
3782
3838
|
let screenshot;
|
|
3783
3839
|
let screenshotWidth;
|
|
3784
3840
|
let screenshotHeight;
|
|
3785
3841
|
try {
|
|
3786
|
-
({
|
|
3842
|
+
({
|
|
3843
|
+
dataUrl: screenshot,
|
|
3844
|
+
width: screenshotWidth,
|
|
3845
|
+
height: screenshotHeight
|
|
3846
|
+
} = await captureRegion(currentEquivalentNodes));
|
|
3787
3847
|
} catch (err) {
|
|
3788
3848
|
showToast("Screenshot capture failed");
|
|
3789
3849
|
console.error("[overlay] captureRegion error:", err);
|
|
3790
3850
|
return;
|
|
3791
3851
|
}
|
|
3792
3852
|
const parent = currentEquivalentNodes[0].parentElement;
|
|
3793
|
-
|
|
3853
|
+
if (!parent) {
|
|
3854
|
+
showToast("Cannot find parent element");
|
|
3855
|
+
return;
|
|
3856
|
+
}
|
|
3857
|
+
const marker = document.createComment("tw-placeholder");
|
|
3858
|
+
parent.insertBefore(marker, currentEquivalentNodes[0]);
|
|
3794
3859
|
const firstStyle = getComputedStyle(currentEquivalentNodes[0]);
|
|
3795
|
-
const lastStyle = getComputedStyle(
|
|
3860
|
+
const lastStyle = getComputedStyle(
|
|
3861
|
+
currentEquivalentNodes[currentEquivalentNodes.length - 1]
|
|
3862
|
+
);
|
|
3796
3863
|
const marginTop = firstStyle.marginTop;
|
|
3797
3864
|
const marginBottom = lastStyle.marginBottom;
|
|
3798
3865
|
const marginLeft = firstStyle.marginLeft;
|
|
@@ -3834,12 +3901,9 @@ ${pad}</${tag}>`;
|
|
|
3834
3901
|
display: block;
|
|
3835
3902
|
`;
|
|
3836
3903
|
wrapper.appendChild(iframe);
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
parent.appendChild(wrapper);
|
|
3841
|
-
}
|
|
3842
|
-
designCanvasWrappers.push({ wrapper, replacedNodes, parent, anchor });
|
|
3904
|
+
parent.insertBefore(wrapper, marker);
|
|
3905
|
+
marker.remove();
|
|
3906
|
+
designCanvasWrappers.push({ wrapper, replacedNodes, parent, anchor: wrapper.nextSibling });
|
|
3843
3907
|
iframe.addEventListener("load", () => {
|
|
3844
3908
|
const contextMsg = {
|
|
3845
3909
|
type: "ELEMENT_CONTEXT",
|
|
@@ -3912,11 +3976,18 @@ ${pad}</${tag}>`;
|
|
|
3912
3976
|
setSelectMode(false);
|
|
3913
3977
|
}
|
|
3914
3978
|
} else if (msg.type === "PATCH_PREVIEW" && currentEquivalentNodes.length > 0) {
|
|
3915
|
-
applyPreview(
|
|
3979
|
+
applyPreview(
|
|
3980
|
+
currentEquivalentNodes,
|
|
3981
|
+
msg.oldClass,
|
|
3982
|
+
msg.newClass,
|
|
3983
|
+
SERVER_ORIGIN
|
|
3984
|
+
);
|
|
3916
3985
|
} else if (msg.type === "PATCH_PREVIEW_BATCH" && currentEquivalentNodes.length > 0) {
|
|
3917
3986
|
applyPreviewBatch(currentEquivalentNodes, msg.pairs, SERVER_ORIGIN);
|
|
3918
3987
|
} else if (msg.type === "PATCH_REVERT") {
|
|
3919
3988
|
revertPreview();
|
|
3989
|
+
} else if (msg.type === "PATCH_REVERT_STAGED" && currentEquivalentNodes.length > 0) {
|
|
3990
|
+
applyPreview(currentEquivalentNodes, msg.oldClass, msg.newClass, SERVER_ORIGIN).then(() => commitPreview());
|
|
3920
3991
|
} else if (msg.type === "PATCH_STAGE" && currentTargetEl && currentBoundary) {
|
|
3921
3992
|
const state = getPreviewState();
|
|
3922
3993
|
const originalClassMap = /* @__PURE__ */ new Map();
|
|
@@ -3927,7 +3998,12 @@ ${pad}</${tag}>`;
|
|
|
3927
3998
|
}
|
|
3928
3999
|
const targetElIndex = currentEquivalentNodes.indexOf(currentTargetEl);
|
|
3929
4000
|
const originalClassString = state && targetElIndex !== -1 ? state.originalClasses[targetElIndex] : currentTargetEl.className;
|
|
3930
|
-
const context = buildContext(
|
|
4001
|
+
const context = buildContext(
|
|
4002
|
+
currentTargetEl,
|
|
4003
|
+
msg.oldClass,
|
|
4004
|
+
msg.newClass,
|
|
4005
|
+
originalClassMap
|
|
4006
|
+
);
|
|
3931
4007
|
send({
|
|
3932
4008
|
type: "PATCH_STAGED",
|
|
3933
4009
|
patch: {
|
|
@@ -4002,6 +4078,12 @@ ${pad}</${tag}>`;
|
|
|
4002
4078
|
}
|
|
4003
4079
|
}
|
|
4004
4080
|
last.wrapper.remove();
|
|
4081
|
+
if (currentTargetEl && currentEquivalentNodes.length > 0) {
|
|
4082
|
+
for (const n of currentEquivalentNodes) {
|
|
4083
|
+
highlightElement(n);
|
|
4084
|
+
}
|
|
4085
|
+
showDrawButton(currentTargetEl);
|
|
4086
|
+
}
|
|
4005
4087
|
}
|
|
4006
4088
|
}
|
|
4007
4089
|
});
|
|
@@ -4014,15 +4096,19 @@ ${pad}</${tag}>`;
|
|
|
4014
4096
|
positionWithFlip(currentTargetEl, toolbarEl);
|
|
4015
4097
|
}
|
|
4016
4098
|
});
|
|
4017
|
-
window.addEventListener(
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
currentEquivalentNodes.
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4099
|
+
window.addEventListener(
|
|
4100
|
+
"scroll",
|
|
4101
|
+
() => {
|
|
4102
|
+
if (currentEquivalentNodes.length > 0) {
|
|
4103
|
+
shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
|
|
4104
|
+
currentEquivalentNodes.forEach((n) => highlightElement(n));
|
|
4105
|
+
}
|
|
4106
|
+
if (toolbarEl && currentTargetEl) {
|
|
4107
|
+
positionWithFlip(currentTargetEl, toolbarEl);
|
|
4108
|
+
}
|
|
4109
|
+
},
|
|
4110
|
+
{ capture: true, passive: true }
|
|
4111
|
+
);
|
|
4026
4112
|
if (sessionStorage.getItem(PANEL_OPEN_KEY) === "1") {
|
|
4027
4113
|
active = true;
|
|
4028
4114
|
btn.classList.add("active");
|