UNPKG

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
(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