blissful-web-agentation 1.0.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/dist/agentation.cjs.js +851 -0
- package/dist/agentation.cjs.js.map +1 -0
- package/dist/agentation.d.ts +19 -0
- package/dist/agentation.esm.mjs +845 -0
- package/dist/agentation.esm.mjs.map +1 -0
- package/dist/agentation.iife.js +864 -0
- package/dist/agentation.iife.js.map +1 -0
- package/dist/agentation.iife.min.js +2 -0
- package/dist/agentation.iife.min.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = "agentation-prefs";
|
|
4
|
+
const defaults = {
|
|
5
|
+
active: false,
|
|
6
|
+
highlightColor: "#0063F7",
|
|
7
|
+
theme: "dark"
|
|
8
|
+
};
|
|
9
|
+
function loadPrefs() {
|
|
10
|
+
try {
|
|
11
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
12
|
+
if (raw) {
|
|
13
|
+
const parsed = JSON.parse(raw);
|
|
14
|
+
return { ...defaults, ...parsed };
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
return { ...defaults };
|
|
19
|
+
}
|
|
20
|
+
const prefs = loadPrefs();
|
|
21
|
+
const state = {
|
|
22
|
+
active: false,
|
|
23
|
+
hoveredElement: null,
|
|
24
|
+
selectedElement: null,
|
|
25
|
+
panelVisible: false,
|
|
26
|
+
shadowHost: null,
|
|
27
|
+
shadowRoot: null,
|
|
28
|
+
highlightColor: prefs.highlightColor,
|
|
29
|
+
theme: prefs.theme
|
|
30
|
+
};
|
|
31
|
+
const savedActive = prefs.active;
|
|
32
|
+
function savePrefs() {
|
|
33
|
+
try {
|
|
34
|
+
const data = {
|
|
35
|
+
active: state.active,
|
|
36
|
+
highlightColor: state.highlightColor,
|
|
37
|
+
theme: state.theme
|
|
38
|
+
};
|
|
39
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function resetState() {
|
|
44
|
+
state.active = false;
|
|
45
|
+
state.hoveredElement = null;
|
|
46
|
+
state.selectedElement = null;
|
|
47
|
+
state.panelVisible = false;
|
|
48
|
+
state.highlightColor = "#0063F7";
|
|
49
|
+
state.theme = "dark";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var cssText = "/* ============================================\n Agentation — Element Inspector Styles\n ============================================\n Edit these to customize the inspector's look.\n All styles are scoped inside Shadow DOM so they\n won't leak into the host page.\n ============================================ */\n\n/* --- Host container (don't remove) --- */\n\n@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap');\n\n:host {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n width: 0 !important;\n height: 0 !important;\n overflow: visible !important;\n z-index: 2147483647 !important;\n pointer-events: none !important;\n font-family: \"Geist\", sans-serif;\n}\n\n*, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* --- Highlight overlay (blue border on hover) --- */\n\n.ag-highlight {\n position: fixed;\n pointer-events: none;\n border: 2px solid #0063F7;\n background: #0063f717;\n border-radius: 2px;\n transition: top 0.15s cubic-bezier(0.4, 0, 0.2, 1), left 0.15s cubic-bezier(0.4, 0, 0.2, 1), width 0.15s cubic-bezier(0.4, 0, 0.2, 1), height 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: 2147483646;\n}\n\n/* --- Floating label (tag name badge) --- */\n\n.ag-label {\n position: fixed;\n pointer-events: none;\n background: #28293D;\n color: #f1f5f9;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n padding: 4px 8px;\n border-radius: 4px;\n white-space: nowrap;\n z-index: 2147483647;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n.ag-label .ag-tag {\n color: #57EBA1;\n font-weight: 600;\n}\n\n.ag-label .ag-class {\n color: #DDA5E9;\n}\n\n.ag-label .ag-dims {\n color: #94a3b8;\n margin-left: 6px;\n}\n\n/* --- Toolbar (bottom-right bar) --- */\n\n.ag-toolbar {\n position: fixed;\n bottom: 16px;\n right: 16px;\n display: flex;\n align-items: center;\n gap: 0;\n pointer-events: auto;\n z-index: 2147483647;\n background: #1a1a1a;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 10px;\n border: 0.5px solid rgba(255, 255, 255, 0.08);\n box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.75), 0 0.5px 0 0 rgba(255, 255, 255, 0.05) inset, 0 -0.5px 0 0 rgba(0, 0, 0, 0.05) inset, 0 0 0 0.5px rgba(255, 255, 255, 0.10) inset;\n padding: 2px;\n}\n\n.ag-toolbar:active {\n transform: scale(.99);\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.ag-toolbar.ag-light {\n background: #ffff;\n border-color: none;\n box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.06), 0px 1px 2px -1px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(0, 0, 0, 0.04);\n}\n\n.ag-toolbar-divider {\n width: 1px;\n height: 20px;\n background: rgba(255, 255, 255, 0.1);\n flex-shrink: 0;\n margin: 0 2px;\n}\n\n.ag-toolbar.ag-light .ag-toolbar-divider {\n background: rgba(0, 0, 0, 0.12);\n}\n\n/* --- Toggle button (inside toolbar) --- */\n\n.ag-toggle {\n height: 34px;\n border-radius: 8px;\n background: transparent;\n color: rgba(255, 255, 255, 0.5);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 8px;\n transition: all 0.15s ease;\n font-family: \"Geist\", sans-serif;\n font-size: 12px;\n font-weight: 500;\n white-space: nowrap;\n}\n\n.ag-toggle:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n\n.ag-toggle.active {\n color: rgba(255, 255, 255, 0.9);\n}\n\n.ag-toolbar.ag-light .ag-toggle {\n color: rgba(0, 0, 0, 0.45);\n}\n\n.ag-toolbar.ag-light .ag-toggle:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n.ag-toolbar.ag-light .ag-toggle.active {\n color: rgba(0, 0, 0, 0.85);\n}\n\n.ag-toggle-label {\n pointer-events: none;\n}\n\n.ag-toggle-track {\n width: 40px;\n height: 24px;\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.12);\n position: relative;\n transition: background 0.2s ease;\n pointer-events: none;\n flex-shrink: 0;\n}\n\n.ag-toggle.active .ag-toggle-track {\n background: #0063F7;\n}\n\n.ag-toolbar.ag-light .ag-toggle-track {\n background: rgba(0, 0, 0, 0.15);\n}\n\n.ag-toolbar.ag-light .ag-toggle.active .ag-toggle-track {\n background: #0063F7;\n}\n\n.ag-toggle-knob {\n position: absolute;\n top: 3px;\n left: 3px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 1);\n box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.04), 0 2px 2px 0 rgba(0, 0, 0, 0.02), 0 0.5px 0 0 rgba(255, 255, 255, 0.20) inset, 0 -0.5px 0 0 rgba(0, 0, 0, 0.10) inset, 0 0 0 0.5px rgba(0, 0, 0, 0.15) inset;\n transition: all 0.2s ease;\n}\n\n.ag-toggle.active .ag-toggle-knob {\n left: 19px;\n background: #fff;\n}\n\n/* --- Color picker --- */\n\n.ag-color-picker {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 34px;\n height: 34px;\n cursor: pointer;\n border-radius: 8px;\n transition: background 0.15s ease;\n position: relative;\n}\n\n.ag-color-picker:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n\n.ag-toolbar.ag-light .ag-color-picker:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n.ag-color-swatch {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 2px solid rgba(255, 255, 255, 0.2);\n pointer-events: none;\n}\n\n.ag-toolbar.ag-light .ag-color-swatch {\n border-color: rgba(0, 0, 0, 0.15);\n}\n\n.ag-color-picker input[type=\"color\"] {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0;\n cursor: pointer;\n top: 0;\n left: 0;\n}\n\n/* --- Theme toggle --- */\n\n@keyframes ag-icon-out {\n from { transform: scale(1); opacity: 1; filter: blur(0); }\n to { transform: scale(0.5); opacity: 0.5; filter: blur(2px); }\n}\n\n@keyframes ag-icon-in {\n from { transform: scale(0.5); opacity: 0.5; filter: blur(2px); }\n to { transform: scale(1); opacity: 1; filter: blur(0); }\n}\n\n.ag-theme-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 34px;\n height: 34px;\n background: transparent;\n border: none;\n color: rgba(255, 255, 255, 0.4);\n cursor: pointer;\n border-radius: 8px;\n transition: background 0.15s ease, color 0.15s ease;\n}\n\n.ag-theme-btn:hover {\n background: rgba(255, 255, 255, 0.06);\n color: rgba(255, 255, 255, 0.8);\n}\n\n.ag-toolbar.ag-light .ag-theme-btn {\n color: rgba(0, 0, 0, 0.35);\n}\n\n.ag-toolbar.ag-light .ag-theme-btn:hover {\n background: rgba(0, 0, 0, 0.05);\n color: rgba(0, 0, 0, 0.75);\n}\n\n.ag-theme-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.ag-theme-icon.ag-icon-out {\n animation: ag-icon-out 0.15s ease forwards;\n}\n\n.ag-theme-icon.ag-icon-in {\n animation: ag-icon-in 0.15s ease forwards;\n}\n\n/* --- Style panel (dark) --- */\n\n.ag-panel {\n position: fixed;\n pointer-events: auto;\n background: #000;\n color: #fff;\n border-radius: 12px;\n width: 340px;\n max-height: 480px;\n overflow-y: auto;\n overscroll-behavior: none;\n font-size: 12px;\n line-height: 1.5;\n box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.75), 0 4px 24px rgba(0, 0, 0, 0.5), 0 0.5px 0 0 rgba(255, 255, 255, 0.05) inset, 0 -0.5px 0 0 rgba(0, 0, 0, 0.05) inset, 0 0 0 0.5px rgba(255, 255, 255, 0.10) inset;\n z-index: 2147483647;\n\n /* enter/exit animation */\n opacity: 0;\n filter: blur(4px);\n transform: scale(0.96) translateY(4px);\n transition: opacity 0.18s ease, filter 0.18s ease, transform 0.18s ease;\n}\n\n.ag-panel.ag-panel-visible {\n opacity: 1;\n filter: blur(0);\n transform: scale(1) translateY(0);\n}\n\n.ag-panel.ag-panel-hiding {\n opacity: 0;\n filter: blur(4px);\n transform: scale(0.96) translateY(4px);\n}\n\n.ag-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 14px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n position: sticky;\n top: 0;\n background: #000;\n border-radius: 12px 12px 0 0;\n z-index: 1;\n}\n\n.ag-panel-title {\n font-weight: 600;\n color: #57EBA1;\n font-size: 14px;\n}\n\n.ag-panel-actions {\n display: flex;\n gap: 6px;\n}\n\n.ag-panel-btn {\n background: #1a1a1a;\n color: #888;\n border: 1px solid #2a2a2a;\n border-radius: 6px;\n padding: 4px 8px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n pointer-events: auto;\n transition: all 0.1s ease;\n}\n\n.ag-panel-btn:hover {\n background: #2a2a2a;\n color: #fff;\n border-color: #444;\n}\n\n.ag-panel-btn.copied {\n background: #065f46;\n color: #6ee7b7;\n border-color: #059669;\n}\n\n.ag-panel-close {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.5);\n cursor: pointer;\n padding-left: 12px;\n border-left: 1px solid #2a2a2a;\n display: flex;\n align-items: center;\n pointer-events: auto;\n transition: all 0.1s ease;\n}\n\n.ag-panel-close:hover {\n color: rgba(255, 255, 255, 1);\n}\n\n/* --- Selector display --- */\n\n.ag-selector {\n padding: 8px 14px;\n background: #28293D;\n font-family: \"Geist Mono\", monospace;\n font-size: 11px;\n color: #DDA5E9;\n word-break: break-all;\n}\n\n/* --- Style sections --- */\n\n.ag-section {\n padding: 10px 14px 0 14px;\n /* border-bottom: 1px solid rgba(255, 255, 255, 0.1); */\n}\n\n.ag-section:last-child {\n padding-bottom: 14px;\n border-bottom: none;\n}\n\n.ag-section-title {\n font-weight: 500;\n color: rgba(255, 255, 255, 0.5);\n text-transform: uppercase;\n font-size: 10px;\n letter-spacing: 0.5px;\n margin-bottom: 6px;\n}\n\n/* --- Code block (property list) --- */\n\n.ag-code {\n background: #1a1a1a;\n border-radius: 8px;\n padding: 10px 14px;\n font-family: \"Geist Mono\", monospace;\n font-size: 12px;\n line-height: 1.7;\n}\n\n.ag-code-line {\n white-space: normal;\n word-break: break-all;\n}\n\n.ag-prop-name {\n color: rgba(255, 255, 255, 0.85);\n}\n\n.ag-prop-value {\n color: #DDA5E9;\n}\n\n/* --- Scrollbar --- */\n\n.ag-panel::-webkit-scrollbar {\n display: none;\n}\n\n/* ============================================\n Light theme overrides\n ============================================ */\n\n.ag-panel.ag-light {\n background: #fff;\n color: #1a1a1a;\n box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.1), 0 4px 24px rgba(0, 0, 0, 0.12);\n}\n\n.ag-panel.ag-light .ag-panel-header {\n background: #fff;\n border-bottom-color: rgba(0, 0, 0, 0.08);\n}\n\n.ag-panel.ag-light .ag-panel-title {\n color: #0d7c4a;\n}\n\n.ag-panel.ag-light .ag-panel-btn {\n background: #f0f0f0;\n color: #666;\n border-color: #e0e0e0;\n}\n\n.ag-panel.ag-light .ag-panel-btn:hover {\n background: #e0e0e0;\n color: #111;\n border-color: #ccc;\n}\n\n.ag-panel.ag-light .ag-panel-close {\n color: rgba(0, 0, 0, 0.4);\n border-left-color: #e0e0e0;\n}\n\n.ag-panel.ag-light .ag-panel-close:hover {\n color: rgba(0, 0, 0, 0.85);\n}\n\n.ag-panel.ag-light .ag-selector {\n background: #f0f0f5;\n color: #8b5cf6;\n}\n\n.ag-panel.ag-light .ag-section {\n border-bottom-color: rgba(0, 0, 0, 0.06);\n}\n\n.ag-panel.ag-light .ag-section-title {\n color: rgba(0, 0, 0, 0.45);\n}\n\n.ag-panel.ag-light .ag-code {\n background: #f5f5f5;\n}\n\n.ag-panel.ag-light .ag-prop-name {\n color: rgba(0, 0, 0, 0.75);\n}\n\n.ag-panel.ag-light .ag-prop-value {\n color: #7c3aed;\n}\n";
|
|
53
|
+
|
|
54
|
+
const STYLES = cssText;
|
|
55
|
+
|
|
56
|
+
const HOST_ID = "agentation-inspector";
|
|
57
|
+
function createShadowHost() {
|
|
58
|
+
const existing = document.getElementById(HOST_ID);
|
|
59
|
+
if (existing) existing.remove();
|
|
60
|
+
const host = document.createElement("div");
|
|
61
|
+
host.id = HOST_ID;
|
|
62
|
+
host.setAttribute("data-agentation", "true");
|
|
63
|
+
host.style.cssText = "position:fixed!important;top:0!important;left:0!important;width:0!important;height:0!important;overflow:visible!important;z-index:2147483647!important;pointer-events:none!important;";
|
|
64
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
65
|
+
const style = document.createElement("style");
|
|
66
|
+
style.textContent = STYLES;
|
|
67
|
+
shadow.appendChild(style);
|
|
68
|
+
document.body.appendChild(host);
|
|
69
|
+
state.shadowHost = host;
|
|
70
|
+
state.shadowRoot = shadow;
|
|
71
|
+
return shadow;
|
|
72
|
+
}
|
|
73
|
+
function destroyShadowHost() {
|
|
74
|
+
if (state.shadowHost) {
|
|
75
|
+
state.shadowHost.remove();
|
|
76
|
+
state.shadowHost = null;
|
|
77
|
+
state.shadowRoot = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ICONS = {
|
|
82
|
+
close: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`,
|
|
83
|
+
copy: `<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="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,
|
|
84
|
+
check: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`,
|
|
85
|
+
sun: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>`,
|
|
86
|
+
moon: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>`
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const SKIP_TAGS = /* @__PURE__ */ new Set([
|
|
90
|
+
"SCRIPT",
|
|
91
|
+
"STYLE",
|
|
92
|
+
"LINK",
|
|
93
|
+
"META",
|
|
94
|
+
"HEAD",
|
|
95
|
+
"TITLE",
|
|
96
|
+
"BR",
|
|
97
|
+
"WBR",
|
|
98
|
+
"NOSCRIPT",
|
|
99
|
+
"TEMPLATE"
|
|
100
|
+
]);
|
|
101
|
+
function isInteractable(el) {
|
|
102
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
103
|
+
if (SKIP_TAGS.has(el.tagName)) return false;
|
|
104
|
+
const rect = el.getBoundingClientRect();
|
|
105
|
+
if (rect.width === 0 && rect.height === 0) return false;
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
function isOwnElement(el) {
|
|
109
|
+
if (!state.shadowHost) return false;
|
|
110
|
+
return state.shadowHost === el || state.shadowHost.contains(el);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let highlightEl = null;
|
|
114
|
+
function ensureHighlight() {
|
|
115
|
+
if (highlightEl && state.shadowRoot?.contains(highlightEl)) {
|
|
116
|
+
return highlightEl;
|
|
117
|
+
}
|
|
118
|
+
highlightEl = document.createElement("div");
|
|
119
|
+
highlightEl.className = "ag-highlight";
|
|
120
|
+
state.shadowRoot?.appendChild(highlightEl);
|
|
121
|
+
return highlightEl;
|
|
122
|
+
}
|
|
123
|
+
function showHighlight(target) {
|
|
124
|
+
const el = ensureHighlight();
|
|
125
|
+
const rect = target.getBoundingClientRect();
|
|
126
|
+
el.style.top = `${rect.top}px`;
|
|
127
|
+
el.style.left = `${rect.left}px`;
|
|
128
|
+
el.style.width = `${rect.width}px`;
|
|
129
|
+
el.style.height = `${rect.height}px`;
|
|
130
|
+
el.style.borderColor = state.highlightColor;
|
|
131
|
+
el.style.background = state.highlightColor + "17";
|
|
132
|
+
el.style.display = "block";
|
|
133
|
+
}
|
|
134
|
+
function hideHighlight() {
|
|
135
|
+
if (highlightEl) {
|
|
136
|
+
highlightEl.style.display = "none";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function destroyHighlight() {
|
|
140
|
+
if (highlightEl) {
|
|
141
|
+
highlightEl.remove();
|
|
142
|
+
highlightEl = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function formatDimensions(width, height) {
|
|
147
|
+
return `${Math.round(width)} \xD7 ${Math.round(height)}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let labelEl = null;
|
|
151
|
+
function ensureLabel() {
|
|
152
|
+
if (labelEl && state.shadowRoot?.contains(labelEl)) {
|
|
153
|
+
return labelEl;
|
|
154
|
+
}
|
|
155
|
+
labelEl = document.createElement("div");
|
|
156
|
+
labelEl.className = "ag-label";
|
|
157
|
+
state.shadowRoot?.appendChild(labelEl);
|
|
158
|
+
return labelEl;
|
|
159
|
+
}
|
|
160
|
+
function showLabel(target) {
|
|
161
|
+
const el = ensureLabel();
|
|
162
|
+
const rect = target.getBoundingClientRect();
|
|
163
|
+
const tag = target.tagName.toLowerCase();
|
|
164
|
+
const classes = Array.from(target.classList).slice(0, 3).map((c) => `.${c}`).join("");
|
|
165
|
+
const dims = formatDimensions(rect.width, rect.height);
|
|
166
|
+
el.innerHTML = `<span class="ag-tag">${tag}</span>` + (classes ? `<span class="ag-class">${classes}</span>` : "") + `<span class="ag-dims">${dims}</span>`;
|
|
167
|
+
const labelHeight = 24;
|
|
168
|
+
const gap = 6;
|
|
169
|
+
let top = rect.top - labelHeight - gap;
|
|
170
|
+
if (top < 4) {
|
|
171
|
+
top = rect.bottom + gap;
|
|
172
|
+
}
|
|
173
|
+
let left = rect.left;
|
|
174
|
+
el.style.display = "block";
|
|
175
|
+
el.style.top = `${top}px`;
|
|
176
|
+
el.style.left = `${Math.max(4, left)}px`;
|
|
177
|
+
requestAnimationFrame(() => {
|
|
178
|
+
const labelRect = el.getBoundingClientRect();
|
|
179
|
+
if (labelRect.right > window.innerWidth - 4) {
|
|
180
|
+
el.style.left = `${window.innerWidth - labelRect.width - 4}px`;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function hideLabel() {
|
|
185
|
+
if (labelEl) {
|
|
186
|
+
labelEl.style.display = "none";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function destroyLabel() {
|
|
190
|
+
if (labelEl) {
|
|
191
|
+
labelEl.remove();
|
|
192
|
+
labelEl = null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const FRAMEWORK_CLASS_PATTERNS = [
|
|
197
|
+
/^v-/,
|
|
198
|
+
/^ng-/,
|
|
199
|
+
/^_ng/,
|
|
200
|
+
/^svelte-/,
|
|
201
|
+
/^css-/,
|
|
202
|
+
/^jsx-/,
|
|
203
|
+
/^sc-/,
|
|
204
|
+
/^__/,
|
|
205
|
+
/^css-\d/,
|
|
206
|
+
/^rs-/,
|
|
207
|
+
/^emotion-/,
|
|
208
|
+
/^[a-zA-Z]{1,3}[A-Z][a-zA-Z0-9]{4,8}$/
|
|
209
|
+
// CSS-module hashes like "aBcDeFgH"
|
|
210
|
+
];
|
|
211
|
+
function isFrameworkClass(cls) {
|
|
212
|
+
return FRAMEWORK_CLASS_PATTERNS.some((p) => p.test(cls));
|
|
213
|
+
}
|
|
214
|
+
function escapeSelector(str) {
|
|
215
|
+
if (typeof CSS !== "undefined" && CSS.escape) {
|
|
216
|
+
return CSS.escape(str);
|
|
217
|
+
}
|
|
218
|
+
return str.replace(/([\0-\x1f\x7f]|^-?\d|^-$|[^\x80-\uFFFF\w-])/g, (ch) => {
|
|
219
|
+
if (ch === "\0") return "\uFFFD";
|
|
220
|
+
return "\\" + ch;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function getNthChildIndex(el) {
|
|
224
|
+
let index = 1;
|
|
225
|
+
let sibling = el.previousElementSibling;
|
|
226
|
+
while (sibling) {
|
|
227
|
+
if (sibling.tagName === el.tagName) index++;
|
|
228
|
+
sibling = sibling.previousElementSibling;
|
|
229
|
+
}
|
|
230
|
+
return index;
|
|
231
|
+
}
|
|
232
|
+
function getSiblingCount(el) {
|
|
233
|
+
let count = 0;
|
|
234
|
+
const parent = el.parentElement;
|
|
235
|
+
if (!parent) return 0;
|
|
236
|
+
for (let i = 0; i < parent.children.length; i++) {
|
|
237
|
+
if (parent.children[i].tagName === el.tagName) count++;
|
|
238
|
+
}
|
|
239
|
+
return count;
|
|
240
|
+
}
|
|
241
|
+
function buildSegment(el) {
|
|
242
|
+
const tag = el.tagName.toLowerCase();
|
|
243
|
+
if (el.id && !isFrameworkClass(el.id)) {
|
|
244
|
+
return `#${escapeSelector(el.id)}`;
|
|
245
|
+
}
|
|
246
|
+
const classes = Array.from(el.classList).filter((c) => !isFrameworkClass(c));
|
|
247
|
+
if (classes.length > 0) {
|
|
248
|
+
const classSelector = tag + classes.map((c) => `.${escapeSelector(c)}`).join("");
|
|
249
|
+
const parent = el.parentElement;
|
|
250
|
+
if (parent) {
|
|
251
|
+
const matches = parent.querySelectorAll(`:scope > ${classSelector}`);
|
|
252
|
+
if (matches.length === 1) return classSelector;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (getSiblingCount(el) > 1) {
|
|
256
|
+
return `${tag}:nth-of-type(${getNthChildIndex(el)})`;
|
|
257
|
+
}
|
|
258
|
+
return tag;
|
|
259
|
+
}
|
|
260
|
+
function generateSelector(el) {
|
|
261
|
+
if (el.id && !isFrameworkClass(el.id)) {
|
|
262
|
+
const id = `#${escapeSelector(el.id)}`;
|
|
263
|
+
if (document.querySelectorAll(id).length === 1) return id;
|
|
264
|
+
}
|
|
265
|
+
const parts = [];
|
|
266
|
+
let current = el;
|
|
267
|
+
while (current && current !== document.documentElement && current !== document.body) {
|
|
268
|
+
const segment = buildSegment(current);
|
|
269
|
+
parts.unshift(segment);
|
|
270
|
+
if (segment.startsWith("#")) break;
|
|
271
|
+
const candidate = parts.join(" > ");
|
|
272
|
+
try {
|
|
273
|
+
if (document.querySelectorAll(candidate).length === 1) break;
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
current = current.parentElement;
|
|
277
|
+
}
|
|
278
|
+
const selector = parts.join(" > ");
|
|
279
|
+
try {
|
|
280
|
+
if (document.querySelectorAll(selector).length !== 1) {
|
|
281
|
+
return "body > " + selector;
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
return selector;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const CATEGORIES = [
|
|
289
|
+
{
|
|
290
|
+
name: "Typography",
|
|
291
|
+
props: [
|
|
292
|
+
"font-family",
|
|
293
|
+
"font-size",
|
|
294
|
+
"font-weight",
|
|
295
|
+
"font-style",
|
|
296
|
+
"line-height",
|
|
297
|
+
"letter-spacing",
|
|
298
|
+
"text-align",
|
|
299
|
+
"text-decoration",
|
|
300
|
+
"text-transform",
|
|
301
|
+
"color"
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "Box Model",
|
|
306
|
+
props: [
|
|
307
|
+
"width",
|
|
308
|
+
"height",
|
|
309
|
+
"padding",
|
|
310
|
+
"margin",
|
|
311
|
+
"border",
|
|
312
|
+
"border-radius"
|
|
313
|
+
]
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "Background",
|
|
317
|
+
props: [
|
|
318
|
+
"background-color",
|
|
319
|
+
"background-image",
|
|
320
|
+
"background-size",
|
|
321
|
+
"background-position",
|
|
322
|
+
"opacity"
|
|
323
|
+
]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: "Layout",
|
|
327
|
+
props: [
|
|
328
|
+
"display",
|
|
329
|
+
"position",
|
|
330
|
+
"top",
|
|
331
|
+
"right",
|
|
332
|
+
"bottom",
|
|
333
|
+
"left",
|
|
334
|
+
"z-index",
|
|
335
|
+
"overflow",
|
|
336
|
+
"float",
|
|
337
|
+
"clear"
|
|
338
|
+
]
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "Flexbox / Grid",
|
|
342
|
+
props: [
|
|
343
|
+
"flex-direction",
|
|
344
|
+
"flex-wrap",
|
|
345
|
+
"justify-content",
|
|
346
|
+
"align-items",
|
|
347
|
+
"align-content",
|
|
348
|
+
"gap",
|
|
349
|
+
"grid-template-columns",
|
|
350
|
+
"grid-template-rows",
|
|
351
|
+
"grid-column",
|
|
352
|
+
"grid-row"
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
];
|
|
356
|
+
function isDefaultValue(prop, value) {
|
|
357
|
+
const defaults = {
|
|
358
|
+
"font-style": /* @__PURE__ */ new Set(["normal"]),
|
|
359
|
+
"letter-spacing": /* @__PURE__ */ new Set(["normal", "0px"]),
|
|
360
|
+
"text-decoration": /* @__PURE__ */ new Set(["none", "none solid rgb(0, 0, 0)"]),
|
|
361
|
+
"text-transform": /* @__PURE__ */ new Set(["none"]),
|
|
362
|
+
"background-image": /* @__PURE__ */ new Set(["none"]),
|
|
363
|
+
"background-size": /* @__PURE__ */ new Set(["auto"]),
|
|
364
|
+
"background-position": /* @__PURE__ */ new Set(["0% 0%"]),
|
|
365
|
+
"opacity": /* @__PURE__ */ new Set(["1"]),
|
|
366
|
+
"top": /* @__PURE__ */ new Set(["auto"]),
|
|
367
|
+
"right": /* @__PURE__ */ new Set(["auto"]),
|
|
368
|
+
"bottom": /* @__PURE__ */ new Set(["auto"]),
|
|
369
|
+
"left": /* @__PURE__ */ new Set(["auto"]),
|
|
370
|
+
"z-index": /* @__PURE__ */ new Set(["auto"]),
|
|
371
|
+
"overflow": /* @__PURE__ */ new Set(["visible"]),
|
|
372
|
+
"float": /* @__PURE__ */ new Set(["none"]),
|
|
373
|
+
"clear": /* @__PURE__ */ new Set(["none"]),
|
|
374
|
+
"flex-direction": /* @__PURE__ */ new Set(["row"]),
|
|
375
|
+
"flex-wrap": /* @__PURE__ */ new Set(["nowrap"]),
|
|
376
|
+
"grid-template-columns": /* @__PURE__ */ new Set(["none"]),
|
|
377
|
+
"grid-template-rows": /* @__PURE__ */ new Set(["none"]),
|
|
378
|
+
"grid-column": /* @__PURE__ */ new Set(["auto", "auto / auto"]),
|
|
379
|
+
"grid-row": /* @__PURE__ */ new Set(["auto", "auto / auto"])
|
|
380
|
+
};
|
|
381
|
+
return defaults[prop]?.has(value) ?? false;
|
|
382
|
+
}
|
|
383
|
+
function readShorthand(cs, prop) {
|
|
384
|
+
if (prop === "padding") {
|
|
385
|
+
const t = cs.paddingTop, r = cs.paddingRight, b = cs.paddingBottom, l = cs.paddingLeft;
|
|
386
|
+
if (t === r && r === b && b === l) return t;
|
|
387
|
+
if (t === b && r === l) return `${t} ${r}`;
|
|
388
|
+
return `${t} ${r} ${b} ${l}`;
|
|
389
|
+
}
|
|
390
|
+
if (prop === "margin") {
|
|
391
|
+
const t = cs.marginTop, r = cs.marginRight, b = cs.marginBottom, l = cs.marginLeft;
|
|
392
|
+
if (t === r && r === b && b === l) return t;
|
|
393
|
+
if (t === b && r === l) return `${t} ${r}`;
|
|
394
|
+
return `${t} ${r} ${b} ${l}`;
|
|
395
|
+
}
|
|
396
|
+
if (prop === "border") {
|
|
397
|
+
return `${cs.borderTopWidth} ${cs.borderTopStyle} ${cs.borderTopColor}`;
|
|
398
|
+
}
|
|
399
|
+
if (prop === "border-radius") {
|
|
400
|
+
const tl = cs.borderTopLeftRadius, tr = cs.borderTopRightRadius;
|
|
401
|
+
const br = cs.borderBottomRightRadius, bl = cs.borderBottomLeftRadius;
|
|
402
|
+
if (tl === tr && tr === br && br === bl) return tl;
|
|
403
|
+
return `${tl} ${tr} ${br} ${bl}`;
|
|
404
|
+
}
|
|
405
|
+
return cs.getPropertyValue(prop);
|
|
406
|
+
}
|
|
407
|
+
function extractStyles(el) {
|
|
408
|
+
const cs = getComputedStyle(el);
|
|
409
|
+
const result = [];
|
|
410
|
+
for (const cat of CATEGORIES) {
|
|
411
|
+
const properties = [];
|
|
412
|
+
for (const prop of cat.props) {
|
|
413
|
+
const value = readShorthand(cs, prop);
|
|
414
|
+
if (value && !isDefaultValue(prop, value)) {
|
|
415
|
+
properties.push({ name: prop, value });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (properties.length > 0) {
|
|
419
|
+
result.push({ name: cat.name, properties });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
function formatStylesForClipboard(categories) {
|
|
425
|
+
const lines = [];
|
|
426
|
+
for (const cat of categories) {
|
|
427
|
+
lines.push(`/* ${cat.name} */`);
|
|
428
|
+
for (const p of cat.properties) {
|
|
429
|
+
lines.push(`${p.name}: ${p.value};`);
|
|
430
|
+
}
|
|
431
|
+
lines.push("");
|
|
432
|
+
}
|
|
433
|
+
return lines.join("\n").trim();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function copyToClipboard(text) {
|
|
437
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
438
|
+
try {
|
|
439
|
+
await navigator.clipboard.writeText(text);
|
|
440
|
+
return true;
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const textarea = document.createElement("textarea");
|
|
445
|
+
textarea.value = text;
|
|
446
|
+
textarea.style.position = "fixed";
|
|
447
|
+
textarea.style.left = "-9999px";
|
|
448
|
+
textarea.style.top = "-9999px";
|
|
449
|
+
textarea.style.opacity = "0";
|
|
450
|
+
document.body.appendChild(textarea);
|
|
451
|
+
textarea.focus();
|
|
452
|
+
textarea.select();
|
|
453
|
+
try {
|
|
454
|
+
const ok = document.execCommand("copy");
|
|
455
|
+
return ok;
|
|
456
|
+
} catch {
|
|
457
|
+
return false;
|
|
458
|
+
} finally {
|
|
459
|
+
document.body.removeChild(textarea);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let panelEl = null;
|
|
464
|
+
let pageClickX = 0;
|
|
465
|
+
let pageClickY = 0;
|
|
466
|
+
function createPanelHTML(el, selector) {
|
|
467
|
+
const categories = extractStyles(el);
|
|
468
|
+
const tag = el.tagName.toLowerCase();
|
|
469
|
+
let sectionsHTML = "";
|
|
470
|
+
for (const cat of categories) {
|
|
471
|
+
let linesHTML = "";
|
|
472
|
+
for (const p of cat.properties) {
|
|
473
|
+
linesHTML += `<div class="ag-code-line"><span class="ag-prop-name">${p.name}</span>: <span class="ag-prop-value">${escapeHTML(p.value)}</span>;</div>`;
|
|
474
|
+
}
|
|
475
|
+
sectionsHTML += `<div class="ag-section"><div class="ag-section-title">${cat.name}</div><div class="ag-code">${linesHTML}</div></div>`;
|
|
476
|
+
}
|
|
477
|
+
return `
|
|
478
|
+
<div class="ag-panel-header">
|
|
479
|
+
<span class="ag-panel-title"><${tag}></span>
|
|
480
|
+
<div class="ag-panel-actions">
|
|
481
|
+
<button class="ag-panel-btn" data-action="copy-selector" title="Copy selector">
|
|
482
|
+
${ICONS.copy} Selector
|
|
483
|
+
</button>
|
|
484
|
+
<button class="ag-panel-btn" data-action="copy-styles" title="Copy all styles">
|
|
485
|
+
${ICONS.copy} Styles
|
|
486
|
+
</button>
|
|
487
|
+
<button class="ag-panel-close" data-action="close" title="Close panel">
|
|
488
|
+
${ICONS.close}
|
|
489
|
+
</button>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
<div class="ag-selector">${escapeHTML(selector)}</div>
|
|
493
|
+
${sectionsHTML}
|
|
494
|
+
`;
|
|
495
|
+
}
|
|
496
|
+
function escapeHTML(str) {
|
|
497
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
498
|
+
}
|
|
499
|
+
function positionPanel() {
|
|
500
|
+
if (!panelEl) return;
|
|
501
|
+
const panelWidth = 340;
|
|
502
|
+
const gap = 12;
|
|
503
|
+
const viewX = pageClickX - window.scrollX;
|
|
504
|
+
const viewY = pageClickY - window.scrollY;
|
|
505
|
+
let left = viewX + gap;
|
|
506
|
+
if (left + panelWidth > window.innerWidth - 8) {
|
|
507
|
+
left = viewX - panelWidth - gap;
|
|
508
|
+
if (left < 8) {
|
|
509
|
+
left = window.innerWidth - panelWidth - 8;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
let top = viewY - 20;
|
|
513
|
+
if (top + 480 > window.innerHeight - 8) {
|
|
514
|
+
top = Math.max(8, window.innerHeight - 488);
|
|
515
|
+
}
|
|
516
|
+
if (top < 8) top = 8;
|
|
517
|
+
panelEl.style.top = `${top}px`;
|
|
518
|
+
panelEl.style.left = `${left}px`;
|
|
519
|
+
}
|
|
520
|
+
function handlePanelClick(e) {
|
|
521
|
+
const target = e.target;
|
|
522
|
+
const btn = target.closest("[data-action]");
|
|
523
|
+
if (!btn) return;
|
|
524
|
+
const action = btn.dataset.action;
|
|
525
|
+
if (action === "close") {
|
|
526
|
+
hidePanel();
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (action === "copy-selector" && state.selectedElement) {
|
|
530
|
+
const selector = generateSelector(state.selectedElement);
|
|
531
|
+
copyToClipboard(selector).then(() => flashCopied(btn));
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (action === "copy-styles" && state.selectedElement) {
|
|
535
|
+
const categories = extractStyles(state.selectedElement);
|
|
536
|
+
const text = formatStylesForClipboard(categories);
|
|
537
|
+
copyToClipboard(text).then(() => flashCopied(btn));
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function flashCopied(btn) {
|
|
542
|
+
const original = btn.innerHTML;
|
|
543
|
+
btn.innerHTML = `${ICONS.check} Copied!`;
|
|
544
|
+
btn.classList.add("copied");
|
|
545
|
+
setTimeout(() => {
|
|
546
|
+
btn.innerHTML = original;
|
|
547
|
+
btn.classList.remove("copied");
|
|
548
|
+
}, 1500);
|
|
549
|
+
}
|
|
550
|
+
function showPanel(target, cursorX, cursorY) {
|
|
551
|
+
hidePanel(true);
|
|
552
|
+
state.selectedElement = target;
|
|
553
|
+
state.panelVisible = true;
|
|
554
|
+
pageClickX = cursorX + window.scrollX;
|
|
555
|
+
pageClickY = cursorY + window.scrollY;
|
|
556
|
+
const selector = generateSelector(target);
|
|
557
|
+
panelEl = document.createElement("div");
|
|
558
|
+
panelEl.className = "ag-panel" + (state.theme === "light" ? " ag-light" : "");
|
|
559
|
+
panelEl.innerHTML = createPanelHTML(target, selector);
|
|
560
|
+
panelEl.addEventListener("click", handlePanelClick);
|
|
561
|
+
state.shadowRoot?.appendChild(panelEl);
|
|
562
|
+
positionPanel();
|
|
563
|
+
requestAnimationFrame(() => {
|
|
564
|
+
panelEl?.classList.add("ag-panel-visible");
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
function hidePanel(immediate = false) {
|
|
568
|
+
if (!panelEl) return;
|
|
569
|
+
if (immediate) {
|
|
570
|
+
panelEl.removeEventListener("click", handlePanelClick);
|
|
571
|
+
panelEl.remove();
|
|
572
|
+
panelEl = null;
|
|
573
|
+
state.selectedElement = null;
|
|
574
|
+
state.panelVisible = false;
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const el = panelEl;
|
|
578
|
+
el.classList.remove("ag-panel-visible");
|
|
579
|
+
el.classList.add("ag-panel-hiding");
|
|
580
|
+
const onDone = () => {
|
|
581
|
+
el.removeEventListener("transitionend", onDone);
|
|
582
|
+
el.removeEventListener("click", handlePanelClick);
|
|
583
|
+
el.remove();
|
|
584
|
+
if (panelEl === el) panelEl = null;
|
|
585
|
+
};
|
|
586
|
+
el.addEventListener("transitionend", onDone, { once: true });
|
|
587
|
+
setTimeout(onDone, 250);
|
|
588
|
+
state.selectedElement = null;
|
|
589
|
+
state.panelVisible = false;
|
|
590
|
+
}
|
|
591
|
+
function repositionPanel() {
|
|
592
|
+
if (panelEl && state.panelVisible) {
|
|
593
|
+
positionPanel();
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function destroyPanel() {
|
|
597
|
+
hidePanel(true);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function onMouseMove(e) {
|
|
601
|
+
if (!state.active) return;
|
|
602
|
+
const target = e.target;
|
|
603
|
+
if (!target || isOwnElement(target) || !isInteractable(target)) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const el = target;
|
|
607
|
+
if (state.hoveredElement === el) return;
|
|
608
|
+
state.hoveredElement = el;
|
|
609
|
+
showHighlight(el);
|
|
610
|
+
showLabel(el);
|
|
611
|
+
}
|
|
612
|
+
function onClick(e) {
|
|
613
|
+
if (!state.active) return;
|
|
614
|
+
const target = e.target;
|
|
615
|
+
if (!target || isOwnElement(target)) return;
|
|
616
|
+
if (!isInteractable(target)) return;
|
|
617
|
+
e.preventDefault();
|
|
618
|
+
e.stopPropagation();
|
|
619
|
+
const el = target;
|
|
620
|
+
if (state.panelVisible && state.selectedElement === el) {
|
|
621
|
+
hidePanel();
|
|
622
|
+
} else {
|
|
623
|
+
showPanel(el, e.clientX, e.clientY);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function bindMouseEvents() {
|
|
627
|
+
document.addEventListener("mousemove", onMouseMove, true);
|
|
628
|
+
document.addEventListener("click", onClick, true);
|
|
629
|
+
}
|
|
630
|
+
function unbindMouseEvents() {
|
|
631
|
+
document.removeEventListener("mousemove", onMouseMove, true);
|
|
632
|
+
document.removeEventListener("click", onClick, true);
|
|
633
|
+
hideHighlight();
|
|
634
|
+
hideLabel();
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
let toggleCallback = null;
|
|
638
|
+
function onKeyDown(e) {
|
|
639
|
+
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "A") {
|
|
640
|
+
e.preventDefault();
|
|
641
|
+
e.stopPropagation();
|
|
642
|
+
toggleCallback?.();
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
if (e.key === "Escape" && state.active) {
|
|
646
|
+
e.preventDefault();
|
|
647
|
+
if (state.panelVisible) {
|
|
648
|
+
hidePanel();
|
|
649
|
+
} else {
|
|
650
|
+
toggleCallback?.();
|
|
651
|
+
}
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function bindKeyboardEvents(onToggle) {
|
|
656
|
+
toggleCallback = onToggle;
|
|
657
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
658
|
+
}
|
|
659
|
+
function unbindKeyboardEvents() {
|
|
660
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
661
|
+
toggleCallback = null;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
let rafId = null;
|
|
665
|
+
function update() {
|
|
666
|
+
rafId = null;
|
|
667
|
+
if (!state.active) return;
|
|
668
|
+
if (state.hoveredElement) {
|
|
669
|
+
showHighlight(state.hoveredElement);
|
|
670
|
+
showLabel(state.hoveredElement);
|
|
671
|
+
}
|
|
672
|
+
if (state.panelVisible) {
|
|
673
|
+
repositionPanel();
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
function onScrollOrResize() {
|
|
677
|
+
if (rafId !== null) return;
|
|
678
|
+
rafId = requestAnimationFrame(update);
|
|
679
|
+
}
|
|
680
|
+
function bindScrollResize() {
|
|
681
|
+
window.addEventListener("scroll", onScrollOrResize, true);
|
|
682
|
+
window.addEventListener("resize", onScrollOrResize, true);
|
|
683
|
+
}
|
|
684
|
+
function unbindScrollResize() {
|
|
685
|
+
window.removeEventListener("scroll", onScrollOrResize, true);
|
|
686
|
+
window.removeEventListener("resize", onScrollOrResize, true);
|
|
687
|
+
if (rafId !== null) {
|
|
688
|
+
cancelAnimationFrame(rafId);
|
|
689
|
+
rafId = null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
let toolbar = null;
|
|
694
|
+
let themeBtn = null;
|
|
695
|
+
class Inspector {
|
|
696
|
+
constructor() {
|
|
697
|
+
this.initialized = false;
|
|
698
|
+
}
|
|
699
|
+
init() {
|
|
700
|
+
if (this.initialized) return;
|
|
701
|
+
this.initialized = true;
|
|
702
|
+
const shadow = createShadowHost();
|
|
703
|
+
this.createToolbar(shadow);
|
|
704
|
+
bindKeyboardEvents(() => this.toggle());
|
|
705
|
+
bindScrollResize();
|
|
706
|
+
if (state.theme === "light") {
|
|
707
|
+
toolbar?.classList.add("ag-light");
|
|
708
|
+
}
|
|
709
|
+
if (savedActive) {
|
|
710
|
+
this.activate();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
createToolbar(shadow) {
|
|
714
|
+
toolbar = document.createElement("div");
|
|
715
|
+
toolbar.className = "ag-toolbar";
|
|
716
|
+
const toggleBtn = document.createElement("button");
|
|
717
|
+
toggleBtn.className = "ag-toggle";
|
|
718
|
+
toggleBtn.innerHTML = `<span class="ag-toggle-label">Inspect</span><span class="ag-toggle-track"><span class="ag-toggle-knob"></span></span>`;
|
|
719
|
+
toggleBtn.title = "Toggle Element Inspector (Ctrl+Shift+A)";
|
|
720
|
+
toggleBtn.addEventListener("click", (e) => {
|
|
721
|
+
e.stopPropagation();
|
|
722
|
+
this.toggle();
|
|
723
|
+
});
|
|
724
|
+
const div1 = document.createElement("span");
|
|
725
|
+
div1.className = "ag-toolbar-divider";
|
|
726
|
+
const colorWrap = document.createElement("label");
|
|
727
|
+
colorWrap.className = "ag-color-picker";
|
|
728
|
+
colorWrap.title = "Highlight color";
|
|
729
|
+
const colorSwatch = document.createElement("span");
|
|
730
|
+
colorSwatch.className = "ag-color-swatch";
|
|
731
|
+
colorSwatch.style.background = state.highlightColor;
|
|
732
|
+
const colorInput = document.createElement("input");
|
|
733
|
+
colorInput.type = "color";
|
|
734
|
+
colorInput.value = state.highlightColor;
|
|
735
|
+
colorInput.addEventListener("input", (e) => {
|
|
736
|
+
e.stopPropagation();
|
|
737
|
+
const val = e.target.value;
|
|
738
|
+
state.highlightColor = val;
|
|
739
|
+
colorSwatch.style.background = val;
|
|
740
|
+
if (state.hoveredElement) {
|
|
741
|
+
showHighlight(state.hoveredElement);
|
|
742
|
+
}
|
|
743
|
+
savePrefs();
|
|
744
|
+
});
|
|
745
|
+
colorInput.addEventListener("click", (e) => e.stopPropagation());
|
|
746
|
+
colorWrap.appendChild(colorSwatch);
|
|
747
|
+
colorWrap.appendChild(colorInput);
|
|
748
|
+
const div2 = document.createElement("span");
|
|
749
|
+
div2.className = "ag-toolbar-divider";
|
|
750
|
+
themeBtn = document.createElement("button");
|
|
751
|
+
themeBtn.className = "ag-theme-btn";
|
|
752
|
+
themeBtn.title = "Toggle light/dark panel";
|
|
753
|
+
const initIcon = state.theme === "dark" ? ICONS.sun : ICONS.moon;
|
|
754
|
+
themeBtn.innerHTML = `<span class="ag-theme-icon">${initIcon}</span>`;
|
|
755
|
+
themeBtn.addEventListener("click", (e) => {
|
|
756
|
+
e.stopPropagation();
|
|
757
|
+
this.toggleTheme();
|
|
758
|
+
});
|
|
759
|
+
toolbar.appendChild(toggleBtn);
|
|
760
|
+
toolbar.appendChild(div1);
|
|
761
|
+
toolbar.appendChild(colorWrap);
|
|
762
|
+
toolbar.appendChild(div2);
|
|
763
|
+
toolbar.appendChild(themeBtn);
|
|
764
|
+
shadow.appendChild(toolbar);
|
|
765
|
+
}
|
|
766
|
+
toggleTheme() {
|
|
767
|
+
state.theme = state.theme === "dark" ? "light" : "dark";
|
|
768
|
+
if (themeBtn) {
|
|
769
|
+
const iconWrap = themeBtn.querySelector(".ag-theme-icon");
|
|
770
|
+
if (iconWrap) {
|
|
771
|
+
iconWrap.classList.add("ag-icon-out");
|
|
772
|
+
setTimeout(() => {
|
|
773
|
+
const newIcon = state.theme === "dark" ? ICONS.sun : ICONS.moon;
|
|
774
|
+
iconWrap.innerHTML = newIcon;
|
|
775
|
+
iconWrap.classList.remove("ag-icon-out");
|
|
776
|
+
iconWrap.classList.add("ag-icon-in");
|
|
777
|
+
iconWrap.addEventListener("animationend", () => {
|
|
778
|
+
iconWrap.classList.remove("ag-icon-in");
|
|
779
|
+
}, { once: true });
|
|
780
|
+
}, 150);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
state.shadowRoot?.querySelector(".ag-panel")?.classList.toggle("ag-light", state.theme === "light");
|
|
784
|
+
toolbar?.classList.toggle("ag-light", state.theme === "light");
|
|
785
|
+
savePrefs();
|
|
786
|
+
}
|
|
787
|
+
activate() {
|
|
788
|
+
if (state.active) return;
|
|
789
|
+
state.active = true;
|
|
790
|
+
toolbar?.querySelector(".ag-toggle")?.classList.add("active");
|
|
791
|
+
bindMouseEvents();
|
|
792
|
+
savePrefs();
|
|
793
|
+
}
|
|
794
|
+
deactivate() {
|
|
795
|
+
if (!state.active) return;
|
|
796
|
+
state.active = false;
|
|
797
|
+
toolbar?.querySelector(".ag-toggle")?.classList.remove("active");
|
|
798
|
+
unbindMouseEvents();
|
|
799
|
+
hideHighlight();
|
|
800
|
+
hideLabel();
|
|
801
|
+
hidePanel();
|
|
802
|
+
state.hoveredElement = null;
|
|
803
|
+
savePrefs();
|
|
804
|
+
}
|
|
805
|
+
toggle() {
|
|
806
|
+
if (state.active) {
|
|
807
|
+
this.deactivate();
|
|
808
|
+
} else {
|
|
809
|
+
this.activate();
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
destroy() {
|
|
813
|
+
this.deactivate();
|
|
814
|
+
unbindKeyboardEvents();
|
|
815
|
+
unbindScrollResize();
|
|
816
|
+
destroyHighlight();
|
|
817
|
+
destroyLabel();
|
|
818
|
+
destroyPanel();
|
|
819
|
+
destroyShadowHost();
|
|
820
|
+
resetState();
|
|
821
|
+
toolbar = null;
|
|
822
|
+
themeBtn = null;
|
|
823
|
+
this.initialized = false;
|
|
824
|
+
}
|
|
825
|
+
get isActive() {
|
|
826
|
+
return state.active;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
let instance = null;
|
|
831
|
+
function init() {
|
|
832
|
+
if (!instance) {
|
|
833
|
+
instance = new Inspector();
|
|
834
|
+
}
|
|
835
|
+
instance.init();
|
|
836
|
+
return instance;
|
|
837
|
+
}
|
|
838
|
+
function destroy() {
|
|
839
|
+
instance?.destroy();
|
|
840
|
+
instance = null;
|
|
841
|
+
}
|
|
842
|
+
function getInstance() {
|
|
843
|
+
return instance;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
exports.Inspector = Inspector;
|
|
847
|
+
exports.destroy = destroy;
|
|
848
|
+
exports.generateSelector = generateSelector;
|
|
849
|
+
exports.getInstance = getInstance;
|
|
850
|
+
exports.init = init;
|
|
851
|
+
//# sourceMappingURL=agentation.cjs.js.map
|