@lego-build/plugins 0.0.13 → 0.0.15

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.
@@ -0,0 +1,774 @@
1
+ // src/index.ts
2
+ var INTERACTIVE_ELEMENTS = /* @__PURE__ */ new Set([
3
+ "a",
4
+ "button",
5
+ "input",
6
+ "select",
7
+ "textarea",
8
+ "label",
9
+ "details",
10
+ "summary",
11
+ "dialog",
12
+ "menu",
13
+ "menuitem"
14
+ ]);
15
+ var SEMANTIC_ELEMENTS = /* @__PURE__ */ new Set([
16
+ "header",
17
+ "footer",
18
+ "main",
19
+ "nav",
20
+ "aside",
21
+ "section",
22
+ "article",
23
+ "h1",
24
+ "h2",
25
+ "h3",
26
+ "h4",
27
+ "h5",
28
+ "h6",
29
+ "p",
30
+ "figure",
31
+ "figcaption",
32
+ "ul",
33
+ "ol",
34
+ "li",
35
+ "dl",
36
+ "dt",
37
+ "dd",
38
+ "table",
39
+ "thead",
40
+ "tbody",
41
+ "tr",
42
+ "td",
43
+ "th",
44
+ "form",
45
+ "fieldset",
46
+ "legend",
47
+ "img",
48
+ "video",
49
+ "audio",
50
+ "canvas",
51
+ "svg"
52
+ ]);
53
+ var CSS_PREVIEW_PROPERTIES = [
54
+ "display",
55
+ "position",
56
+ "width",
57
+ "height",
58
+ "padding",
59
+ "margin",
60
+ "color",
61
+ "background-color",
62
+ "font-size",
63
+ "font-weight"
64
+ ];
65
+ function sendToParent(type, payload) {
66
+ window.parent.postMessage({ type, payload }, "*");
67
+ }
68
+ function setupIframeBridge() {
69
+ const pushState = window.history.pushState.bind(window.history);
70
+ const originalPushState = (...args) => {
71
+ pushState(...args);
72
+ sendToParent("IFRAME_URL_CHANGED", window.location.href);
73
+ };
74
+ window.history.pushState = originalPushState;
75
+ const replaceState = window.history.replaceState.bind(window.history);
76
+ const originalReplaceState = (...args) => {
77
+ replaceState(...args);
78
+ sendToParent("IFRAME_URL_CHANGED", window.location.href);
79
+ };
80
+ window.history.replaceState = originalReplaceState;
81
+ window.addEventListener("popstate", () => {
82
+ sendToParent("IFRAME_URL_CHANGED", window.location.href);
83
+ });
84
+ window.addEventListener("message", (e) => {
85
+ const msg = e.data;
86
+ if (!msg?.type) return;
87
+ switch (msg.type) {
88
+ case "NAVIGATE_BACK":
89
+ window.history.back();
90
+ break;
91
+ case "NAVIGATE_FORWARD":
92
+ window.history.forward();
93
+ break;
94
+ case "NAVIGATE_URL":
95
+ if (msg.payload) {
96
+ window.history.pushState({}, "", msg.payload);
97
+ sendToParent("IFRAME_URL_CHANGED", window.location.href);
98
+ }
99
+ break;
100
+ case "REFRESH":
101
+ window.location.reload();
102
+ break;
103
+ }
104
+ });
105
+ }
106
+ function createElementSelectorState() {
107
+ return {
108
+ isActive: false,
109
+ isEditing: false,
110
+ isSelected: false,
111
+ hoveredElement: null,
112
+ selectedElement: null,
113
+ overlay: null,
114
+ selectionOverlay: null,
115
+ tooltip: null,
116
+ cssPanel: null,
117
+ inlineEditor: null,
118
+ depth: 0,
119
+ originalText: "",
120
+ originalHTML: "",
121
+ lastMouseMoveTime: 0,
122
+ pendingMouseMove: null,
123
+ rawTarget: null
124
+ };
125
+ }
126
+ function getElementPriority(element) {
127
+ const tagName = element.tagName.toLowerCase();
128
+ let score = 0;
129
+ if (INTERACTIVE_ELEMENTS.has(tagName)) score += 100;
130
+ if (SEMANTIC_ELEMENTS.has(tagName)) score += 50;
131
+ if (element.hasAttribute("data-locator-path")) score += 200;
132
+ if (element.id) score += 20;
133
+ if (element.className && typeof element.className === "string") {
134
+ const classes = element.className.split(" ").filter((c) => c && !c.startsWith("__"));
135
+ score += Math.min(classes.length * 5, 25);
136
+ }
137
+ const textContent = element.textContent?.trim() || "";
138
+ if (textContent.length > 0 && textContent.length < 200) score += 15;
139
+ if ((tagName === "div" || tagName === "span") && !element.id && !element.className) {
140
+ score -= 30;
141
+ }
142
+ if (element.hasAttribute("role")) score += 25;
143
+ const attrs = element.attributes;
144
+ for (let i = 0; i < attrs.length; i++) {
145
+ if (attrs[i].name.startsWith("aria-")) {
146
+ score += 10;
147
+ break;
148
+ }
149
+ }
150
+ return score;
151
+ }
152
+ function getBreadcrumb(element, maxDepth = 4) {
153
+ const parts = [];
154
+ let current = element;
155
+ let depth = 0;
156
+ while (current && current !== document.body && depth < maxDepth) {
157
+ const tagName = current.tagName.toLowerCase();
158
+ let part = tagName;
159
+ if (current.id) {
160
+ part += `#${current.id}`;
161
+ } else if (current.className && typeof current.className === "string") {
162
+ const classes = current.className.split(" ").filter((c) => c && !c.startsWith("__") && c.length < 20);
163
+ if (classes.length > 0) {
164
+ part += `.${classes[0]}`;
165
+ }
166
+ }
167
+ parts.unshift(part);
168
+ current = current.parentElement;
169
+ depth++;
170
+ }
171
+ if (current && current !== document.body) {
172
+ parts.unshift("...");
173
+ }
174
+ return parts.join(" \u203A ");
175
+ }
176
+ function getCSSPreview(element) {
177
+ const style = window.getComputedStyle(element);
178
+ const result = {};
179
+ for (const prop of CSS_PREVIEW_PROPERTIES) {
180
+ const value = style.getPropertyValue(prop);
181
+ if (value && value !== "none" && value !== "auto" && value !== "normal") {
182
+ if (prop.includes("color") && value.startsWith("rgb")) {
183
+ result[prop] = rgbToHex(value);
184
+ } else {
185
+ result[prop] = value;
186
+ }
187
+ }
188
+ }
189
+ return result;
190
+ }
191
+ function rgbToHex(rgb) {
192
+ const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
193
+ if (!match) return rgb;
194
+ const r = parseInt(match[1]).toString(16).padStart(2, "0");
195
+ const g = parseInt(match[2]).toString(16).padStart(2, "0");
196
+ const b = parseInt(match[3]).toString(16).padStart(2, "0");
197
+ return `#${r}${g}${b}`;
198
+ }
199
+ function createOverlay() {
200
+ const overlay = document.createElement("div");
201
+ overlay.id = "lego-element-selector-overlay";
202
+ overlay.style.cssText = `
203
+ position: fixed;
204
+ pointer-events: none;
205
+ z-index: 2147483645;
206
+ background: rgba(59, 130, 246, 0.1);
207
+ border: 2px solid #3b82f6;
208
+ border-radius: 4px;
209
+ transition: all 0.1s ease-out;
210
+ `;
211
+ document.body.appendChild(overlay);
212
+ return overlay;
213
+ }
214
+ function createSelectionOverlay() {
215
+ const overlay = document.createElement("div");
216
+ overlay.id = "lego-element-selector-selection";
217
+ overlay.style.cssText = `
218
+ position: fixed;
219
+ pointer-events: none;
220
+ z-index: 2147483646;
221
+ background: rgba(34, 197, 94, 0.15);
222
+ border: 2px solid #22c55e;
223
+ border-radius: 4px;
224
+ box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2);
225
+ transition: all 0.15s ease-out;
226
+ `;
227
+ document.body.appendChild(overlay);
228
+ return overlay;
229
+ }
230
+ function createTooltip() {
231
+ const tooltip = document.createElement("div");
232
+ tooltip.id = "lego-element-selector-tooltip";
233
+ tooltip.style.cssText = `
234
+ position: fixed;
235
+ pointer-events: none;
236
+ z-index: 2147483647;
237
+ background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
238
+ color: white;
239
+ padding: 8px 12px;
240
+ border-radius: 8px;
241
+ font-family: ui-sans-serif, system-ui, sans-serif;
242
+ font-size: 12px;
243
+ max-width: 400px;
244
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1);
245
+ transition: all 0.1s ease-out;
246
+ backdrop-filter: blur(8px);
247
+ `;
248
+ document.body.appendChild(tooltip);
249
+ return tooltip;
250
+ }
251
+ function createCSSPanel() {
252
+ const panel = document.createElement("div");
253
+ panel.id = "lego-element-selector-css";
254
+ panel.style.cssText = `
255
+ position: fixed;
256
+ pointer-events: none;
257
+ z-index: 2147483646;
258
+ background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
259
+ color: white;
260
+ padding: 10px 14px;
261
+ border-radius: 8px;
262
+ font-family: ui-monospace, monospace;
263
+ font-size: 11px;
264
+ max-width: 280px;
265
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1);
266
+ transition: all 0.15s ease-out;
267
+ opacity: 0;
268
+ transform: translateY(4px);
269
+ backdrop-filter: blur(8px);
270
+ `;
271
+ document.body.appendChild(panel);
272
+ return panel;
273
+ }
274
+ function updateCSSPanel(state, element) {
275
+ if (!state.cssPanel) {
276
+ state.cssPanel = createCSSPanel();
277
+ }
278
+ const cssInfo = getCSSPreview(element);
279
+ const entries = Object.entries(cssInfo);
280
+ if (entries.length === 0) {
281
+ state.cssPanel.style.opacity = "0";
282
+ return;
283
+ }
284
+ const html = entries.map(([prop, value]) => {
285
+ 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>` : "";
286
+ return `<div style="display:flex;justify-content:space-between;align-items:center;padding:2px 0;">
287
+ <span style="color:#94a3b8;">${prop}:</span>
288
+ <span style="color:#e2e8f0;margin-left:12px;">${colorDot}${value}</span>
289
+ </div>`;
290
+ }).join("");
291
+ state.cssPanel.innerHTML = `
292
+ <div style="font-weight:600;color:#60a5fa;margin-bottom:6px;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;">CSS Properties</div>
293
+ ${html}
294
+ `;
295
+ const rect = element.getBoundingClientRect();
296
+ const tooltipHeight = state.tooltip?.offsetHeight || 40;
297
+ let panelTop = rect.top - tooltipHeight - 60;
298
+ const panelLeft = Math.min(rect.left, window.innerWidth - 300);
299
+ if (panelTop < 8) {
300
+ panelTop = rect.bottom + tooltipHeight + 16;
301
+ }
302
+ state.cssPanel.style.top = `${panelTop}px`;
303
+ state.cssPanel.style.left = `${Math.max(8, panelLeft)}px`;
304
+ state.cssPanel.style.opacity = "1";
305
+ state.cssPanel.style.transform = "translateY(0)";
306
+ }
307
+ function enableInlineEditing(state, element, onSave, onCancel) {
308
+ const originalOutline = element.style.outline;
309
+ const originalBoxShadow = element.style.boxShadow;
310
+ const originalBackground = element.style.background;
311
+ element.contentEditable = "true";
312
+ element.style.outline = "2px solid #3b82f6";
313
+ element.style.boxShadow = "0 0 0 4px rgba(59, 130, 246, 0.2)";
314
+ element.style.background = "rgba(59, 130, 246, 0.05)";
315
+ element.style.borderRadius = "4px";
316
+ const marker = document.createElement("div");
317
+ marker.id = "lego-inline-editor";
318
+ marker.style.display = "none";
319
+ marker.dataset.originalOutline = originalOutline;
320
+ marker.dataset.originalBoxShadow = originalBoxShadow;
321
+ marker.dataset.originalBackground = originalBackground;
322
+ document.body.appendChild(marker);
323
+ element.focus();
324
+ const range = document.createRange();
325
+ range.selectNodeContents(element);
326
+ range.collapse(false);
327
+ const selection = window.getSelection();
328
+ selection?.removeAllRanges();
329
+ selection?.addRange(range);
330
+ const handleKeyDown = (e) => {
331
+ if (e.key === "Escape") {
332
+ e.preventDefault();
333
+ e.stopPropagation();
334
+ onCancel();
335
+ } else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
336
+ e.preventDefault();
337
+ e.stopPropagation();
338
+ onSave();
339
+ }
340
+ };
341
+ const handleBlur = (e) => {
342
+ setTimeout(() => {
343
+ if (document.activeElement !== element && state.isEditing) {
344
+ onSave();
345
+ }
346
+ }, 100);
347
+ };
348
+ element.addEventListener("keydown", handleKeyDown);
349
+ element.addEventListener("blur", handleBlur);
350
+ marker.__cleanup = () => {
351
+ element.removeEventListener("keydown", handleKeyDown);
352
+ element.removeEventListener("blur", handleBlur);
353
+ element.contentEditable = "false";
354
+ element.style.outline = originalOutline;
355
+ element.style.boxShadow = originalBoxShadow;
356
+ element.style.background = originalBackground;
357
+ element.style.borderRadius = "";
358
+ window.getSelection()?.removeAllRanges();
359
+ };
360
+ return marker;
361
+ }
362
+ function getElementTextContent(element) {
363
+ return element.innerText?.trim() || element.textContent?.trim() || "";
364
+ }
365
+ function getElementDepth(element) {
366
+ let depth = 0;
367
+ let current = element;
368
+ while (current && current !== document.body) {
369
+ depth++;
370
+ current = current.parentElement;
371
+ }
372
+ return depth;
373
+ }
374
+ function updateOverlayPosition(state, element, overlay) {
375
+ const targetElement = element || state.hoveredElement;
376
+ const targetOverlay = overlay || state.overlay;
377
+ if (!targetElement || !targetOverlay) return;
378
+ const rect = targetElement.getBoundingClientRect();
379
+ targetOverlay.style.top = `${rect.top}px`;
380
+ targetOverlay.style.left = `${rect.left}px`;
381
+ targetOverlay.style.width = `${rect.width}px`;
382
+ targetOverlay.style.height = `${rect.height}px`;
383
+ }
384
+ function updateTooltipPosition(state) {
385
+ if (!state.hoveredElement || !state.tooltip) return;
386
+ const rect = state.hoveredElement.getBoundingClientRect();
387
+ const viewportWidth = window.innerWidth;
388
+ let tooltipTop = rect.top - 32;
389
+ let tooltipLeft = rect.left;
390
+ if (tooltipTop < 8) {
391
+ tooltipTop = rect.bottom + 8;
392
+ }
393
+ const estimatedTooltipWidth = state.tooltip.offsetWidth || 150;
394
+ if (tooltipLeft + estimatedTooltipWidth > viewportWidth - 8) {
395
+ tooltipLeft = viewportWidth - estimatedTooltipWidth - 8;
396
+ }
397
+ if (tooltipLeft < 8) {
398
+ tooltipLeft = 8;
399
+ }
400
+ state.tooltip.style.top = `${tooltipTop}px`;
401
+ state.tooltip.style.left = `${tooltipLeft}px`;
402
+ }
403
+ function updateSelectionOverlayPosition(state) {
404
+ if (!state.selectedElement || !state.selectionOverlay) return;
405
+ updateOverlayPosition(state, state.selectedElement, state.selectionOverlay);
406
+ }
407
+ function getElementInfo(element) {
408
+ const filePath = element.getAttribute("data-locator-path") || "";
409
+ const line = element.getAttribute("data-locator-line");
410
+ const depth = getElementDepth(element);
411
+ const id = element.id ? `#${element.id}` : "";
412
+ const classes = element.className && typeof element.className === "string" ? element.className.split(" ").filter((c) => c).map((c) => `.${c}`).join("") : "";
413
+ return {
414
+ tagName: element.tagName.toLowerCase(),
415
+ selector: element.tagName.toLowerCase() + id + classes.slice(0, 50),
416
+ filePath,
417
+ line: line ? parseInt(line) : void 0,
418
+ depth,
419
+ rect: element.getBoundingClientRect()
420
+ };
421
+ }
422
+ function highlightElement(state, element) {
423
+ state.hoveredElement = element;
424
+ if (!state.overlay) {
425
+ state.overlay = createOverlay();
426
+ }
427
+ if (!state.tooltip) {
428
+ state.tooltip = createTooltip();
429
+ }
430
+ state.overlay.style.display = "block";
431
+ state.tooltip.style.display = "block";
432
+ const info = getElementInfo(element);
433
+ const breadcrumb = getBreadcrumb(element);
434
+ state.depth = info.depth;
435
+ const isSelectedElement = state.isSelected && state.selectedElement === element;
436
+ const priority = getElementPriority(element);
437
+ const priorityIndicator = priority >= 100 ? "\u{1F3AF}" : priority >= 50 ? "\u{1F4E6}" : "";
438
+ const tagBadgeColor = INTERACTIVE_ELEMENTS.has(info.tagName) ? "#22c55e" : SEMANTIC_ELEMENTS.has(info.tagName) ? "#3b82f6" : "#64748b";
439
+ state.tooltip.innerHTML = `
440
+ <div style="display:flex;flex-direction:column;gap:6px;">
441
+ <div style="display:flex;align-items:center;gap:8px;">
442
+ <span style="background:${tagBadgeColor};padding:2px 8px;border-radius:4px;font-family:ui-monospace,monospace;font-size:11px;font-weight:600;">
443
+ ${priorityIndicator} ${info.selector}
444
+ </span>
445
+ ${info.filePath ? `<span style="color:#94a3b8;font-size:11px;">\u{1F4C4} ${info.filePath.split("/").pop()}${info.line ? `:${info.line}` : ""}</span>` : ""}
446
+ </div>
447
+ <div style="color:#64748b;font-size:10px;font-family:ui-monospace,monospace;">
448
+ ${breadcrumb}
449
+ </div>
450
+ <div style="color:#94a3b8;font-size:10px;border-top:1px solid rgba(255,255,255,0.1);padding-top:6px;margin-top:2px;">
451
+ ${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`}
452
+ </div>
453
+ </div>
454
+ `;
455
+ updateOverlayPosition(state);
456
+ updateTooltipPosition(state);
457
+ updateCSSPanel(state, element);
458
+ sendToParent("HOVER_ELEMENT", {
459
+ tagName: info.tagName,
460
+ rect: info.rect,
461
+ filePath: info.filePath || void 0,
462
+ line: info.line,
463
+ depth: info.depth,
464
+ breadcrumb
465
+ });
466
+ }
467
+ function navigateDOM(state, direction) {
468
+ if (!state.hoveredElement) return;
469
+ let target = null;
470
+ switch (direction) {
471
+ case "parent":
472
+ target = state.hoveredElement.parentElement;
473
+ if (target && target === document.body) target = null;
474
+ break;
475
+ case "child":
476
+ target = state.hoveredElement.firstElementChild;
477
+ break;
478
+ case "next":
479
+ target = state.hoveredElement.nextElementSibling;
480
+ break;
481
+ case "prev":
482
+ target = state.hoveredElement.previousElementSibling;
483
+ break;
484
+ }
485
+ if (target && target !== document.documentElement && target !== document.body) {
486
+ highlightElement(state, target);
487
+ }
488
+ }
489
+ function setupElementSelector() {
490
+ const state = createElementSelectorState();
491
+ function saveInlineEdit() {
492
+ if (!state.inlineEditor || !state.selectedElement) return;
493
+ const newText = getElementTextContent(state.selectedElement);
494
+ const info = getElementInfo(state.selectedElement);
495
+ if (newText !== state.originalText && info.filePath) {
496
+ sendToParent("SAVE_INLINE_EDIT", {
497
+ filePath: info.filePath,
498
+ originalContent: state.originalText,
499
+ newContent: newText,
500
+ line: info.line
501
+ });
502
+ }
503
+ exitEditMode();
504
+ }
505
+ function cancelInlineEdit() {
506
+ if (state.selectedElement && state.originalHTML !== void 0) {
507
+ state.selectedElement.innerHTML = state.originalHTML;
508
+ }
509
+ sendToParent("CANCEL_INLINE_EDIT");
510
+ exitEditMode();
511
+ }
512
+ function exitEditMode() {
513
+ state.isEditing = false;
514
+ if (state.inlineEditor) {
515
+ const cleanup = state.inlineEditor.__cleanup;
516
+ if (cleanup) cleanup();
517
+ state.inlineEditor.remove();
518
+ state.inlineEditor = null;
519
+ }
520
+ clearSelection();
521
+ if (state.isActive && state.hoveredElement) {
522
+ if (state.overlay) state.overlay.style.display = "block";
523
+ if (state.tooltip) state.tooltip.style.display = "block";
524
+ }
525
+ }
526
+ function enterEditMode() {
527
+ if (!state.selectedElement) return;
528
+ state.isEditing = true;
529
+ state.originalHTML = state.selectedElement.innerHTML;
530
+ if (state.overlay) state.overlay.style.display = "none";
531
+ if (state.tooltip) state.tooltip.style.display = "none";
532
+ if (state.selectionOverlay) state.selectionOverlay.style.display = "none";
533
+ state.inlineEditor = enableInlineEditing(state, state.selectedElement, saveInlineEdit, cancelInlineEdit);
534
+ const info = getElementInfo(state.selectedElement);
535
+ sendToParent("START_INLINE_EDIT", {
536
+ tagName: info.tagName,
537
+ filePath: info.filePath,
538
+ line: info.line,
539
+ text: state.originalText
540
+ });
541
+ }
542
+ function clearSelection() {
543
+ state.isSelected = false;
544
+ state.selectedElement = null;
545
+ state.originalText = "";
546
+ state.originalHTML = "";
547
+ if (state.selectionOverlay) {
548
+ state.selectionOverlay.style.display = "none";
549
+ }
550
+ }
551
+ function selectElement() {
552
+ if (!state.hoveredElement) return;
553
+ if (state.isSelected && state.selectedElement === state.hoveredElement) {
554
+ enterEditMode();
555
+ return;
556
+ }
557
+ if (state.isSelected) {
558
+ clearSelection();
559
+ }
560
+ state.isSelected = true;
561
+ state.selectedElement = state.hoveredElement;
562
+ const info = getElementInfo(state.hoveredElement);
563
+ const text = getElementTextContent(state.hoveredElement);
564
+ state.originalText = text;
565
+ if (!state.selectionOverlay) {
566
+ state.selectionOverlay = createSelectionOverlay();
567
+ }
568
+ state.selectionOverlay.style.display = "block";
569
+ updateSelectionOverlayPosition(state);
570
+ if (state.tooltip) {
571
+ state.tooltip.innerHTML = `
572
+ <span style="opacity: 0.8">${info.selector}</span>
573
+ ${info.filePath ? `<span style="margin-left: 8px; opacity: 0.6">\u{1F4C4} ${info.filePath.split("/").pop()}</span>` : ""}
574
+ <span style="margin-left: 8px; color: #22c55e; font-size: 10px;">\u2713 Selected \xB7 Click again to edit</span>
575
+ `;
576
+ }
577
+ sendToParent("CLICK_ELEMENT", {
578
+ tagName: info.tagName,
579
+ filePath: info.filePath,
580
+ line: info.line,
581
+ depth: info.depth,
582
+ text
583
+ });
584
+ }
585
+ function startEditing() {
586
+ if (!state.hoveredElement) return;
587
+ state.selectedElement = state.hoveredElement;
588
+ const info = getElementInfo(state.hoveredElement);
589
+ const text = getElementTextContent(state.hoveredElement);
590
+ state.originalText = text;
591
+ sendToParent("CLICK_ELEMENT", {
592
+ tagName: info.tagName,
593
+ filePath: info.filePath,
594
+ line: info.line,
595
+ depth: info.depth,
596
+ text
597
+ });
598
+ enterEditMode();
599
+ }
600
+ const handleMouseMove = (e) => {
601
+ if (!state.isActive) return;
602
+ const target = e.target;
603
+ if (target.id === "lego-element-selector-overlay" || target.id === "lego-element-selector-tooltip" || target.id === "lego-element-selector-selection") {
604
+ return;
605
+ }
606
+ if (target === state.hoveredElement) return;
607
+ highlightElement(state, target);
608
+ };
609
+ const handleScroll = () => {
610
+ if (state.isActive && state.hoveredElement) {
611
+ updateOverlayPosition(state);
612
+ updateTooltipPosition(state);
613
+ }
614
+ if (state.isSelected && state.selectedElement) {
615
+ updateSelectionOverlayPosition(state);
616
+ }
617
+ };
618
+ const handleResize = () => {
619
+ if (state.isActive && state.hoveredElement) {
620
+ updateOverlayPosition(state);
621
+ updateTooltipPosition(state);
622
+ }
623
+ if (state.isSelected && state.selectedElement) {
624
+ updateSelectionOverlayPosition(state);
625
+ }
626
+ };
627
+ const handleKeyDown = (e) => {
628
+ if (!state.isActive) return;
629
+ if (state.isEditing) {
630
+ if (e.key === "Escape") {
631
+ e.preventDefault();
632
+ cancelInlineEdit();
633
+ return;
634
+ }
635
+ return;
636
+ }
637
+ switch (e.key) {
638
+ case "Escape":
639
+ if (state.isSelected) {
640
+ e.preventDefault();
641
+ clearSelection();
642
+ } else {
643
+ sendToParent("SELECTOR_EXIT", true);
644
+ }
645
+ break;
646
+ case "ArrowUp":
647
+ e.preventDefault();
648
+ navigateDOM(state, "parent");
649
+ break;
650
+ case "ArrowDown":
651
+ e.preventDefault();
652
+ navigateDOM(state, "child");
653
+ break;
654
+ case "ArrowLeft":
655
+ e.preventDefault();
656
+ navigateDOM(state, "prev");
657
+ break;
658
+ case "ArrowRight":
659
+ e.preventDefault();
660
+ navigateDOM(state, "next");
661
+ break;
662
+ case "Enter":
663
+ e.preventDefault();
664
+ if (state.isSelected && state.selectedElement === state.hoveredElement) {
665
+ enterEditMode();
666
+ } else {
667
+ selectElement();
668
+ }
669
+ break;
670
+ case "e":
671
+ case "E":
672
+ if (state.hoveredElement) {
673
+ e.preventDefault();
674
+ startEditing();
675
+ }
676
+ break;
677
+ }
678
+ };
679
+ const handleClick = (e) => {
680
+ if (!state.isActive) return;
681
+ if (state.isEditing) {
682
+ const target = e.target;
683
+ if (state.selectedElement?.contains(target) || target === state.selectedElement) {
684
+ return;
685
+ }
686
+ e.preventDefault();
687
+ e.stopPropagation();
688
+ e.stopImmediatePropagation();
689
+ saveInlineEdit();
690
+ return;
691
+ }
692
+ e.preventDefault();
693
+ e.stopPropagation();
694
+ e.stopImmediatePropagation();
695
+ selectElement();
696
+ };
697
+ const handleDoubleClick = (e) => {
698
+ if (!state.isActive || state.isEditing) return;
699
+ e.preventDefault();
700
+ e.stopPropagation();
701
+ e.stopImmediatePropagation();
702
+ startEditing();
703
+ };
704
+ const activateSelector = () => {
705
+ state.isActive = true;
706
+ document.addEventListener("mousemove", handleMouseMove, true);
707
+ document.addEventListener("click", handleClick, true);
708
+ document.addEventListener("dblclick", handleDoubleClick, true);
709
+ document.addEventListener("keydown", handleKeyDown, true);
710
+ window.addEventListener("scroll", handleScroll, true);
711
+ window.addEventListener("resize", handleResize);
712
+ document.body.style.cursor = "crosshair";
713
+ state.overlay = createOverlay();
714
+ state.tooltip = createTooltip();
715
+ };
716
+ const deactivateSelector = () => {
717
+ state.isActive = false;
718
+ state.isEditing = false;
719
+ state.isSelected = false;
720
+ document.removeEventListener("mousemove", handleMouseMove, true);
721
+ document.removeEventListener("click", handleClick, true);
722
+ document.removeEventListener("dblclick", handleDoubleClick, true);
723
+ document.removeEventListener("keydown", handleKeyDown, true);
724
+ window.removeEventListener("scroll", handleScroll, true);
725
+ window.removeEventListener("resize", handleResize);
726
+ if (state.overlay) {
727
+ state.overlay.remove();
728
+ state.overlay = null;
729
+ }
730
+ if (state.selectionOverlay) {
731
+ state.selectionOverlay.remove();
732
+ state.selectionOverlay = null;
733
+ }
734
+ if (state.tooltip) {
735
+ state.tooltip.remove();
736
+ state.tooltip = null;
737
+ }
738
+ if (state.inlineEditor) {
739
+ state.inlineEditor.remove();
740
+ state.inlineEditor = null;
741
+ }
742
+ state.hoveredElement = null;
743
+ state.selectedElement = null;
744
+ state.originalText = "";
745
+ document.body.style.cursor = "";
746
+ state.depth = 0;
747
+ };
748
+ window.addEventListener("message", (e) => {
749
+ const msg = e.data;
750
+ if (msg.type === "TOGGLE_ELEMENT_SELECTOR") {
751
+ if (msg.payload) {
752
+ activateSelector();
753
+ } else {
754
+ deactivateSelector();
755
+ }
756
+ }
757
+ });
758
+ }
759
+ function initIframeBridge() {
760
+ try {
761
+ if (window.parent !== window) {
762
+ setupIframeBridge();
763
+ setupElementSelector();
764
+ }
765
+ } catch {
766
+ }
767
+ }
768
+
769
+ export {
770
+ sendToParent,
771
+ setupIframeBridge,
772
+ setupElementSelector,
773
+ initIframeBridge
774
+ };