open-web-inspector
Version:
The open source web element inspector - AI-controllable DOM inspection with live CSS editing, hover highlighting, and developer tools integration
1,207 lines (1,036 loc) • 168 kB
JavaScript
(function() {
'use strict';
// Avoid conflicts if already loaded
if (window.OpenWebInspector) {
return;
}
class OpenWebInspector {
constructor() {
this.isAnalyzeMode = false;
this.currentHighlightedElement = null;
this.popup = null;
this.overlay = null;
this.originalCursor = null;
this.cssChanges = new Map(); // Track CSS changes: element -> {property: {original, current}}
this.currentElement = null; // Track current element being inspected
this.actualSelectedElement = null; // Track actual page element (not inspector UI)
this.init();
}
init() {
this.createStyles();
this.setupEventListeners();
this.setupExternalAPI();
this.setupKeyboardShortcuts();
this.setupDOMEvents();
this.checkURLParameters();
}
createStyles() {
// Create and inject CSS styles
const style = document.createElement('style');
style.id = 'open-web-inspector-styles';
style.textContent = `
.open-web-inspector-highlight {
position: relative !important;
outline: 2px solid #ff4444 !important;
outline-offset: 1px !important;
box-shadow: 0 0 0 1px rgba(255, 68, 68, 0.3) !important;
z-index: 9998 !important;
}
.open-web-inspector-popup {
position: fixed !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
width: 98vw !important;
max-width: 1200px !important;
max-height: 90vh !important;
background: white !important;
border: 1px solid #ddd !important;
border-radius: 10px !important;
box-shadow: 0 10px 40px rgba(0,0,0,0.3) !important;
z-index: 10001 !important;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
font-size: 15px !important;
overflow: hidden !important;
}
.open-web-inspector-popup-header {
background: #f8f9fa !important;
padding: 20px 25px !important;
border-bottom: 1px solid #ddd !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
}
.open-web-inspector-popup-title {
margin: 0 !important;
font-size: 20px !important;
font-weight: 600 !important;
color: #333 !important;
}
.open-web-inspector-popup-close {
background: none !important;
border: none !important;
font-size: 24px !important;
cursor: pointer !important;
color: #666 !important;
padding: 0 !important;
width: 32px !important;
height: 32px !important;
border-radius: 50% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
transition: background-color 0.2s !important;
}
.open-web-inspector-popup-close:hover {
background-color: #f0f0f0 !important;
}
.open-web-inspector-ai-snapshot-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
color: white !important;
padding: 8px 16px !important;
border-radius: 6px !important;
font-size: 13px !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
margin-right: 12px !important;
}
.open-web-inspector-ai-snapshot-btn:hover {
transform: translateY(-1px) !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.15) !important;
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%) !important;
}
.open-web-inspector-ai-snapshot-btn:active {
transform: translateY(0) !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
}
.open-web-inspector-ai-snapshot-btn.copied {
background: linear-gradient(135deg, #48bb78 0%, #38a169 100%) !important;
}
.open-web-inspector-popup-header-actions {
display: flex !important;
align-items: center !important;
gap: 8px !important;
}
.open-web-inspector-screenshot-btn {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%) !important;
border: none !important;
color: white !important;
padding: 8px 16px !important;
border-radius: 6px !important;
font-size: 13px !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
margin-right: 8px !important;
}
.open-web-inspector-screenshot-btn:hover {
transform: translateY(-1px) !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.15) !important;
background: linear-gradient(135deg, #ff5252 0%, #e53e3e 100%) !important;
}
.open-web-inspector-screenshot-btn:active {
transform: translateY(0) !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
}
.open-web-inspector-screenshot-btn.capturing {
background: linear-gradient(135deg, #ffa726 0%, #ff9800 100%) !important;
}
.open-web-inspector-screenshot-btn.copied {
background: linear-gradient(135deg, #48bb78 0%, #38a169 100%) !important;
}
.open-web-inspector-css-value-input {
background: transparent !important;
border: 1px solid transparent !important;
color: #e74c3c !important;
font-family: 'Courier New', monospace !important;
font-size: 11px !important;
padding: 2px 4px !important;
border-radius: 3px !important;
min-width: 60px !important;
max-width: 200px !important;
transition: all 0.2s ease !important;
}
.open-web-inspector-css-value-input:hover {
border-color: #3498db !important;
background: rgba(52, 152, 219, 0.1) !important;
}
.open-web-inspector-css-value-input:focus {
outline: none !important;
border-color: #3498db !important;
background: rgba(52, 152, 219, 0.15) !important;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2) !important;
}
.open-web-inspector-css-value-input.changed {
background: rgba(46, 204, 113, 0.15) !important;
border-color: #2ecc71 !important;
color: #27ae60 !important;
font-weight: 600 !important;
}
.open-web-inspector-css-reset-btn {
background: #95a5a6 !important;
border: none !important;
color: white !important;
padding: 2px 6px !important;
border-radius: 3px !important;
font-size: 10px !important;
cursor: pointer !important;
margin-left: 8px !important;
opacity: 0 !important;
transition: opacity 0.2s ease !important;
}
.open-web-inspector-css-property:hover .open-web-inspector-css-reset-btn {
opacity: 1 !important;
}
.open-web-inspector-css-reset-btn:hover {
background: #7f8c8d !important;
}
.open-web-inspector-css-rule-group {
background: #ffffff !important;
border: 1px solid #e2e8f0 !important;
border-radius: 6px !important;
margin-bottom: 12px !important;
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
overflow: hidden !important;
}
.open-web-inspector-css-rule-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
padding: 8px 12px !important;
cursor: pointer !important;
display: flex !important;
align-items: center !important;
justify-content: space-between !important;
font-weight: 600 !important;
font-size: 12px !important;
user-select: none !important;
transition: all 0.2s ease !important;
}
.open-web-inspector-css-rule-header:hover {
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%) !important;
transform: translateY(-1px) !important;
}
.open-web-inspector-css-rule-header.collapsed {
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%) !important;
}
.open-web-inspector-css-rule-selector {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
font-size: 11px !important;
}
.open-web-inspector-css-rule-toggle {
font-size: 14px !important;
transition: transform 0.2s ease !important;
}
.open-web-inspector-css-rule-toggle.collapsed {
transform: rotate(-90deg) !important;
}
.open-web-inspector-css-rule-content {
display: block !important;
overflow: hidden !important;
transition: max-height 0.3s ease !important;
}
.open-web-inspector-css-rule-content.collapsed {
max-height: 0 !important;
}
.open-web-inspector-css-rule-properties {
padding: 8px !important;
}
.open-web-inspector-css-rules-container {
padding: 0 !important;
margin: 0 !important;
}
.open-web-inspector-css-inherited-rule {
border-left: 3px solid #f39c12 !important;
background: linear-gradient(135deg, #fff3cd 0%, #fef9e7 100%) !important;
}
.open-web-inspector-css-inherited-rule .open-web-inspector-css-rule-header {
background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%) !important;
}
.open-web-inspector-css-inherited-rule .open-web-inspector-css-rule-header:hover {
background: linear-gradient(135deg, #e67e22 0%, #d35400 100%) !important;
}
.open-web-inspector-popup-content {
padding: 0 !important;
max-height: calc(90vh - 70px) !important;
overflow-y: auto !important;
}
.open-web-inspector-tabs {
display: flex !important;
border-bottom: 1px solid #ddd !important;
background: #f8f9fa !important;
}
.open-web-inspector-tab {
padding: 12px 24px !important;
background: none !important;
border: none !important;
cursor: pointer !important;
font-size: 15px !important;
font-weight: 500 !important;
color: #666 !important;
transition: all 0.2s !important;
border-bottom: 3px solid transparent !important;
}
.open-web-inspector-tab.active {
color: #4299e1 !important;
border-bottom-color: #4299e1 !important;
background: white !important;
}
.open-web-inspector-tab:hover {
background: #e2e8f0 !important;
}
.open-web-inspector-tab-content {
padding: 20px 25px !important;
display: none !important;
}
.open-web-inspector-tab-content.active {
display: block !important;
}
.open-web-inspector-html-content {
background: #f8f9fa !important;
border: 1px solid #e2e8f0 !important;
border-radius: 4px !important;
padding: 15px !important;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
font-size: 13px !important;
line-height: 1.5 !important;
white-space: pre-wrap !important;
overflow-x: auto !important;
max-height: 400px !important;
overflow-y: auto !important;
margin: 0 !important;
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
}
.open-web-inspector-css-grid {
display: grid !important;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) !important;
gap: 1px !important;
background: #f0f0f0 !important;
border: 1px solid #e2e8f0 !important;
border-radius: 4px !important;
overflow: hidden !important;
margin: 0 !important;
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
}
.open-web-inspector-css-property {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 6px 10px !important;
background: white !important;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
font-size: 11px !important;
line-height: 1.3 !important;
min-height: 32px !important;
border-bottom: 1px solid #f0f0f0 !important;
}
.open-web-inspector-css-property:nth-child(even) {
background-color: #f8f9fa !important;
}
.open-web-inspector-css-property-name {
font-weight: 600 !important;
color: #e53e3e !important;
min-width: 100px !important;
max-width: 130px !important;
margin-right: 8px !important;
flex-shrink: 0 !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.open-web-inspector-css-property-value {
color: #2d3748 !important;
text-align: right !important;
flex: 1 !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
font-size: 11px !important;
}
.open-web-inspector-overlay {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background: rgba(0, 0, 0, 0.5) !important;
z-index: 10000 !important;
}
.open-web-inspector-analyze-cursor {
cursor: crosshair !important;
}
.open-web-inspector-element-tree-section {
background: #ffffff !important;
border: 1px solid #e2e8f0 !important;
border-radius: 6px !important;
margin-bottom: 25px !important;
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
}
.open-web-inspector-element-tree-header {
background: #4299e1 !important;
color: white !important;
padding: 10px 15px !important;
border-radius: 6px 6px 0 0 !important;
font-size: 13px !important;
font-weight: 600 !important;
margin: 0 !important;
border-bottom: 1px solid #3182ce !important;
}
.open-web-inspector-element-path {
background: #f7fafc !important;
padding: 8px 12px !important;
border-radius: 0 0 6px 6px !important;
margin: 0 !important;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
font-size: 13px !important;
color: #4a5568 !important;
white-space: pre !important;
overflow-x: auto !important;
line-height: 1.5 !important;
max-height: 250px !important;
overflow-y: auto !important;
}
.open-web-inspector-content-section {
background: #ffffff !important;
border: 1px solid #e2e8f0 !important;
border-radius: 6px !important;
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
overflow: hidden !important;
}
.open-web-inspector-element-path .tree-element {
cursor: pointer !important;
padding: 2px 4px !important;
border-radius: 3px !important;
transition: all 0.2s ease !important;
display: inline-block !important;
}
.open-web-inspector-element-path .tree-element:hover {
background-color: #e2e8f0 !important;
color: #2d3748 !important;
}
.open-web-inspector-element-path .tree-element.selected {
background-color: #4299e1 !important;
color: white !important;
font-weight: 600 !important;
}
.open-web-inspector-element-path .tree-element.selected:hover {
background-color: #3182ce !important;
}
.open-web-inspector-preview-content {
transform: scale(0.8) !important;
transform-origin: top left !important;
width: 125% !important;
pointer-events: none !important;
position: relative !important;
overflow: hidden !important;
padding: 15px !important;
border: 1px solid #e2e8f0 !important;
border-radius: 4px !important;
background: #ffffff !important;
max-height: 300px !important;
overflow-y: auto !important;
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
}
.open-web-inspector-preview-content * {
pointer-events: none !important;
max-width: none !important;
position: static !important;
}
.open-web-inspector-preview-content img {
max-width: 100% !important;
height: auto !important;
}
.open-web-inspector-preview-content script {
display: none !important;
}
.open-web-inspector-sub-tabs {
display: flex !important;
border-bottom: 1px solid #e2e8f0 !important;
background: #f8f9fa !important;
margin: 0 !important;
padding: 0 20px !important;
}
.open-web-inspector-sub-tab {
padding: 8px 16px !important;
background: none !important;
border: none !important;
cursor: pointer !important;
font-size: 13px !important;
font-weight: 500 !important;
color: #666 !important;
transition: all 0.2s !important;
border-bottom: 2px solid transparent !important;
position: relative !important;
}
.open-web-inspector-sub-tab.active {
color: #4299e1 !important;
border-bottom-color: #4299e1 !important;
background: white !important;
}
.open-web-inspector-sub-tab:hover {
background: #e2e8f0 !important;
}
.open-web-inspector-sub-tab.active:hover {
background: white !important;
}
.open-web-inspector-sub-tab-content {
display: none !important;
padding: 20px !important;
}
.open-web-inspector-sub-tab-content.active {
display: block !important;
}
/* ========== NEW FAB-STYLE POPUP ========== */
.open-web-inspector-fab-popup {
position: fixed !important;
z-index: 999999 !important;
background: #2d3748 !important;
border-radius: 25px !important;
padding: 8px 12px !important;
display: flex !important;
gap: 4px !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
backdrop-filter: blur(10px) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
animation: fabSlideIn 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55) !important;
}
@keyframes fabSlideIn {
from {
opacity: 0;
transform: scale(0.8) translateY(10px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.open-web-inspector-fab-button {
width: 52px !important;
height: 52px !important;
border-radius: 18px !important;
border: none !important;
background: transparent !important;
color: #e2e8f0 !important;
cursor: pointer !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
font-size: 14px !important;
position: relative !important;
overflow: hidden !important;
padding: 6px 4px 4px 4px !important;
gap: 2px !important;
}
.open-web-inspector-fab-button:before {
content: '' !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
background: rgba(255, 255, 255, 0.1) !important;
border-radius: 18px !important;
opacity: 0 !important;
transition: opacity 0.2s !important;
}
.open-web-inspector-fab-label {
font-size: 9px !important;
font-weight: 500 !important;
color: inherit !important;
text-align: center !important;
line-height: 1 !important;
margin-top: 1px !important;
letter-spacing: 0.3px !important;
opacity: 0.9 !important;
z-index: 1 !important;
position: relative !important;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.open-web-inspector-fab-button:hover:before {
opacity: 1 !important;
}
.open-web-inspector-fab-button:hover .open-web-inspector-fab-label {
opacity: 1 !important;
transform: translateY(-1px) !important;
}
.open-web-inspector-fab-button:hover {
transform: scale(1.05) !important;
color: white !important;
}
.open-web-inspector-fab-button:active {
transform: scale(0.95) !important;
}
.open-web-inspector-fab-button svg {
width: 16px !important;
height: 16px !important;
fill: currentColor !important;
z-index: 1 !important;
position: relative !important;
}
/* Color variants for different buttons */
.open-web-inspector-fab-button.code {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
}
.open-web-inspector-fab-button.screenshot {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%) !important;
}
.open-web-inspector-fab-button.natural-language {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important;
}
.open-web-inspector-fab-tooltip {
position: absolute !important;
bottom: -40px !important;
left: 50% !important;
transform: translateX(-50%) !important;
background: #1a202c !important;
color: white !important;
padding: 4px 8px !important;
border-radius: 6px !important;
font-size: 11px !important;
white-space: nowrap !important;
opacity: 0 !important;
pointer-events: none !important;
transition: opacity 0.2s !important;
z-index: 1000000 !important;
}
.open-web-inspector-fab-button:hover .open-web-inspector-fab-tooltip {
opacity: 1 !important;
}
/* Panel styles for when buttons are clicked */
.open-web-inspector-panel {
position: fixed !important;
z-index: 999998 !important;
background: white !important;
border-radius: 12px !important;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15) !important;
border: 1px solid #e2e8f0 !important;
min-width: 350px !important;
max-width: 500px !important;
max-height: 600px !important;
overflow: hidden !important;
animation: panelSlideIn 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55) !important;
}
@keyframes panelSlideIn {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.open-web-inspector-panel-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
padding: 16px 20px !important;
font-weight: 600 !important;
font-size: 16px !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
cursor: move !important;
}
.open-web-inspector-panel-close {
background: rgba(255, 255, 255, 0.2) !important;
border: none !important;
color: white !important;
width: 24px !important;
height: 24px !important;
border-radius: 12px !important;
cursor: pointer !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
transition: all 0.2s !important;
}
.open-web-inspector-panel-close:hover {
background: rgba(255, 255, 255, 0.3) !important;
transform: scale(1.1) !important;
}
.open-web-inspector-panel-header-actions {
display: flex !important;
align-items: center !important;
gap: 10px !important;
}
.open-web-inspector-copy-ai-instructions-btn {
background: rgba(255, 255, 255, 0.9) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
color: #4a5568 !important;
padding: 6px 12px !important;
border-radius: 6px !important;
font-size: 12px !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
font-weight: 600 !important;
backdrop-filter: blur(10px) !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
}
.open-web-inspector-copy-ai-instructions-btn:hover {
background: rgba(255, 255, 255, 1) !important;
transform: translateY(-1px) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
color: #2d3748 !important;
}
/* Natural Language Panel Styling */
.open-web-inspector-natural-language-panel {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
box-shadow: 0 10px 40px rgba(102, 126, 234, 0.4) !important;
z-index: 1000000 !important; /* Higher than FAB popup (999999) */
}
.open-web-inspector-natural-language-panel .open-web-inspector-panel-header {
background: rgba(255, 255, 255, 0.1) !important;
backdrop-filter: blur(10px) !important;
border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important;
}
.open-web-inspector-ask-ai-title {
display: flex !important;
flex-direction: column !important;
gap: 2px !important;
}
.open-web-inspector-ask-ai-main-title {
color: white !important;
font-weight: 700 !important;
font-size: 16px !important;
line-height: 1.2 !important;
}
.open-web-inspector-ask-ai-subtitle {
color: rgba(255, 255, 255, 0.8) !important;
font-weight: 400 !important;
font-size: 12px !important;
line-height: 1.2 !important;
opacity: 0.9 !important;
}
.open-web-inspector-natural-language-panel .open-web-inspector-panel-content {
background: rgba(255, 255, 255, 0.95) !important;
backdrop-filter: blur(10px) !important;
}
/* Code Panel Title Styling */
.open-web-inspector-code-panel-title {
display: flex !important;
flex-direction: column !important;
gap: 2px !important;
}
.open-web-inspector-code-panel-main-title {
color: white !important;
font-weight: 700 !important;
font-size: 16px !important;
line-height: 1.2 !important;
}
.open-web-inspector-code-panel-subtitle {
color: rgba(255, 255, 255, 0.8) !important;
font-weight: 400 !important;
font-size: 12px !important;
line-height: 1.2 !important;
opacity: 0.9 !important;
}
.open-web-inspector-panel-content {
padding: 0 !important;
overflow-y: auto !important;
max-height: 520px !important;
}
/* Code panel specific styles */
.open-web-inspector-code-tabs {
display: flex !important;
background: #f8f9fa !important;
border-bottom: 1px solid #e2e8f0 !important;
margin: 0 !important;
padding: 0 !important;
}
.open-web-inspector-code-tab {
flex: 1 !important;
padding: 12px 20px !important;
background: none !important;
border: none !important;
cursor: pointer !important;
font-size: 14px !important;
font-weight: 500 !important;
color: #666 !important;
transition: all 0.2s !important;
border-bottom: 3px solid transparent !important;
}
.open-web-inspector-code-tab.active {
color: #4299e1 !important;
border-bottom-color: #4299e1 !important;
background: white !important;
}
.open-web-inspector-code-tab:hover {
background: #e2e8f0 !important;
}
.open-web-inspector-code-tab.active:hover {
background: white !important;
}
.open-web-inspector-code-tab-content {
display: none !important;
}
.open-web-inspector-code-tab-content.active {
display: block !important;
}
`;
document.head.appendChild(style);
}
setupEventListeners() {
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
document.addEventListener('keydown', this.handleKeyPress);
}
setupExternalAPI() {
// Store reference to this instance for later API attachment
this._apiInstance = this;
console.log('🚀 OpenWebInspector External API Setup Complete!');
console.log('📘 API will be available after initialization...');
}
attachGlobalAPI() {
// Attach API methods to the global instance (called after window.OpenWebInspector is set)
const instance = this;
// Store original class methods
instance._originalMethods = {
enableAnalyzeMode: instance.enableAnalyzeMode,
disableAnalyzeMode: instance.disableAnalyzeMode,
toggleAnalyzeMode: instance.toggleAnalyzeMode
};
// Add external API methods to the instance
instance.enable = () => {
if (!instance.isAnalyzeMode) {
instance.enableAnalyzeMode();
}
return true;
};
instance.disable = () => {
if (instance.isAnalyzeMode) {
instance.disableAnalyzeMode();
}
return true;
};
instance.toggle = () => {
instance.toggleAnalyzeMode();
return instance.isAnalyzeMode;
};
instance.isActive = () => {
return instance.isAnalyzeMode;
};
instance.getVersion = () => {
return '2.0.0'; // Updated version with external API
};
// Advanced API methods
instance.selectElement = (selector) => {
if (!instance.isAnalyzeMode) {
instance.enableAnalyzeMode();
}
const element = document.querySelector(selector);
if (element) {
instance.showElementDetails(element);
return true;
}
return false;
};
console.log('🚀 OpenWebInspector Global API Ready!');
console.log('📘 Available methods:');
console.log(' • OpenWebInspector.enable()');
console.log(' • OpenWebInspector.disable()');
console.log(' • OpenWebInspector.toggle()');
console.log(' • OpenWebInspector.isActive()');
console.log(' • OpenWebInspector.selectElement(selector)');
console.log(' • OpenWebInspector.getVersion()');
}
setupKeyboardShortcuts() {
// Keyboard shortcuts for external control
document.addEventListener('keydown', (e) => {
// Ctrl+Shift+E = Enable analyze mode
if (e.ctrlKey && e.shiftKey && e.key === 'E') {
e.preventDefault();
this.toggleAnalyzeMode();
console.log('🔥 Analyze mode toggled via keyboard shortcut (Ctrl+Shift+E)');
}
// Escape = Disable analyze mode (if active)
if (e.key === 'Escape' && this.isAnalyzeMode) {
e.preventDefault();
this.disableAnalyzeMode();
console.log('👋 Analyze mode disabled via Escape key');
}
});
console.log('⌨️ Keyboard shortcuts active:');
console.log(' • Ctrl+Shift+E = Toggle analyze mode');
console.log(' • Escape = Disable analyze mode');
}
setupDOMEvents() {
// Custom DOM events for external control
const instance = this;
document.addEventListener('open-web-inspector-enable', () => {
if (instance.enable) {
instance.enable();
} else {
instance.enableAnalyzeMode();
}
console.log('🎯 Analyze mode enabled via DOM event');
});
document.addEventListener('open-web-inspector-disable', () => {
if (instance.disable) {
instance.disable();
} else {
instance.disableAnalyzeMode();
}
console.log('🎯 Analyze mode disabled via DOM event');
});
document.addEventListener('open-web-inspector-toggle', () => {
if (instance.toggle) {
instance.toggle();
} else {
instance.toggleAnalyzeMode();
}
console.log('🎯 Analyze mode toggled via DOM event');
});
document.addEventListener('open-web-inspector-select', (e) => {
if (e.detail && e.detail.selector) {
if (instance.selectElement) {
instance.selectElement(e.detail.selector);
} else {
// Fallback to manual selection
if (!instance.isAnalyzeMode) {
instance.enableAnalyzeMode();
}
const element = document.querySelector(e.detail.selector);
if (element) {
instance.showElementDetails(element);
}
}
console.log('🎯 Element selected via DOM event:', e.detail.selector);
}
});
console.log('📡 DOM events listening:');
console.log(' • open-web-inspector-enable');
console.log(' • open-web-inspector-disable');
console.log(' • open-web-inspector-toggle');
console.log(' • open-web-inspector-select (with detail.selector)');
}
checkURLParameters() {
// Check URL parameters for auto-enable
const urlParams = new URLSearchParams(window.location.search);
const openWebInspector = urlParams.get('open-web-inspector');
const inspect = urlParams.get('inspect');
const instance = this;
if (openWebInspector === 'true' || openWebInspector === '1' || openWebInspector === 'enable') {
setTimeout(() => {
if (instance.enable) {
instance.enable();
} else {
instance.enableAnalyzeMode();
}
console.log('🌐 Analyze mode auto-enabled via URL parameter');
}, 100);
}
if (inspect) {
setTimeout(() => {
if (instance.selectElement) {
instance.selectElement(inspect);
} else {
// Fallback to manual selection
if (!instance.isAnalyzeMode) {
instance.enableAnalyzeMode();
}
const element = document.querySelector(inspect);
if (element) {
instance.showElementDetails(element);
}
}
console.log('🌐 Element auto-selected via URL parameter:', inspect);
}, 200);
}
console.log('🔗 URL parameters supported:');
console.log(' • ?open-web-inspector=true (auto-enable)');
console.log(' • ?inspect=.selector (auto-select element)');
}
toggleAnalyzeMode() {
if (this.isAnalyzeMode) {
this.disableAnalyzeMode();
} else {
this.enableAnalyzeMode();
}
}
enableAnalyzeMode() {
this.isAnalyzeMode = true;
document.addEventListener('mouseover', this.handleMouseOver);
document.addEventListener('mouseout', this.handleMouseOut);
document.addEventListener('click', this.handleClick);
// Change cursor for the entire document
this.originalCursor = document.body.style.cursor;
document.body.classList.add('open-web-inspector-analyze-cursor');
console.log('🔍 Analyze mode enabled - hover and click elements to inspect!');
}
disableAnalyzeMode() {
this.isAnalyzeMode = false;
document.removeEventListener('mouseover', this.handleMouseOver);
document.removeEventListener('mouseout', this.handleMouseOut);
document.removeEventListener('click', this.handleClick);
// Restore original cursor
document.body.classList.remove('open-web-inspector-analyze-cursor');
// Remove any existing highlight
this.removeHighlight();
// Close popup if open
this.closePopup();
console.log('👋 Analyze mode disabled');
}
handleMouseOver(event) {
if (!this.isAnalyzeMode) return;
// Don't highlight our own popup elements
if (this.isOurElement(event.target)) return;
this.highlightElement(event.target);
}
handleMouseOut(event) {
if (!this.isAnalyzeMode) return;
// Don't remove highlight when moving to child elements
if (event.target.contains(event.relatedTarget)) return;
this.removeHighlight();
}
handleClick(event) {
if (!this.isAnalyzeMode) return;
// Don't handle clicks on our popup elements
if (this.isOurElement(event.target)) return;
event.preventDefault();
event.stopPropagation();
this.showElementDetails(event.target);
}
handleKeyPress(event) {
// ESC key closes popup
if (event.key === 'Escape') {
this.closePopup();
}
}
isOurElement(element) {
return element.closest('.open-web-inspector-popup, .open-web-inspector-overlay, .open-web-inspector-fab-popup, .open-web-inspector-panel, .open-web-inspector-natural-language-panel') ||
element.id === 'analyzeToggle' ||
element.classList.contains('open-web-inspector-popup') ||
element.classList.contains('open-web-inspector-overlay') ||
element.classList.contains('open-web-inspector-fab-popup') ||
element.classList.contains('open-web-inspector-panel') ||
element.classList.contains('open-web-inspector-natural-language-panel');
}
highlightElement(element) {
this.removeHighlight();
this.currentHighlightedElement = element;
element.classList.add('open-web-inspector-highlight');
}
removeHighlight() {
if (this.currentHighlightedElement) {
this.currentHighlightedElement.classList.remove('open-web-inspector-highlight');
this.currentHighlightedElement = null;
}
}
showElementDetails(element) {
this.removeHighlight();
this.createPopup(element);
}
createPopup(element) {
this.closePopup();
this.currentElement = element;
// Store the actual page element (not inspector UI) for CSS analysis
if (!element.className.includes('open-web-inspector')) {
this.actualSelectedElemen