@lego-build/plugins 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto.js +1 -1
- package/dist/chunk-AXYKOFRV.js +1016 -0
- package/dist/chunk-FHFX6XIT.js +1033 -0
- package/dist/chunk-GM6CYKZ4.js +1022 -0
- package/dist/chunk-GZWEKEDF.js +1035 -0
- package/dist/chunk-HSC4RGRS.js +1031 -0
- package/dist/chunk-IWXADAJU.js +1034 -0
- package/dist/chunk-NSML7MP5.js +1016 -0
- package/dist/chunk-PJLB7JZJ.js +1091 -0
- package/dist/chunk-Q3UVT433.js +1026 -0
- package/dist/chunk-QQHM3DQ7.js +1029 -0
- package/dist/chunk-RP7HEYXU.js +1034 -0
- package/dist/chunk-Z2IAQK4S.js +1038 -0
- package/dist/index.js +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var SKIP_ELEMENTS = /* @__PURE__ */ new Set([
|
|
3
|
+
"html",
|
|
4
|
+
"head",
|
|
5
|
+
"script",
|
|
6
|
+
"style",
|
|
7
|
+
"meta",
|
|
8
|
+
"link",
|
|
9
|
+
"title",
|
|
10
|
+
"noscript",
|
|
11
|
+
"br",
|
|
12
|
+
"wbr",
|
|
13
|
+
"template",
|
|
14
|
+
"slot"
|
|
15
|
+
]);
|
|
16
|
+
var INTERACTIVE_ELEMENTS = /* @__PURE__ */ new Set([
|
|
17
|
+
"a",
|
|
18
|
+
"button",
|
|
19
|
+
"input",
|
|
20
|
+
"select",
|
|
21
|
+
"textarea",
|
|
22
|
+
"label",
|
|
23
|
+
"details",
|
|
24
|
+
"summary",
|
|
25
|
+
"dialog",
|
|
26
|
+
"menu",
|
|
27
|
+
"menuitem"
|
|
28
|
+
]);
|
|
29
|
+
var SEMANTIC_ELEMENTS = /* @__PURE__ */ new Set([
|
|
30
|
+
"header",
|
|
31
|
+
"footer",
|
|
32
|
+
"main",
|
|
33
|
+
"nav",
|
|
34
|
+
"aside",
|
|
35
|
+
"section",
|
|
36
|
+
"article",
|
|
37
|
+
"h1",
|
|
38
|
+
"h2",
|
|
39
|
+
"h3",
|
|
40
|
+
"h4",
|
|
41
|
+
"h5",
|
|
42
|
+
"h6",
|
|
43
|
+
"p",
|
|
44
|
+
"figure",
|
|
45
|
+
"figcaption",
|
|
46
|
+
"ul",
|
|
47
|
+
"ol",
|
|
48
|
+
"li",
|
|
49
|
+
"dl",
|
|
50
|
+
"dt",
|
|
51
|
+
"dd",
|
|
52
|
+
"table",
|
|
53
|
+
"thead",
|
|
54
|
+
"tbody",
|
|
55
|
+
"tr",
|
|
56
|
+
"td",
|
|
57
|
+
"th",
|
|
58
|
+
"form",
|
|
59
|
+
"fieldset",
|
|
60
|
+
"legend",
|
|
61
|
+
"img",
|
|
62
|
+
"video",
|
|
63
|
+
"audio",
|
|
64
|
+
"canvas",
|
|
65
|
+
"svg"
|
|
66
|
+
]);
|
|
67
|
+
var MIN_ELEMENT_SIZE = 8;
|
|
68
|
+
var MOUSEMOVE_DEBOUNCE = 16;
|
|
69
|
+
var CSS_PREVIEW_PROPERTIES = [
|
|
70
|
+
"display",
|
|
71
|
+
"position",
|
|
72
|
+
"width",
|
|
73
|
+
"height",
|
|
74
|
+
"padding",
|
|
75
|
+
"margin",
|
|
76
|
+
"color",
|
|
77
|
+
"background-color",
|
|
78
|
+
"font-size",
|
|
79
|
+
"font-weight"
|
|
80
|
+
];
|
|
81
|
+
function sendToParent(type, payload) {
|
|
82
|
+
window.parent.postMessage({ type, payload }, "*");
|
|
83
|
+
}
|
|
84
|
+
function setupIframeBridge() {
|
|
85
|
+
const pushState = window.history.pushState.bind(window.history);
|
|
86
|
+
const originalPushState = (...args) => {
|
|
87
|
+
pushState(...args);
|
|
88
|
+
sendToParent("IFRAME_URL_CHANGED", window.location.href);
|
|
89
|
+
};
|
|
90
|
+
window.history.pushState = originalPushState;
|
|
91
|
+
const replaceState = window.history.replaceState.bind(window.history);
|
|
92
|
+
const originalReplaceState = (...args) => {
|
|
93
|
+
replaceState(...args);
|
|
94
|
+
sendToParent("IFRAME_URL_CHANGED", window.location.href);
|
|
95
|
+
};
|
|
96
|
+
window.history.replaceState = originalReplaceState;
|
|
97
|
+
window.addEventListener("popstate", () => {
|
|
98
|
+
sendToParent("IFRAME_URL_CHANGED", window.location.href);
|
|
99
|
+
});
|
|
100
|
+
window.addEventListener("message", (e) => {
|
|
101
|
+
const msg = e.data;
|
|
102
|
+
if (!msg?.type) return;
|
|
103
|
+
switch (msg.type) {
|
|
104
|
+
case "NAVIGATE_BACK":
|
|
105
|
+
window.history.back();
|
|
106
|
+
break;
|
|
107
|
+
case "NAVIGATE_FORWARD":
|
|
108
|
+
window.history.forward();
|
|
109
|
+
break;
|
|
110
|
+
case "NAVIGATE_URL":
|
|
111
|
+
if (msg.payload) {
|
|
112
|
+
window.history.pushState({}, "", msg.payload);
|
|
113
|
+
sendToParent("IFRAME_URL_CHANGED", window.location.href);
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case "REFRESH":
|
|
117
|
+
window.location.reload();
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function createElementSelectorState() {
|
|
123
|
+
return {
|
|
124
|
+
isActive: false,
|
|
125
|
+
isEditing: false,
|
|
126
|
+
isSelected: false,
|
|
127
|
+
isCssExpanded: false,
|
|
128
|
+
hoveredElement: null,
|
|
129
|
+
selectedElement: null,
|
|
130
|
+
overlay: null,
|
|
131
|
+
selectionOverlay: null,
|
|
132
|
+
tooltip: null,
|
|
133
|
+
editToolbar: null,
|
|
134
|
+
inlineEditor: null,
|
|
135
|
+
depth: 0,
|
|
136
|
+
originalText: "",
|
|
137
|
+
originalHTML: "",
|
|
138
|
+
lastMouseMoveTime: 0,
|
|
139
|
+
pendingMouseMove: null,
|
|
140
|
+
rawTarget: null
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function shouldSkipElement(element) {
|
|
144
|
+
const tagName = element.tagName.toLowerCase();
|
|
145
|
+
if (SKIP_ELEMENTS.has(tagName)) return true;
|
|
146
|
+
if (element.id?.startsWith("lego-element-selector")) return true;
|
|
147
|
+
const style = window.getComputedStyle(element);
|
|
148
|
+
if (style.display === "none" || style.visibility === "hidden") return true;
|
|
149
|
+
const rect = element.getBoundingClientRect();
|
|
150
|
+
if (rect.width < MIN_ELEMENT_SIZE && rect.height < MIN_ELEMENT_SIZE) return true;
|
|
151
|
+
if (rect.width === 0 && rect.height === 0 && !element.children.length) return true;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
function getElementPriority(element) {
|
|
155
|
+
const tagName = element.tagName.toLowerCase();
|
|
156
|
+
let score = 0;
|
|
157
|
+
if (INTERACTIVE_ELEMENTS.has(tagName)) score += 100;
|
|
158
|
+
if (SEMANTIC_ELEMENTS.has(tagName)) score += 50;
|
|
159
|
+
if (element.hasAttribute("data-locator-path")) score += 200;
|
|
160
|
+
if (element.id) score += 20;
|
|
161
|
+
if (element.className && typeof element.className === "string") {
|
|
162
|
+
const classes = element.className.split(" ").filter((c) => c && !c.startsWith("__"));
|
|
163
|
+
score += Math.min(classes.length * 5, 25);
|
|
164
|
+
}
|
|
165
|
+
const textContent = element.textContent?.trim() || "";
|
|
166
|
+
if (textContent.length > 0 && textContent.length < 200) score += 15;
|
|
167
|
+
if ((tagName === "div" || tagName === "span") && !element.id && !element.className) {
|
|
168
|
+
score -= 30;
|
|
169
|
+
}
|
|
170
|
+
if (element.hasAttribute("role")) score += 25;
|
|
171
|
+
const attrs = element.attributes;
|
|
172
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
173
|
+
if (attrs[i].name.startsWith("aria-")) {
|
|
174
|
+
score += 10;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return score;
|
|
179
|
+
}
|
|
180
|
+
function findBestElement(target) {
|
|
181
|
+
if (shouldSkipElement(target)) {
|
|
182
|
+
const parent = target.parentElement;
|
|
183
|
+
if (parent && parent !== document.body && parent !== document.documentElement) {
|
|
184
|
+
return findBestElement(parent);
|
|
185
|
+
}
|
|
186
|
+
return target;
|
|
187
|
+
}
|
|
188
|
+
const targetPriority = getElementPriority(target);
|
|
189
|
+
if (targetPriority < 20) {
|
|
190
|
+
const parent = target.parentElement;
|
|
191
|
+
if (parent && parent !== document.body && parent !== document.documentElement) {
|
|
192
|
+
const parentPriority = getElementPriority(parent);
|
|
193
|
+
if (parentPriority > targetPriority + 30) {
|
|
194
|
+
return findBestElement(parent);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
let current = target;
|
|
199
|
+
while (current && current !== document.body) {
|
|
200
|
+
const tagName = current.tagName.toLowerCase();
|
|
201
|
+
if (INTERACTIVE_ELEMENTS.has(tagName)) {
|
|
202
|
+
return current;
|
|
203
|
+
}
|
|
204
|
+
if (current !== target && SEMANTIC_ELEMENTS.has(tagName)) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
current = current.parentElement;
|
|
208
|
+
}
|
|
209
|
+
return target;
|
|
210
|
+
}
|
|
211
|
+
function getElementsAtPoint(x, y) {
|
|
212
|
+
const elements = [];
|
|
213
|
+
const allElements = document.elementsFromPoint(x, y);
|
|
214
|
+
for (const el of allElements) {
|
|
215
|
+
if (el === document.documentElement || el === document.body) continue;
|
|
216
|
+
if (shouldSkipElement(el)) continue;
|
|
217
|
+
elements.push(el);
|
|
218
|
+
}
|
|
219
|
+
return elements;
|
|
220
|
+
}
|
|
221
|
+
function getElementAtPoint(x, y) {
|
|
222
|
+
const elements = getElementsAtPoint(x, y);
|
|
223
|
+
if (elements.length === 0) return null;
|
|
224
|
+
return findBestElement(elements[0]);
|
|
225
|
+
}
|
|
226
|
+
function getBreadcrumb(element, maxDepth = 4) {
|
|
227
|
+
const parts = [];
|
|
228
|
+
let current = element;
|
|
229
|
+
let depth = 0;
|
|
230
|
+
while (current && current !== document.body && depth < maxDepth) {
|
|
231
|
+
const tagName = current.tagName.toLowerCase();
|
|
232
|
+
let part = tagName;
|
|
233
|
+
if (current.id) {
|
|
234
|
+
part += `#${current.id}`;
|
|
235
|
+
} else if (current.className && typeof current.className === "string") {
|
|
236
|
+
const classes = current.className.split(" ").filter((c) => c && !c.startsWith("__") && c.length < 20);
|
|
237
|
+
if (classes.length > 0) {
|
|
238
|
+
part += `.${classes[0]}`;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
parts.unshift(part);
|
|
242
|
+
current = current.parentElement;
|
|
243
|
+
depth++;
|
|
244
|
+
}
|
|
245
|
+
if (current && current !== document.body) {
|
|
246
|
+
parts.unshift("...");
|
|
247
|
+
}
|
|
248
|
+
return parts.join(" \u203A ");
|
|
249
|
+
}
|
|
250
|
+
function getCSSPreview(element) {
|
|
251
|
+
const style = window.getComputedStyle(element);
|
|
252
|
+
const result = {};
|
|
253
|
+
for (const prop of CSS_PREVIEW_PROPERTIES) {
|
|
254
|
+
const value = style.getPropertyValue(prop);
|
|
255
|
+
if (value && value !== "none" && value !== "auto" && value !== "normal") {
|
|
256
|
+
if (prop.includes("color") && value.startsWith("rgb")) {
|
|
257
|
+
result[prop] = rgbToHex(value);
|
|
258
|
+
} else {
|
|
259
|
+
result[prop] = value;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
function rgbToHex(rgb) {
|
|
266
|
+
const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
267
|
+
if (!match) return rgb;
|
|
268
|
+
const r = parseInt(match[1]).toString(16).padStart(2, "0");
|
|
269
|
+
const g = parseInt(match[2]).toString(16).padStart(2, "0");
|
|
270
|
+
const b = parseInt(match[3]).toString(16).padStart(2, "0");
|
|
271
|
+
return `#${r}${g}${b}`;
|
|
272
|
+
}
|
|
273
|
+
function createOverlay() {
|
|
274
|
+
const overlay = document.createElement("div");
|
|
275
|
+
overlay.id = "lego-element-selector-overlay";
|
|
276
|
+
overlay.style.cssText = `
|
|
277
|
+
position: fixed;
|
|
278
|
+
pointer-events: none;
|
|
279
|
+
z-index: 2147483645;
|
|
280
|
+
background: rgba(59, 130, 246, 0.08);
|
|
281
|
+
border: 2px solid #3b82f6;
|
|
282
|
+
border-radius: 6px;
|
|
283
|
+
transition: all 0.08s ease-out;
|
|
284
|
+
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.3), inset 0 0 20px rgba(59, 130, 246, 0.05);
|
|
285
|
+
`;
|
|
286
|
+
document.body.appendChild(overlay);
|
|
287
|
+
return overlay;
|
|
288
|
+
}
|
|
289
|
+
function createSelectionOverlay() {
|
|
290
|
+
const overlay = document.createElement("div");
|
|
291
|
+
overlay.id = "lego-element-selector-selection";
|
|
292
|
+
overlay.style.cssText = `
|
|
293
|
+
position: fixed;
|
|
294
|
+
pointer-events: none;
|
|
295
|
+
z-index: 2147483646;
|
|
296
|
+
background: rgba(59, 130, 246, 0.1);
|
|
297
|
+
border: 2px solid #3b82f6;
|
|
298
|
+
border-radius: 6px;
|
|
299
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2), 0 0 20px rgba(59, 130, 246, 0.15);
|
|
300
|
+
transition: all 0.12s ease-out;
|
|
301
|
+
animation: lego-pulse 2s ease-in-out infinite;
|
|
302
|
+
`;
|
|
303
|
+
if (!document.getElementById("lego-selector-styles")) {
|
|
304
|
+
const styleSheet = document.createElement("style");
|
|
305
|
+
styleSheet.id = "lego-selector-styles";
|
|
306
|
+
styleSheet.textContent = `
|
|
307
|
+
@keyframes lego-pulse {
|
|
308
|
+
0%, 100% { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2), 0 0 20px rgba(59, 130, 246, 0.15); }
|
|
309
|
+
50% { box-shadow: 0 0 0 5px rgba(59, 130, 246, 0.15), 0 0 30px rgba(59, 130, 246, 0.25); }
|
|
310
|
+
}
|
|
311
|
+
`;
|
|
312
|
+
document.head.appendChild(styleSheet);
|
|
313
|
+
}
|
|
314
|
+
document.body.appendChild(overlay);
|
|
315
|
+
return overlay;
|
|
316
|
+
}
|
|
317
|
+
function createTooltip() {
|
|
318
|
+
const tooltip = document.createElement("div");
|
|
319
|
+
tooltip.id = "lego-element-selector-tooltip";
|
|
320
|
+
tooltip.style.cssText = `
|
|
321
|
+
position: fixed;
|
|
322
|
+
pointer-events: auto;
|
|
323
|
+
z-index: 2147483647;
|
|
324
|
+
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
|
325
|
+
color: white;
|
|
326
|
+
padding: 8px 12px;
|
|
327
|
+
border-radius: 8px;
|
|
328
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
329
|
+
font-size: 12px;
|
|
330
|
+
max-width: 400px;
|
|
331
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
332
|
+
transition: all 0.1s ease-out;
|
|
333
|
+
backdrop-filter: blur(8px);
|
|
334
|
+
`;
|
|
335
|
+
document.body.appendChild(tooltip);
|
|
336
|
+
return tooltip;
|
|
337
|
+
}
|
|
338
|
+
function createEditToolbar(onSave, onCancel) {
|
|
339
|
+
const toolbar = document.createElement("div");
|
|
340
|
+
toolbar.id = "lego-element-selector-edit-toolbar";
|
|
341
|
+
toolbar.style.cssText = `
|
|
342
|
+
position: fixed;
|
|
343
|
+
pointer-events: auto;
|
|
344
|
+
z-index: 2147483647;
|
|
345
|
+
display: flex;
|
|
346
|
+
gap: 6px;
|
|
347
|
+
padding: 6px 8px;
|
|
348
|
+
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
|
349
|
+
border-radius: 8px;
|
|
350
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
351
|
+
backdrop-filter: blur(8px);
|
|
352
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
353
|
+
font-size: 12px;
|
|
354
|
+
`;
|
|
355
|
+
const cancelBtn = document.createElement("button");
|
|
356
|
+
cancelBtn.innerHTML = `<span style="margin-right:4px;">\u2715</span> Cancel <kbd style="margin-left:6px;background:#334155;padding:1px 4px;border-radius:3px;font-size:10px;">Esc</kbd>`;
|
|
357
|
+
cancelBtn.style.cssText = `
|
|
358
|
+
display: flex;
|
|
359
|
+
align-items: center;
|
|
360
|
+
padding: 6px 12px;
|
|
361
|
+
background: #374151;
|
|
362
|
+
color: #f3f4f6;
|
|
363
|
+
border: none;
|
|
364
|
+
border-radius: 6px;
|
|
365
|
+
cursor: pointer;
|
|
366
|
+
font-size: 12px;
|
|
367
|
+
font-weight: 500;
|
|
368
|
+
transition: all 0.15s ease;
|
|
369
|
+
`;
|
|
370
|
+
cancelBtn.onmouseenter = () => {
|
|
371
|
+
cancelBtn.style.background = "#4b5563";
|
|
372
|
+
};
|
|
373
|
+
cancelBtn.onmouseleave = () => {
|
|
374
|
+
cancelBtn.style.background = "#374151";
|
|
375
|
+
};
|
|
376
|
+
cancelBtn.onclick = (e) => {
|
|
377
|
+
e.preventDefault();
|
|
378
|
+
e.stopPropagation();
|
|
379
|
+
onCancel();
|
|
380
|
+
};
|
|
381
|
+
const saveBtn = document.createElement("button");
|
|
382
|
+
saveBtn.innerHTML = `<span style="margin-right:4px;">\u2713</span> Save <kbd style="margin-left:6px;background:rgba(255,255,255,0.2);padding:1px 4px;border-radius:3px;font-size:10px;">\u2318\u21B5</kbd>`;
|
|
383
|
+
saveBtn.style.cssText = `
|
|
384
|
+
display: flex;
|
|
385
|
+
align-items: center;
|
|
386
|
+
padding: 6px 12px;
|
|
387
|
+
background: #3b82f6;
|
|
388
|
+
color: white;
|
|
389
|
+
border: none;
|
|
390
|
+
border-radius: 6px;
|
|
391
|
+
cursor: pointer;
|
|
392
|
+
font-size: 12px;
|
|
393
|
+
font-weight: 500;
|
|
394
|
+
transition: all 0.15s ease;
|
|
395
|
+
`;
|
|
396
|
+
saveBtn.onmouseenter = () => {
|
|
397
|
+
saveBtn.style.background = "#2563eb";
|
|
398
|
+
};
|
|
399
|
+
saveBtn.onmouseleave = () => {
|
|
400
|
+
saveBtn.style.background = "#3b82f6";
|
|
401
|
+
};
|
|
402
|
+
saveBtn.onclick = (e) => {
|
|
403
|
+
e.preventDefault();
|
|
404
|
+
e.stopPropagation();
|
|
405
|
+
onSave();
|
|
406
|
+
};
|
|
407
|
+
toolbar.appendChild(cancelBtn);
|
|
408
|
+
toolbar.appendChild(saveBtn);
|
|
409
|
+
document.body.appendChild(toolbar);
|
|
410
|
+
return toolbar;
|
|
411
|
+
}
|
|
412
|
+
function updateEditToolbarPosition(state) {
|
|
413
|
+
if (!state.selectedElement || !state.editToolbar) return;
|
|
414
|
+
const rect = state.selectedElement.getBoundingClientRect();
|
|
415
|
+
const toolbarWidth = state.editToolbar.offsetWidth || 200;
|
|
416
|
+
const toolbarHeight = state.editToolbar.offsetHeight || 40;
|
|
417
|
+
let toolbarTop = rect.top - toolbarHeight - 8;
|
|
418
|
+
let toolbarLeft = rect.right - toolbarWidth;
|
|
419
|
+
if (toolbarTop < 8) {
|
|
420
|
+
toolbarTop = rect.bottom + 8;
|
|
421
|
+
}
|
|
422
|
+
if (toolbarLeft < 8) {
|
|
423
|
+
toolbarLeft = 8;
|
|
424
|
+
}
|
|
425
|
+
if (toolbarLeft + toolbarWidth > window.innerWidth - 8) {
|
|
426
|
+
toolbarLeft = window.innerWidth - toolbarWidth - 8;
|
|
427
|
+
}
|
|
428
|
+
state.editToolbar.style.top = `${toolbarTop}px`;
|
|
429
|
+
state.editToolbar.style.left = `${toolbarLeft}px`;
|
|
430
|
+
}
|
|
431
|
+
function getCSSPropertiesHTML(element) {
|
|
432
|
+
const cssInfo = getCSSPreview(element);
|
|
433
|
+
const entries = Object.entries(cssInfo);
|
|
434
|
+
if (entries.length === 0) return "";
|
|
435
|
+
return entries.map(([prop, value]) => {
|
|
436
|
+
const colorDot = prop.includes("color") && value.startsWith("#") ? `<span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:${value};margin-right:6px;vertical-align:middle;border:1px solid rgba(255,255,255,0.2);"></span>` : "";
|
|
437
|
+
return `<div style="display:flex;justify-content:space-between;align-items:center;padding:2px 0;">
|
|
438
|
+
<span style="color:#94a3b8;">${prop}:</span>
|
|
439
|
+
<span style="color:#e2e8f0;margin-left:12px;">${colorDot}${value}</span>
|
|
440
|
+
</div>`;
|
|
441
|
+
}).join("");
|
|
442
|
+
}
|
|
443
|
+
function enableInlineEditing(state, element, onSave, onCancel) {
|
|
444
|
+
const originalOutline = element.style.outline;
|
|
445
|
+
const originalBoxShadow = element.style.boxShadow;
|
|
446
|
+
const originalBackground = element.style.background;
|
|
447
|
+
const originalColor = element.style.color;
|
|
448
|
+
const originalWebkitBackgroundClip = element.style.webkitBackgroundClip;
|
|
449
|
+
const originalBackgroundClip = element.style.backgroundClip;
|
|
450
|
+
const originalWebkitTextFillColor = element.style.webkitTextFillColor;
|
|
451
|
+
const computedStyle = window.getComputedStyle(element);
|
|
452
|
+
const usesGradientText = computedStyle.webkitBackgroundClip === "text" || computedStyle.backgroundClip === "text";
|
|
453
|
+
element.contentEditable = "true";
|
|
454
|
+
element.style.outline = "2px solid #3b82f6";
|
|
455
|
+
element.style.boxShadow = "0 0 0 4px rgba(59, 130, 246, 0.2)";
|
|
456
|
+
element.style.borderRadius = "4px";
|
|
457
|
+
if (usesGradientText) {
|
|
458
|
+
element.style.webkitBackgroundClip = "unset";
|
|
459
|
+
element.style.backgroundClip = "unset";
|
|
460
|
+
element.style.color = "#ffffff";
|
|
461
|
+
element.style.webkitTextFillColor = "#ffffff";
|
|
462
|
+
element.style.background = "rgba(59, 130, 246, 0.1)";
|
|
463
|
+
} else {
|
|
464
|
+
element.style.background = "rgba(59, 130, 246, 0.05)";
|
|
465
|
+
}
|
|
466
|
+
const marker = document.createElement("div");
|
|
467
|
+
marker.id = "lego-inline-editor";
|
|
468
|
+
marker.style.display = "none";
|
|
469
|
+
marker.dataset.originalOutline = originalOutline;
|
|
470
|
+
marker.dataset.originalBoxShadow = originalBoxShadow;
|
|
471
|
+
marker.dataset.originalBackground = originalBackground;
|
|
472
|
+
marker.dataset.originalColor = originalColor;
|
|
473
|
+
marker.dataset.originalWebkitBackgroundClip = originalWebkitBackgroundClip;
|
|
474
|
+
marker.dataset.originalBackgroundClip = originalBackgroundClip;
|
|
475
|
+
marker.dataset.originalWebkitTextFillColor = originalWebkitTextFillColor;
|
|
476
|
+
marker.dataset.usesGradientText = usesGradientText ? "true" : "false";
|
|
477
|
+
document.body.appendChild(marker);
|
|
478
|
+
element.focus();
|
|
479
|
+
const range = document.createRange();
|
|
480
|
+
range.selectNodeContents(element);
|
|
481
|
+
range.collapse(false);
|
|
482
|
+
const selection = window.getSelection();
|
|
483
|
+
selection?.removeAllRanges();
|
|
484
|
+
selection?.addRange(range);
|
|
485
|
+
const handleKeyDown = (e) => {
|
|
486
|
+
if (e.key === "Escape") {
|
|
487
|
+
e.preventDefault();
|
|
488
|
+
e.stopPropagation();
|
|
489
|
+
onCancel();
|
|
490
|
+
} else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
|
491
|
+
e.preventDefault();
|
|
492
|
+
e.stopPropagation();
|
|
493
|
+
onSave();
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
const handleBlur = () => {
|
|
497
|
+
setTimeout(() => {
|
|
498
|
+
if (document.activeElement !== element && state.isEditing) {
|
|
499
|
+
onSave();
|
|
500
|
+
}
|
|
501
|
+
}, 100);
|
|
502
|
+
};
|
|
503
|
+
element.addEventListener("keydown", handleKeyDown);
|
|
504
|
+
element.addEventListener("blur", handleBlur);
|
|
505
|
+
const restoreStyle = (prop, originalValue) => {
|
|
506
|
+
if (originalValue) {
|
|
507
|
+
element.style.setProperty(prop, originalValue);
|
|
508
|
+
} else {
|
|
509
|
+
element.style.removeProperty(prop);
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
marker.__cleanup = () => {
|
|
513
|
+
element.removeEventListener("keydown", handleKeyDown);
|
|
514
|
+
element.removeEventListener("blur", handleBlur);
|
|
515
|
+
element.contentEditable = "false";
|
|
516
|
+
restoreStyle("outline", originalOutline);
|
|
517
|
+
restoreStyle("box-shadow", originalBoxShadow);
|
|
518
|
+
restoreStyle("background", originalBackground);
|
|
519
|
+
element.style.removeProperty("border-radius");
|
|
520
|
+
if (usesGradientText) {
|
|
521
|
+
restoreStyle("color", originalColor);
|
|
522
|
+
restoreStyle("-webkit-background-clip", originalWebkitBackgroundClip);
|
|
523
|
+
restoreStyle("background-clip", originalBackgroundClip);
|
|
524
|
+
restoreStyle("-webkit-text-fill-color", originalWebkitTextFillColor);
|
|
525
|
+
}
|
|
526
|
+
window.getSelection()?.removeAllRanges();
|
|
527
|
+
};
|
|
528
|
+
return marker;
|
|
529
|
+
}
|
|
530
|
+
function getElementTextContent(element) {
|
|
531
|
+
return element.innerText?.trim() || element.textContent?.trim() || "";
|
|
532
|
+
}
|
|
533
|
+
function getElementDepth(element) {
|
|
534
|
+
let depth = 0;
|
|
535
|
+
let current = element;
|
|
536
|
+
while (current && current !== document.body) {
|
|
537
|
+
depth++;
|
|
538
|
+
current = current.parentElement;
|
|
539
|
+
}
|
|
540
|
+
return depth;
|
|
541
|
+
}
|
|
542
|
+
function updateOverlayPosition(state, element, overlay) {
|
|
543
|
+
const targetElement = element || state.hoveredElement;
|
|
544
|
+
const targetOverlay = overlay || state.overlay;
|
|
545
|
+
if (!targetElement || !targetOverlay) return;
|
|
546
|
+
const rect = targetElement.getBoundingClientRect();
|
|
547
|
+
targetOverlay.style.top = `${rect.top}px`;
|
|
548
|
+
targetOverlay.style.left = `${rect.left}px`;
|
|
549
|
+
targetOverlay.style.width = `${rect.width}px`;
|
|
550
|
+
targetOverlay.style.height = `${rect.height}px`;
|
|
551
|
+
}
|
|
552
|
+
function updateTooltipPosition(state) {
|
|
553
|
+
if (!state.hoveredElement || !state.tooltip) return;
|
|
554
|
+
const rect = state.hoveredElement.getBoundingClientRect();
|
|
555
|
+
const viewportWidth = window.innerWidth;
|
|
556
|
+
const viewportHeight = window.innerHeight;
|
|
557
|
+
const tooltipHeight = state.tooltip.offsetHeight || 80;
|
|
558
|
+
const tooltipWidth = state.tooltip.offsetWidth || 300;
|
|
559
|
+
let tooltipTop = rect.bottom + 8;
|
|
560
|
+
let tooltipLeft = rect.left;
|
|
561
|
+
if (tooltipTop + tooltipHeight > viewportHeight - 8) {
|
|
562
|
+
tooltipTop = rect.top - tooltipHeight - 8;
|
|
563
|
+
}
|
|
564
|
+
if (tooltipTop < 8) {
|
|
565
|
+
tooltipTop = Math.max(8, rect.top);
|
|
566
|
+
tooltipLeft = rect.right + 8;
|
|
567
|
+
if (tooltipLeft + tooltipWidth > viewportWidth - 8) {
|
|
568
|
+
tooltipLeft = rect.left - tooltipWidth - 8;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (tooltipLeft + tooltipWidth > viewportWidth - 8) {
|
|
572
|
+
tooltipLeft = viewportWidth - tooltipWidth - 8;
|
|
573
|
+
}
|
|
574
|
+
if (tooltipLeft < 8) {
|
|
575
|
+
tooltipLeft = 8;
|
|
576
|
+
}
|
|
577
|
+
state.tooltip.style.top = `${tooltipTop}px`;
|
|
578
|
+
state.tooltip.style.left = `${tooltipLeft}px`;
|
|
579
|
+
}
|
|
580
|
+
function updateSelectionOverlayPosition(state) {
|
|
581
|
+
if (!state.selectedElement || !state.selectionOverlay) return;
|
|
582
|
+
updateOverlayPosition(state, state.selectedElement, state.selectionOverlay);
|
|
583
|
+
}
|
|
584
|
+
function getElementInfo(element) {
|
|
585
|
+
let filePath = element.getAttribute("data-locator-path") || "";
|
|
586
|
+
let line = element.getAttribute("data-locator-line");
|
|
587
|
+
if (!filePath) {
|
|
588
|
+
let current = element.parentElement;
|
|
589
|
+
while (current && current !== document.body && current !== document.documentElement) {
|
|
590
|
+
const ancestorPath = current.getAttribute("data-locator-path");
|
|
591
|
+
if (ancestorPath) {
|
|
592
|
+
filePath = ancestorPath;
|
|
593
|
+
line = current.getAttribute("data-locator-line");
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
current = current.parentElement;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const depth = getElementDepth(element);
|
|
600
|
+
const id = element.id ? `#${element.id}` : "";
|
|
601
|
+
const classes = element.className && typeof element.className === "string" ? element.className.split(" ").filter((c) => c).map((c) => `.${c}`).join("") : "";
|
|
602
|
+
return {
|
|
603
|
+
tagName: element.tagName.toLowerCase(),
|
|
604
|
+
selector: element.tagName.toLowerCase() + id + classes.slice(0, 50),
|
|
605
|
+
filePath,
|
|
606
|
+
line: line ? parseInt(line) : void 0,
|
|
607
|
+
depth,
|
|
608
|
+
rect: element.getBoundingClientRect()
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
function highlightElement(state, element) {
|
|
612
|
+
state.hoveredElement = element;
|
|
613
|
+
if (!state.overlay) {
|
|
614
|
+
state.overlay = createOverlay();
|
|
615
|
+
}
|
|
616
|
+
if (!state.tooltip) {
|
|
617
|
+
state.tooltip = createTooltip();
|
|
618
|
+
}
|
|
619
|
+
state.overlay.style.display = "block";
|
|
620
|
+
state.tooltip.style.display = "block";
|
|
621
|
+
const info = getElementInfo(element);
|
|
622
|
+
const breadcrumb = getBreadcrumb(element);
|
|
623
|
+
state.depth = info.depth;
|
|
624
|
+
const isSelectedElement = state.isSelected && state.selectedElement === element;
|
|
625
|
+
const cssPropertiesHTML = getCSSPropertiesHTML(element);
|
|
626
|
+
const hasCssProperties = cssPropertiesHTML.length > 0;
|
|
627
|
+
state.tooltip.innerHTML = `
|
|
628
|
+
<div style="display:flex;flex-direction:column;gap:6px;">
|
|
629
|
+
<div style="color:#64748b;font-size:10px;font-family:ui-monospace,monospace;">
|
|
630
|
+
${breadcrumb}
|
|
631
|
+
</div>
|
|
632
|
+
<div style="color:#94a3b8;font-size:10px;border-top:1px solid rgba(255,255,255,0.1);padding-top:6px;margin-top:2px;">
|
|
633
|
+
${isSelectedElement ? `<span style="color:#22c55e;">\u2713 Selected</span> \xB7 Click to edit \xB7 <kbd style="background:#334155;padding:1px 4px;border-radius:3px;">E</kbd> quick edit` : state.isSelected ? `Click to select` : `Click select \xB7 Double-click edit \xB7 <kbd style="background:#334155;padding:1px 4px;border-radius:3px;">\u2191\u2193\u2190\u2192</kbd> navigate`}
|
|
634
|
+
</div>
|
|
635
|
+
${hasCssProperties ? `
|
|
636
|
+
<div id="lego-css-toggle" style="display:flex;align-items:center;gap:4px;cursor:pointer;color:#60a5fa;font-size:10px;border-top:1px solid rgba(255,255,255,0.1);padding-top:6px;margin-top:2px;user-select:none;">
|
|
637
|
+
<span style="transition:transform 0.15s;transform:rotate(${state.isCssExpanded ? "90deg" : "0deg"});">\u25B6</span>
|
|
638
|
+
<span>CSS Properties</span>
|
|
639
|
+
</div>
|
|
640
|
+
<div id="lego-css-content" style="display:${state.isCssExpanded ? "block" : "none"};font-family:ui-monospace,monospace;font-size:11px;padding-left:4px;">
|
|
641
|
+
${cssPropertiesHTML}
|
|
642
|
+
</div>
|
|
643
|
+
` : ""}
|
|
644
|
+
</div>
|
|
645
|
+
`;
|
|
646
|
+
updateOverlayPosition(state);
|
|
647
|
+
updateTooltipPosition(state);
|
|
648
|
+
sendToParent("HOVER_ELEMENT", {
|
|
649
|
+
tagName: info.tagName,
|
|
650
|
+
rect: info.rect,
|
|
651
|
+
filePath: info.filePath || void 0,
|
|
652
|
+
line: info.line,
|
|
653
|
+
depth: info.depth,
|
|
654
|
+
breadcrumb
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
function navigateDOM(state, direction) {
|
|
658
|
+
if (!state.hoveredElement) return;
|
|
659
|
+
let target = null;
|
|
660
|
+
switch (direction) {
|
|
661
|
+
case "parent":
|
|
662
|
+
target = state.hoveredElement.parentElement;
|
|
663
|
+
if (target && target === document.body) target = null;
|
|
664
|
+
break;
|
|
665
|
+
case "child":
|
|
666
|
+
target = state.hoveredElement.firstElementChild;
|
|
667
|
+
break;
|
|
668
|
+
case "next":
|
|
669
|
+
target = state.hoveredElement.nextElementSibling;
|
|
670
|
+
break;
|
|
671
|
+
case "prev":
|
|
672
|
+
target = state.hoveredElement.previousElementSibling;
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
if (target && target !== document.documentElement && target !== document.body) {
|
|
676
|
+
highlightElement(state, target);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function setupElementSelector() {
|
|
680
|
+
const state = createElementSelectorState();
|
|
681
|
+
function saveInlineEdit() {
|
|
682
|
+
if (!state.inlineEditor || !state.selectedElement) return;
|
|
683
|
+
const newText = getElementTextContent(state.selectedElement);
|
|
684
|
+
const info = getElementInfo(state.selectedElement);
|
|
685
|
+
if (newText !== state.originalText && info.filePath) {
|
|
686
|
+
sendToParent("SAVE_INLINE_EDIT", {
|
|
687
|
+
filePath: info.filePath,
|
|
688
|
+
originalContent: state.originalText,
|
|
689
|
+
newContent: newText,
|
|
690
|
+
line: info.line
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
exitEditMode();
|
|
694
|
+
}
|
|
695
|
+
function cancelInlineEdit() {
|
|
696
|
+
if (state.selectedElement && state.originalHTML !== void 0) {
|
|
697
|
+
state.selectedElement.innerHTML = state.originalHTML;
|
|
698
|
+
}
|
|
699
|
+
sendToParent("CANCEL_INLINE_EDIT");
|
|
700
|
+
exitEditMode();
|
|
701
|
+
}
|
|
702
|
+
function exitEditMode() {
|
|
703
|
+
state.isEditing = false;
|
|
704
|
+
if (state.inlineEditor) {
|
|
705
|
+
const cleanup = state.inlineEditor.__cleanup;
|
|
706
|
+
if (cleanup) cleanup();
|
|
707
|
+
state.inlineEditor.remove();
|
|
708
|
+
state.inlineEditor = null;
|
|
709
|
+
}
|
|
710
|
+
if (state.editToolbar) {
|
|
711
|
+
state.editToolbar.style.display = "none";
|
|
712
|
+
}
|
|
713
|
+
clearSelection();
|
|
714
|
+
if (state.cssPanel) {
|
|
715
|
+
state.cssPanel.style.opacity = "0";
|
|
716
|
+
}
|
|
717
|
+
if (state.isActive && state.hoveredElement) {
|
|
718
|
+
if (state.overlay) state.overlay.style.display = "block";
|
|
719
|
+
if (state.tooltip) state.tooltip.style.display = "block";
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function enterEditMode() {
|
|
723
|
+
if (!state.selectedElement) return;
|
|
724
|
+
state.isEditing = true;
|
|
725
|
+
state.originalHTML = state.selectedElement.innerHTML;
|
|
726
|
+
if (state.overlay) state.overlay.style.display = "none";
|
|
727
|
+
if (state.tooltip) state.tooltip.style.display = "none";
|
|
728
|
+
if (state.cssPanel) state.cssPanel.style.opacity = "0";
|
|
729
|
+
if (!state.editToolbar) {
|
|
730
|
+
state.editToolbar = createEditToolbar(saveInlineEdit, cancelInlineEdit);
|
|
731
|
+
}
|
|
732
|
+
state.editToolbar.style.display = "flex";
|
|
733
|
+
updateEditToolbarPosition(state);
|
|
734
|
+
state.inlineEditor = enableInlineEditing(state, state.selectedElement, saveInlineEdit, cancelInlineEdit);
|
|
735
|
+
const info = getElementInfo(state.selectedElement);
|
|
736
|
+
sendToParent("START_INLINE_EDIT", {
|
|
737
|
+
tagName: info.tagName,
|
|
738
|
+
filePath: info.filePath,
|
|
739
|
+
line: info.line,
|
|
740
|
+
text: state.originalText
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
function clearSelection() {
|
|
744
|
+
state.isSelected = false;
|
|
745
|
+
state.selectedElement = null;
|
|
746
|
+
state.originalText = "";
|
|
747
|
+
state.originalHTML = "";
|
|
748
|
+
if (state.selectionOverlay) {
|
|
749
|
+
state.selectionOverlay.style.display = "none";
|
|
750
|
+
}
|
|
751
|
+
if (state.cssPanel) {
|
|
752
|
+
state.cssPanel.style.opacity = "0";
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function selectElement() {
|
|
756
|
+
if (!state.hoveredElement) return;
|
|
757
|
+
if (state.isSelected && state.selectedElement === state.hoveredElement) {
|
|
758
|
+
enterEditMode();
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (state.isSelected) {
|
|
762
|
+
clearSelection();
|
|
763
|
+
}
|
|
764
|
+
state.isSelected = true;
|
|
765
|
+
state.selectedElement = state.hoveredElement;
|
|
766
|
+
const info = getElementInfo(state.hoveredElement);
|
|
767
|
+
const text = getElementTextContent(state.hoveredElement);
|
|
768
|
+
const cssInfo = getCSSPreview(state.hoveredElement);
|
|
769
|
+
const breadcrumb = getBreadcrumb(state.hoveredElement);
|
|
770
|
+
state.originalText = text;
|
|
771
|
+
if (!state.selectionOverlay) {
|
|
772
|
+
state.selectionOverlay = createSelectionOverlay();
|
|
773
|
+
}
|
|
774
|
+
state.selectionOverlay.style.display = "block";
|
|
775
|
+
updateSelectionOverlayPosition(state);
|
|
776
|
+
if (state.tooltip) {
|
|
777
|
+
state.tooltip.innerHTML = `
|
|
778
|
+
<div style="display:flex;flex-direction:column;gap:6px;">
|
|
779
|
+
<div style="color:#64748b;font-size:10px;font-family:ui-monospace,monospace;">
|
|
780
|
+
${breadcrumb}
|
|
781
|
+
</div>
|
|
782
|
+
<div style="color:#94a3b8;font-size:10px;border-top:1px solid rgba(255,255,255,0.1);padding-top:6px;margin-top:2px;">
|
|
783
|
+
\u2713 Selected \xB7 Click again to edit \xB7 <kbd style="background:#334155;padding:1px 4px;border-radius:3px;">Enter</kbd> or <kbd style="background:#334155;padding:1px 4px;border-radius:3px;">E</kbd>
|
|
784
|
+
</div>
|
|
785
|
+
</div>
|
|
786
|
+
`;
|
|
787
|
+
}
|
|
788
|
+
sendToParent("CLICK_ELEMENT", {
|
|
789
|
+
tagName: info.tagName,
|
|
790
|
+
filePath: info.filePath,
|
|
791
|
+
line: info.line,
|
|
792
|
+
depth: info.depth,
|
|
793
|
+
text,
|
|
794
|
+
cssInfo
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
function startEditing() {
|
|
798
|
+
if (!state.hoveredElement) return;
|
|
799
|
+
state.isSelected = true;
|
|
800
|
+
state.selectedElement = state.hoveredElement;
|
|
801
|
+
const info = getElementInfo(state.hoveredElement);
|
|
802
|
+
const text = getElementTextContent(state.hoveredElement);
|
|
803
|
+
const cssInfo = getCSSPreview(state.hoveredElement);
|
|
804
|
+
state.originalText = text;
|
|
805
|
+
if (!state.selectionOverlay) {
|
|
806
|
+
state.selectionOverlay = createSelectionOverlay();
|
|
807
|
+
}
|
|
808
|
+
state.selectionOverlay.style.display = "block";
|
|
809
|
+
updateSelectionOverlayPosition(state);
|
|
810
|
+
sendToParent("CLICK_ELEMENT", {
|
|
811
|
+
tagName: info.tagName,
|
|
812
|
+
filePath: info.filePath,
|
|
813
|
+
line: info.line,
|
|
814
|
+
depth: info.depth,
|
|
815
|
+
text,
|
|
816
|
+
cssInfo
|
|
817
|
+
});
|
|
818
|
+
enterEditMode();
|
|
819
|
+
}
|
|
820
|
+
const handleMouseMove = (e) => {
|
|
821
|
+
if (!state.isActive || state.isEditing) return;
|
|
822
|
+
const now = performance.now();
|
|
823
|
+
if (now - state.lastMouseMoveTime < MOUSEMOVE_DEBOUNCE) {
|
|
824
|
+
if (state.pendingMouseMove) cancelAnimationFrame(state.pendingMouseMove);
|
|
825
|
+
state.pendingMouseMove = requestAnimationFrame(() => processMouseMove(e));
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
state.lastMouseMoveTime = now;
|
|
829
|
+
processMouseMove(e);
|
|
830
|
+
};
|
|
831
|
+
const processMouseMove = (e) => {
|
|
832
|
+
const element = getElementAtPoint(e.clientX, e.clientY);
|
|
833
|
+
if (!element) return;
|
|
834
|
+
if (element === state.hoveredElement) return;
|
|
835
|
+
state.rawTarget = e.target;
|
|
836
|
+
highlightElement(state, element);
|
|
837
|
+
};
|
|
838
|
+
const handleScroll = () => {
|
|
839
|
+
if (state.isActive && state.hoveredElement && !state.isEditing) {
|
|
840
|
+
updateOverlayPosition(state);
|
|
841
|
+
updateTooltipPosition(state);
|
|
842
|
+
updateCSSPanel(state, state.hoveredElement);
|
|
843
|
+
}
|
|
844
|
+
if (state.isSelected && state.selectedElement) {
|
|
845
|
+
updateSelectionOverlayPosition(state);
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
const handleResize = () => {
|
|
849
|
+
if (state.isActive && state.hoveredElement && !state.isEditing) {
|
|
850
|
+
updateOverlayPosition(state);
|
|
851
|
+
updateTooltipPosition(state);
|
|
852
|
+
updateCSSPanel(state, state.hoveredElement);
|
|
853
|
+
}
|
|
854
|
+
if (state.isSelected && state.selectedElement) {
|
|
855
|
+
updateSelectionOverlayPosition(state);
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
const handleKeyDown = (e) => {
|
|
859
|
+
if (!state.isActive) return;
|
|
860
|
+
if (state.isEditing) {
|
|
861
|
+
if (e.key === "Escape") {
|
|
862
|
+
e.preventDefault();
|
|
863
|
+
cancelInlineEdit();
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
switch (e.key) {
|
|
869
|
+
case "Escape":
|
|
870
|
+
if (state.isSelected) {
|
|
871
|
+
e.preventDefault();
|
|
872
|
+
clearSelection();
|
|
873
|
+
} else {
|
|
874
|
+
sendToParent("SELECTOR_EXIT", true);
|
|
875
|
+
}
|
|
876
|
+
break;
|
|
877
|
+
case "ArrowUp":
|
|
878
|
+
e.preventDefault();
|
|
879
|
+
navigateDOM(state, "parent");
|
|
880
|
+
break;
|
|
881
|
+
case "ArrowDown":
|
|
882
|
+
e.preventDefault();
|
|
883
|
+
navigateDOM(state, "child");
|
|
884
|
+
break;
|
|
885
|
+
case "ArrowLeft":
|
|
886
|
+
e.preventDefault();
|
|
887
|
+
navigateDOM(state, "prev");
|
|
888
|
+
break;
|
|
889
|
+
case "ArrowRight":
|
|
890
|
+
e.preventDefault();
|
|
891
|
+
navigateDOM(state, "next");
|
|
892
|
+
break;
|
|
893
|
+
case "Enter":
|
|
894
|
+
e.preventDefault();
|
|
895
|
+
if (state.isSelected && state.selectedElement === state.hoveredElement) {
|
|
896
|
+
enterEditMode();
|
|
897
|
+
} else {
|
|
898
|
+
selectElement();
|
|
899
|
+
}
|
|
900
|
+
break;
|
|
901
|
+
case "e":
|
|
902
|
+
case "E":
|
|
903
|
+
if (state.hoveredElement) {
|
|
904
|
+
e.preventDefault();
|
|
905
|
+
startEditing();
|
|
906
|
+
}
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
const handleClick = (e) => {
|
|
911
|
+
if (!state.isActive) return;
|
|
912
|
+
if (state.isEditing) {
|
|
913
|
+
const target = e.target;
|
|
914
|
+
if (state.selectedElement?.contains(target) || target === state.selectedElement) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
if (state.editToolbar?.contains(target)) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
e.preventDefault();
|
|
921
|
+
e.stopPropagation();
|
|
922
|
+
e.stopImmediatePropagation();
|
|
923
|
+
saveInlineEdit();
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
e.preventDefault();
|
|
927
|
+
e.stopPropagation();
|
|
928
|
+
e.stopImmediatePropagation();
|
|
929
|
+
selectElement();
|
|
930
|
+
};
|
|
931
|
+
const handleDoubleClick = (e) => {
|
|
932
|
+
if (!state.isActive || state.isEditing) return;
|
|
933
|
+
e.preventDefault();
|
|
934
|
+
e.stopPropagation();
|
|
935
|
+
e.stopImmediatePropagation();
|
|
936
|
+
startEditing();
|
|
937
|
+
};
|
|
938
|
+
const handleMouseLeave = () => {
|
|
939
|
+
if (!state.isActive || state.isEditing) return;
|
|
940
|
+
state.hoveredElement = null;
|
|
941
|
+
state.rawTarget = null;
|
|
942
|
+
if (state.overlay) {
|
|
943
|
+
state.overlay.style.display = "none";
|
|
944
|
+
}
|
|
945
|
+
if (state.tooltip) {
|
|
946
|
+
state.tooltip.style.display = "none";
|
|
947
|
+
}
|
|
948
|
+
if (state.cssPanel) {
|
|
949
|
+
state.cssPanel.style.opacity = "0";
|
|
950
|
+
}
|
|
951
|
+
sendToParent("HOVER_ELEMENT", null);
|
|
952
|
+
};
|
|
953
|
+
const activateSelector = () => {
|
|
954
|
+
state.isActive = true;
|
|
955
|
+
document.addEventListener("mousemove", handleMouseMove, true);
|
|
956
|
+
document.addEventListener("mouseleave", handleMouseLeave, true);
|
|
957
|
+
document.addEventListener("click", handleClick, true);
|
|
958
|
+
document.addEventListener("dblclick", handleDoubleClick, true);
|
|
959
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
960
|
+
window.addEventListener("scroll", handleScroll, true);
|
|
961
|
+
window.addEventListener("resize", handleResize);
|
|
962
|
+
document.body.style.cursor = "crosshair";
|
|
963
|
+
state.overlay = createOverlay();
|
|
964
|
+
state.tooltip = createTooltip();
|
|
965
|
+
};
|
|
966
|
+
const deactivateSelector = () => {
|
|
967
|
+
state.isActive = false;
|
|
968
|
+
state.isEditing = false;
|
|
969
|
+
state.isSelected = false;
|
|
970
|
+
if (state.pendingMouseMove) {
|
|
971
|
+
cancelAnimationFrame(state.pendingMouseMove);
|
|
972
|
+
state.pendingMouseMove = null;
|
|
973
|
+
}
|
|
974
|
+
document.removeEventListener("mousemove", handleMouseMove, true);
|
|
975
|
+
document.removeEventListener("mouseleave", handleMouseLeave, true);
|
|
976
|
+
document.removeEventListener("click", handleClick, true);
|
|
977
|
+
document.removeEventListener("dblclick", handleDoubleClick, true);
|
|
978
|
+
document.removeEventListener("keydown", handleKeyDown, true);
|
|
979
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
980
|
+
window.removeEventListener("resize", handleResize);
|
|
981
|
+
if (state.overlay) {
|
|
982
|
+
state.overlay.remove();
|
|
983
|
+
state.overlay = null;
|
|
984
|
+
}
|
|
985
|
+
if (state.selectionOverlay) {
|
|
986
|
+
state.selectionOverlay.remove();
|
|
987
|
+
state.selectionOverlay = null;
|
|
988
|
+
}
|
|
989
|
+
if (state.tooltip) {
|
|
990
|
+
state.tooltip.remove();
|
|
991
|
+
state.tooltip = null;
|
|
992
|
+
}
|
|
993
|
+
if (state.cssPanel) {
|
|
994
|
+
state.cssPanel.remove();
|
|
995
|
+
state.cssPanel = null;
|
|
996
|
+
}
|
|
997
|
+
if (state.editToolbar) {
|
|
998
|
+
state.editToolbar.remove();
|
|
999
|
+
state.editToolbar = null;
|
|
1000
|
+
}
|
|
1001
|
+
if (state.inlineEditor) {
|
|
1002
|
+
state.inlineEditor.remove();
|
|
1003
|
+
state.inlineEditor = null;
|
|
1004
|
+
}
|
|
1005
|
+
state.hoveredElement = null;
|
|
1006
|
+
state.selectedElement = null;
|
|
1007
|
+
state.rawTarget = null;
|
|
1008
|
+
state.originalText = "";
|
|
1009
|
+
document.body.style.cursor = "";
|
|
1010
|
+
state.depth = 0;
|
|
1011
|
+
};
|
|
1012
|
+
window.addEventListener("message", (e) => {
|
|
1013
|
+
const msg = e.data;
|
|
1014
|
+
if (msg.type === "TOGGLE_ELEMENT_SELECTOR") {
|
|
1015
|
+
if (msg.payload) {
|
|
1016
|
+
activateSelector();
|
|
1017
|
+
} else {
|
|
1018
|
+
deactivateSelector();
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
function initIframeBridge() {
|
|
1024
|
+
try {
|
|
1025
|
+
if (window.parent !== window) {
|
|
1026
|
+
setupIframeBridge();
|
|
1027
|
+
setupElementSelector();
|
|
1028
|
+
}
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
export {
|
|
1034
|
+
sendToParent,
|
|
1035
|
+
setupIframeBridge,
|
|
1036
|
+
setupElementSelector,
|
|
1037
|
+
initIframeBridge
|
|
1038
|
+
};
|