diff --git a/src/info-panel/EventManager.ts b/src/info-panel/EventManager.ts index f7dc610..badd7ed 100644 --- a/src/info-panel/EventManager.ts +++ b/src/info-panel/EventManager.ts @@ -81,6 +81,9 @@ export class EventManager { if (!header) return; if (target.closest('button')) return; + // Button 2 (pin icon, data-action="lock") prevents dragging + if (this.stateManager.state.isPanelLocked) return; + e.preventDefault(); e.stopPropagation(); diff --git a/src/info-panel/UIManager.ts b/src/info-panel/UIManager.ts index cdf52cf..137982b 100644 --- a/src/info-panel/UIManager.ts +++ b/src/info-panel/UIManager.ts @@ -119,9 +119,26 @@ export class UIManager { createFloatingControls(): void { this.elements.controls = document.createElement("div"); this.elements.controls.className = "floating-controls vertical-layout"; // Default to vertical + + /** + * BUTTON MAPPING DOCUMENTATION: + * ============================= + * Button 1 (Unlock Icon): data-action="pin" → controls isPanelPinned + * - Unpinned (default): Panel follows magnify glass + * - Pinned: Panel stays at fixed position (unlocked from following glass) + * + * Button 2 (Pin Icon): data-action="lock" → controls isPanelLocked + * - Unlocked (default): Panel can be dragged + * - Locked: Panel cannot be dragged (pinned in place) + * + * NOTE: The data-action names are counterintuitive but kept for backwards compatibility. + * The ICONS correctly represent the functionality: + * - Unlock icon = unlock from following glass + * - Pin icon = pin position (prevent drag) + */ this.elements.controls.innerHTML = ` - - + + `; diff --git a/web/info-panel/EventManager.js b/web/info-panel/EventManager.js index c4fb607..00aa0a2 100644 --- a/web/info-panel/EventManager.js +++ b/web/info-panel/EventManager.js @@ -46,6 +46,7 @@ class EventManager { const header = target.closest(".panel-header"); if (!header) return; if (target.closest("button")) return; + if (this.stateManager.state.isPanelLocked) return; e.preventDefault(); e.stopPropagation(); isDragging = true; diff --git a/web/info-panel/EventManager.js.map b/web/info-panel/EventManager.js.map index cf03f37..fe909ca 100644 --- a/web/info-panel/EventManager.js.map +++ b/web/info-panel/EventManager.js.map @@ -1 +1 @@ -{"version":3,"file":"EventManager.js","sources":["../../src/info-panel/EventManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel Event Manager (TypeScript)\n * \n * Complete Event Manager extracted from magnify_info_panel.js\n * Handles all event binding and delegation for the info panel.\n */\n\nimport { StateManager } from './StateManager';\nimport { UIManager } from './UIManager'; // Kept for type compatibility if needed, though we use HTMLElement mostly\nimport { PositionManager } from './PositionManager';\nimport { isUserTyping } from '../shared/utils';\n\nexport interface EventCallbacks {\n toggleVisibility: () => void;\n togglePin: () => void;\n updatePosition: () => void;\n}\n\n/**\n * Event Manager class.\n * Handles all event listeners for the info panel.\n */\nexport class EventManager {\n stateManager: StateManager;\n panelElement: HTMLElement | null;\n positionManager: PositionManager;\n callbacks: EventCallbacks | null;\n\n dragCleanup: (() => void) | null;\n\n boundHandleMouseMove: (e: MouseEvent) => void;\n boundHandleKeyDown: (e: KeyboardEvent) => void;\n boundHandleKeyUp: (e: KeyboardEvent) => void;\n\n constructor(stateManager: StateManager, panelElement: HTMLElement | null, positionManager: PositionManager, callbacks?: EventCallbacks) {\n this.stateManager = stateManager;\n this.panelElement = panelElement;\n this.positionManager = positionManager;\n this.callbacks = callbacks || null;\n this.dragCleanup = null;\n\n this.boundHandleMouseMove = this.handleMouseMove.bind(this);\n this.boundHandleKeyDown = this.handleKeyDown.bind(this);\n this.boundHandleKeyUp = this.handleKeyUp.bind(this);\n\n this.setupEventListeners();\n }\n\n setupEventListeners(): void {\n this.setupPanelEvents();\n this.setupHotkeyEvents();\n this.setupDragEvents();\n this.setupHoverEvents();\n }\n\n setupPanelEvents(): void {\n if (!this.panelElement) return;\n\n // Vue handles most internal events now.\n // We can keep specific global-ish listeners if needed here.\n }\n\n setupHotkeyEvents(): void {\n document.addEventListener('keydown', this.boundHandleKeyDown);\n }\n\n setupDragEvents(): void {\n if (!this.panelElement) return;\n\n let isDragging = false;\n let rafId: number | null = null;\n let targetX = 0;\n let targetY = 0;\n\n const startDrag = (e: MouseEvent) => {\n if (!this.panelElement) return;\n const target = e.target as HTMLElement;\n\n // Only allow dragging from header (expand clickable area)\n const header = target.closest('.panel-header');\n if (!header) return;\n if (target.closest('button')) return;\n\n e.preventDefault();\n e.stopPropagation();\n\n isDragging = true;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const rect = this.panelElement.getBoundingClientRect();\n const startLeft = rect.left;\n const startTop = rect.top;\n\n // Visual feedback - dragging state\n this.panelElement.style.cursor = 'grabbing';\n this.panelElement.style.opacity = '0.9';\n this.panelElement.style.transition = 'none'; // Disable transitions during drag\n document.body.style.cursor = 'grabbing';\n document.body.style.userSelect = 'none';\n\n const onMouseMove = (moveEvent: MouseEvent) => {\n if (!isDragging) return;\n\n const dx = moveEvent.clientX - startX;\n const dy = moveEvent.clientY - startY;\n\n targetX = startLeft + dx;\n targetY = startTop + dy;\n\n // Keep within viewport bounds\n const panelWidth = this.panelElement?.offsetWidth || 0;\n const panelHeight = this.panelElement?.offsetHeight || 0;\n targetX = Math.max(0, Math.min(targetX, window.innerWidth - panelWidth));\n targetY = Math.max(0, Math.min(targetY, window.innerHeight - panelHeight));\n\n // Use RAF for smooth updates\n if (rafId === null) {\n rafId = requestAnimationFrame(() => {\n if (this.panelElement && isDragging) {\n this.panelElement.style.left = `${targetX}px`;\n this.panelElement.style.top = `${targetY}px`;\n }\n rafId = null;\n });\n }\n };\n\n const onMouseUp = () => {\n isDragging = false;\n\n // Cancel any pending RAF\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n\n // Restore visual state\n if (this.panelElement) {\n this.panelElement.style.cursor = '';\n this.panelElement.style.opacity = '';\n this.panelElement.style.transition = '';\n\n // Final position update\n this.panelElement.style.left = `${targetX}px`;\n this.panelElement.style.top = `${targetY}px`;\n\n // Save pinned position\n this.stateManager.state.pinnedPosition = { x: targetX, y: targetY };\n }\n\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n this.panelElement.addEventListener('mousedown', startDrag);\n\n this.dragCleanup = () => {\n if (this.panelElement) this.panelElement.removeEventListener('mousedown', startDrag);\n };\n }\n\n setupHoverEvents(): void {\n if (!this.panelElement) return;\n\n this.panelElement.addEventListener('mouseenter', () => {\n this.stateManager.state.isPanelHovered = true;\n this.stateManager.clearAutoExpandTimer();\n });\n\n this.panelElement.addEventListener('mouseleave', () => {\n this.stateManager.state.isPanelHovered = false;\n if (!this.stateManager.state.isHoveringNode) {\n this.stateManager.scheduleAutoCollapse();\n }\n });\n }\n\n handleKeyDown(e: KeyboardEvent): void {\n if (isUserTyping()) return;\n\n const settings = this.stateManager.state.settings;\n const key = e.key.toLowerCase();\n // console.log(`[InfoPanel] KeyDown: ${key}`);\n\n // Toggle visibility\n if (key === (settings[\"🔍MagnifyGlass.ToggleHotkey\"] as string).toLowerCase()) {\n console.log(\"[InfoPanel] Toggle visibility hotkey matched!\");\n e.preventDefault();\n this.callbacks?.toggleVisibility();\n }\n\n // Toggle Pin\n if (key === (settings[\"🔍MagnifyGlass.PinPanelHotkey\"] as string).toLowerCase()) {\n e.preventDefault();\n this.callbacks?.togglePin();\n }\n }\n\n handleMouseMove(e: MouseEvent): void {\n // Placeholder\n }\n\n handleKeyUp(e: KeyboardEvent): void {\n // Placeholder\n }\n\n cleanup(): void {\n document.removeEventListener('keydown', this.boundHandleKeyDown);\n if (this.dragCleanup) this.dragCleanup();\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"EventManager.js","sources":["../../src/info-panel/EventManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel Event Manager (TypeScript)\n * \n * Complete Event Manager extracted from magnify_info_panel.js\n * Handles all event binding and delegation for the info panel.\n */\n\nimport { StateManager } from './StateManager';\nimport { UIManager } from './UIManager'; // Kept for type compatibility if needed, though we use HTMLElement mostly\nimport { PositionManager } from './PositionManager';\nimport { isUserTyping } from '../shared/utils';\n\nexport interface EventCallbacks {\n toggleVisibility: () => void;\n togglePin: () => void;\n updatePosition: () => void;\n}\n\n/**\n * Event Manager class.\n * Handles all event listeners for the info panel.\n */\nexport class EventManager {\n stateManager: StateManager;\n panelElement: HTMLElement | null;\n positionManager: PositionManager;\n callbacks: EventCallbacks | null;\n\n dragCleanup: (() => void) | null;\n\n boundHandleMouseMove: (e: MouseEvent) => void;\n boundHandleKeyDown: (e: KeyboardEvent) => void;\n boundHandleKeyUp: (e: KeyboardEvent) => void;\n\n constructor(stateManager: StateManager, panelElement: HTMLElement | null, positionManager: PositionManager, callbacks?: EventCallbacks) {\n this.stateManager = stateManager;\n this.panelElement = panelElement;\n this.positionManager = positionManager;\n this.callbacks = callbacks || null;\n this.dragCleanup = null;\n\n this.boundHandleMouseMove = this.handleMouseMove.bind(this);\n this.boundHandleKeyDown = this.handleKeyDown.bind(this);\n this.boundHandleKeyUp = this.handleKeyUp.bind(this);\n\n this.setupEventListeners();\n }\n\n setupEventListeners(): void {\n this.setupPanelEvents();\n this.setupHotkeyEvents();\n this.setupDragEvents();\n this.setupHoverEvents();\n }\n\n setupPanelEvents(): void {\n if (!this.panelElement) return;\n\n // Vue handles most internal events now.\n // We can keep specific global-ish listeners if needed here.\n }\n\n setupHotkeyEvents(): void {\n document.addEventListener('keydown', this.boundHandleKeyDown);\n }\n\n setupDragEvents(): void {\n if (!this.panelElement) return;\n\n let isDragging = false;\n let rafId: number | null = null;\n let targetX = 0;\n let targetY = 0;\n\n const startDrag = (e: MouseEvent) => {\n if (!this.panelElement) return;\n const target = e.target as HTMLElement;\n\n // Only allow dragging from header (expand clickable area)\n const header = target.closest('.panel-header');\n if (!header) return;\n if (target.closest('button')) return;\n\n // Button 2 (pin icon, data-action=\"lock\") prevents dragging\n if (this.stateManager.state.isPanelLocked) return;\n\n e.preventDefault();\n e.stopPropagation();\n\n isDragging = true;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const rect = this.panelElement.getBoundingClientRect();\n const startLeft = rect.left;\n const startTop = rect.top;\n\n // Visual feedback - dragging state\n this.panelElement.style.cursor = 'grabbing';\n this.panelElement.style.opacity = '0.9';\n this.panelElement.style.transition = 'none'; // Disable transitions during drag\n document.body.style.cursor = 'grabbing';\n document.body.style.userSelect = 'none';\n\n const onMouseMove = (moveEvent: MouseEvent) => {\n if (!isDragging) return;\n\n const dx = moveEvent.clientX - startX;\n const dy = moveEvent.clientY - startY;\n\n targetX = startLeft + dx;\n targetY = startTop + dy;\n\n // Keep within viewport bounds\n const panelWidth = this.panelElement?.offsetWidth || 0;\n const panelHeight = this.panelElement?.offsetHeight || 0;\n targetX = Math.max(0, Math.min(targetX, window.innerWidth - panelWidth));\n targetY = Math.max(0, Math.min(targetY, window.innerHeight - panelHeight));\n\n // Use RAF for smooth updates\n if (rafId === null) {\n rafId = requestAnimationFrame(() => {\n if (this.panelElement && isDragging) {\n this.panelElement.style.left = `${targetX}px`;\n this.panelElement.style.top = `${targetY}px`;\n }\n rafId = null;\n });\n }\n };\n\n const onMouseUp = () => {\n isDragging = false;\n\n // Cancel any pending RAF\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n\n // Restore visual state\n if (this.panelElement) {\n this.panelElement.style.cursor = '';\n this.panelElement.style.opacity = '';\n this.panelElement.style.transition = '';\n\n // Final position update\n this.panelElement.style.left = `${targetX}px`;\n this.panelElement.style.top = `${targetY}px`;\n\n // Save pinned position\n this.stateManager.state.pinnedPosition = { x: targetX, y: targetY };\n }\n\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n this.panelElement.addEventListener('mousedown', startDrag);\n\n this.dragCleanup = () => {\n if (this.panelElement) this.panelElement.removeEventListener('mousedown', startDrag);\n };\n }\n\n setupHoverEvents(): void {\n if (!this.panelElement) return;\n\n this.panelElement.addEventListener('mouseenter', () => {\n this.stateManager.state.isPanelHovered = true;\n this.stateManager.clearAutoExpandTimer();\n });\n\n this.panelElement.addEventListener('mouseleave', () => {\n this.stateManager.state.isPanelHovered = false;\n if (!this.stateManager.state.isHoveringNode) {\n this.stateManager.scheduleAutoCollapse();\n }\n });\n }\n\n handleKeyDown(e: KeyboardEvent): void {\n if (isUserTyping()) return;\n\n const settings = this.stateManager.state.settings;\n const key = e.key.toLowerCase();\n // console.log(`[InfoPanel] KeyDown: ${key}`);\n\n // Toggle visibility\n if (key === (settings[\"🔍MagnifyGlass.ToggleHotkey\"] as string).toLowerCase()) {\n console.log(\"[InfoPanel] Toggle visibility hotkey matched!\");\n e.preventDefault();\n this.callbacks?.toggleVisibility();\n }\n\n // Toggle Pin\n if (key === (settings[\"🔍MagnifyGlass.PinPanelHotkey\"] as string).toLowerCase()) {\n e.preventDefault();\n this.callbacks?.togglePin();\n }\n }\n\n handleMouseMove(e: MouseEvent): void {\n // Placeholder\n }\n\n handleKeyUp(e: KeyboardEvent): void {\n // Placeholder\n }\n\n cleanup(): void {\n document.removeEventListener('keydown', this.boundHandleKeyDown);\n if (this.dragCleanup) this.dragCleanup();\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/info-panel/UIManager.js b/web/info-panel/UIManager.js index b0405b4..5e60882 100644 --- a/web/info-panel/UIManager.js +++ b/web/info-panel/UIManager.js @@ -79,8 +79,8 @@ class UIManager { this.elements.controls = document.createElement("div"); this.elements.controls.className = "floating-controls vertical-layout"; this.elements.controls.innerHTML = ` - - + + `; diff --git a/web/info-panel/UIManager.js.map b/web/info-panel/UIManager.js.map index f5aac42..7a84e6b 100644 --- a/web/info-panel/UIManager.js.map +++ b/web/info-panel/UIManager.js.map @@ -1 +1 @@ -{"version":3,"file":"UIManager.js","sources":["../../src/info-panel/UIManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel UI Manager (TypeScript)\n * \n * Complete UI Manager extracted from magnify_info_panel.js\n * Handles all DOM manipulation and UI creation for the info panel.\n */\n\n// import { Z_INDEX } from '../shared/constants'; // Not used in original code? Kept for reference if needed\nimport { StateManager } from './StateManager';\nimport { Icons } from '../shared/icons';\n\ninterface InfoPanelElements {\n panel: HTMLDivElement | null;\n header: HTMLDivElement | null;\n content: HTMLDivElement | null;\n controls: HTMLDivElement | null;\n}\n\n/**\n * UI Manager class.\n * Handles all DOM manipulation and UI creation.\n */\nexport class UIManager {\n stateManager: StateManager;\n elements: InfoPanelElements;\n\n constructor(stateManager: StateManager) {\n this.stateManager = stateManager;\n this.elements = {\n panel: null,\n header: null,\n content: null,\n controls: null\n };\n\n this.createPanel();\n this.injectStyles();\n }\n\n /**\n * Create the main panel and its components.\n */\n createPanel(): void {\n // Main panel container\n this.elements.panel = document.createElement(\"div\");\n this.elements.panel.id = \"comfyui-magnify-info-panel-pro-v2\";\n this.elements.panel.className = `magnify-info-panel theme-${this.stateManager.state.currentTheme}`;\n\n // Header\n this.elements.header = document.createElement(\"div\");\n this.elements.header.className = \"panel-header\";\n this.elements.header.innerHTML = `\n
\n
${Icons.magnifyGlass}
\n
Inspector
\n
Real-time analysis
\n
\n
\n \n
\n `;\n\n // Content container\n this.elements.content = document.createElement(\"div\");\n this.elements.content.className = \"panel-content\";\n\n this.elements.panel.appendChild(this.elements.header);\n this.elements.panel.appendChild(this.elements.content);\n\n this.applyStyles();\n document.body.appendChild(this.elements.panel);\n\n // Add click event delegation for panel elements\n this.elements.panel.addEventListener('click', (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n\n // Handle minimize button\n const minimizeBtn = target.closest('[data-action=\"minimize\"]');\n if (minimizeBtn) {\n this.stateManager.state.isPanelMinimized = !this.stateManager.state.isPanelMinimized;\n this.updateMinimizedState();\n return;\n }\n\n // Handle section header clicks for expand/collapse\n const sectionHeader = target.closest('.section-header') as HTMLElement;\n if (sectionHeader) {\n const sectionId = sectionHeader.getAttribute('data-section');\n // Skip node section (always expanded)\n if (sectionId && sectionId !== 'node') {\n const isExpanded = this.stateManager.state.expandedSections.has(sectionId);\n if (isExpanded) {\n this.stateManager.state.expandedSections.delete(sectionId);\n } else {\n this.stateManager.state.expandedSections.add(sectionId);\n }\n\n // Update visual state\n sectionHeader.classList.toggle('expanded', !isExpanded);\n const sectionContent = sectionHeader.nextElementSibling as HTMLElement;\n if (sectionContent && sectionContent.classList.contains('section-content')) {\n sectionContent.classList.toggle('expanded', !isExpanded);\n }\n }\n }\n });\n\n // Create floating controls after panel is in DOM\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.ShowHoveringControls\"]) {\n this.createFloatingControls();\n // Update control states immediately after creation\n this.updateControlStates();\n }\n }\n\n /**\n * Create floating control buttons.\n */\n createFloatingControls(): void {\n this.elements.controls = document.createElement(\"div\");\n this.elements.controls.className = \"floating-controls vertical-layout\"; // Default to vertical\n this.elements.controls.innerHTML = `\n \n \n \n \n `;\n\n // Insert before the panel in the document body, not as a child\n document.body.appendChild(this.elements.controls);\n\n // Hide and position off-screen initially - will be shown when magnify glass activates and positions them\n this.elements.controls.style.display = 'none';\n this.elements.controls.style.visibility = 'hidden';\n this.elements.controls.style.left = '-9999px';\n this.elements.controls.style.top = '-9999px';\n\n // Add click event delegation for control buttons\n this.elements.controls.addEventListener('click', (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n const button = target.closest('button[data-action]') as HTMLButtonElement;\n if (!button) return;\n\n const action = button.getAttribute('data-action');\n console.log(`[InfoPanel] Control button clicked: ${action}`);\n\n switch (action) {\n case 'pin':\n this.stateManager.togglePinning();\n this.updatePinnedState();\n this.updateControlStates();\n break;\n case 'lock':\n this.stateManager.state.isPanelLocked = !this.stateManager.state.isPanelLocked;\n this.updateControlStates();\n if (this.elements.panel) {\n this.elements.panel.classList.toggle('panel-locked', this.stateManager.state.isPanelLocked);\n }\n break;\n case 'toggle-panel':\n if (this.stateManager.state.isPanelVisible) {\n this.hide();\n } else {\n this.show();\n }\n this.updateControlStates();\n break;\n case 'toggle-glass':\n this.stateManager.state.isGlassPreviewVisible = !this.stateManager.state.isGlassPreviewVisible;\n this.updateControlStates();\n // Toggle glass visibility on the magnify glass\n if (window.comfyUIMagnifyGlass) {\n if (this.stateManager.state.isGlassPreviewVisible) {\n window.comfyUIMagnifyGlass.ui.show();\n } else {\n window.comfyUIMagnifyGlass.ui.hide();\n }\n }\n break;\n }\n });\n\n // Set initial layout based on settings\n const controlsPosition = this.stateManager.state.settings[\"🔍MagnifyGlass.ControlsPosition\"] || \"top-right\";\n this.updateControlsLayout(controlsPosition);\n }\n\n /**\n * Update control button states.\n */\n updateControlStates(): void {\n if (!this.elements.controls) return;\n\n const pinBtn = this.elements.controls.querySelector('[data-action=\"pin\"]') as HTMLButtonElement;\n const lockBtn = this.elements.controls.querySelector('[data-action=\"lock\"]') as HTMLButtonElement;\n const visibilityBtn = this.elements.controls.querySelector('[data-action=\"toggle-panel\"]') as HTMLButtonElement;\n const glassBtn = this.elements.controls.querySelector('[data-action=\"toggle-glass\"]') as HTMLButtonElement;\n\n if (pinBtn) {\n pinBtn.classList.toggle('active', this.stateManager.state.isPanelPinned);\n pinBtn.title = this.stateManager.state.isPanelPinned ? \"Lock Panel\" : \"Unlock Panel\";\n pinBtn.innerHTML = this.stateManager.state.isPanelPinned ? Icons.lock : Icons.unlock;\n }\n\n if (lockBtn) {\n // Only show/enable lock button when pinned\n lockBtn.style.display = this.stateManager.state.isPanelPinned ? 'flex' : 'none';\n lockBtn.classList.toggle('active', this.stateManager.state.isPanelLocked);\n lockBtn.title = this.stateManager.state.isPanelLocked ? \"Upin Panel Position\" : \"Pin Panel Position\";\n lockBtn.disabled = !this.stateManager.state.isPanelPinned;\n }\n\n if (visibilityBtn) {\n visibilityBtn.classList.toggle('active', this.stateManager.state.isPanelVisible);\n visibilityBtn.title = this.stateManager.state.isPanelVisible ? \"Show Panel\" : \"Hide Panel\";\n }\n\n if (glassBtn) {\n glassBtn.classList.toggle('active', this.stateManager.state.isGlassPreviewVisible);\n glassBtn.title = this.stateManager.state.isGlassPreviewVisible ? \"Hide Glass Preview\" : \"Show Glass Preview\";\n }\n }\n\n /**\n * Update controls layout based on position setting.\n * @param position \n */\n updateControlsLayout(position: string): void {\n if (!this.elements.controls) return;\n\n // Remove existing layout classes\n this.elements.controls.classList.remove('horizontal-layout', 'vertical-layout');\n\n // Add appropriate layout class\n if (['top', 'bottom'].includes(position)) {\n this.elements.controls.classList.add('horizontal-layout');\n } else {\n this.elements.controls.classList.add('vertical-layout');\n }\n }\n\n /**\n * Apply current styles to elements.\n */\n applyStyles(): void {\n if (!this.elements.panel) return;\n\n const settings = this.stateManager.state.settings;\n\n // Apply custom text color via CSS variable if provided\n const textColor = settings[\"🔍MagnifyGlass.InfoPanelTextColor\"];\n if (textColor) {\n // Ensure color has # symbol\n const normalizedTextColor = textColor.startsWith('#') ? textColor : `#${textColor}`;\n this.elements.panel.style.setProperty('--info-panel-text-color', normalizedTextColor);\n }\n\n // Apply custom accent color via CSS variable if provided\n const accentColor = settings[\"🔍MagnifyGlass.InfoPanelAccentColor\"];\n if (accentColor) {\n // Ensure color has # symbol\n const normalizedAccentColor = accentColor.startsWith('#') ? accentColor : `#${accentColor}`;\n this.elements.panel.style.setProperty('--info-panel-accent-color', normalizedAccentColor);\n }\n\n // Apply opacity setting if panel is visible (convert percentage to decimal)\n if (this.stateManager.state.isPanelVisible) {\n const opacityPercent = settings[\"🔍MagnifyGlass.InfoPanelOpacity\"];\n this.elements.panel.style.opacity = (opacityPercent / 100).toString();\n }\n\n this.elements.panel.style.cssText = `\n position: absolute;\n width: ${settings[\"🔍MagnifyGlass.InfoPanelWidth\"]}px;\n max-height: ${settings[\"🔍MagnifyGlass.InfoPanelMaxHeight\"]}px;\n z-index: 99999;\n display: none;\n opacity: 0;\n transform: translateY(-10px);\n transition: ${settings[\"🔍MagnifyGlass.InfoPanelAnimations\"] ? 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' : 'none'};\n pointer-events: auto;\n user-select: none;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n ${textColor ? `--info-panel-text-color: ${textColor.startsWith('#') ? textColor : `#${textColor}`};` : ''}\n ${accentColor ? `--info-panel-accent-color: ${accentColor.startsWith('#') ? accentColor : `#${accentColor}`};` : ''}\n `;\n }\n\n /**\n * Show the panel.\n */\n show(): void {\n if (!this.elements.panel) return;\n\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"]) {\n this.elements.panel.style.display = \"block\";\n // Controls are shown by positionFloatingControls() after proper positioning\n // Apply user's opacity setting when showing (convert percentage to decimal)\n const opacityPercent = this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelOpacity\"];\n this.elements.panel.style.opacity = (opacityPercent / 100).toString();\n // Trigger reflow\n this.elements.panel.offsetHeight;\n this.elements.panel.classList.add('visible');\n this.stateManager.state.isPanelVisible = true;\n }\n }\n\n /**\n * Hide the panel.\n */\n hide(): void {\n if (!this.elements.panel) return;\n\n this.elements.panel.classList.remove('visible');\n setTimeout(() => {\n if (!this.stateManager.state.isPanelVisible && this.elements.panel) {\n this.elements.panel.style.display = \"none\";\n if (this.elements.controls) {\n this.elements.controls.style.display = \"none\";\n }\n }\n }, this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelAnimations\"] ? 300 : 0);\n this.stateManager.state.isPanelVisible = false;\n }\n\n /**\n * Update minimized state.\n */\n updateMinimizedState(): void {\n if (!this.elements.panel || !this.elements.header) return;\n\n this.elements.panel.classList.toggle('panel-minimized', this.stateManager.state.isPanelMinimized);\n const minimizeBtn = this.elements.header.querySelector('.minimize-btn');\n if (minimizeBtn) {\n minimizeBtn.textContent = this.stateManager.state.isPanelMinimized ? '+' : '−';\n (minimizeBtn as HTMLElement).title = this.stateManager.state.isPanelMinimized ? 'Expand Panel' : 'Minimize Panel';\n }\n }\n\n /**\n * Update pinned state.\n */\n updatePinnedState(): void {\n if (!this.elements.panel) return;\n\n this.elements.panel.classList.toggle('panel-pinned', this.stateManager.state.isPanelPinned);\n this.elements.panel.classList.toggle('panel-locked', this.stateManager.state.isPanelLocked);\n this.updateControlStates();\n }\n\n /**\n * Update theme class.\n * @param newTheme \n */\n updateTheme(newTheme: string): void {\n if (this.elements.panel) {\n this.elements.panel.className = this.elements.panel.className.replace(/theme-\\w+/, `theme-${newTheme.toLowerCase()}`);\n }\n }\n\n /**\n * Display information in the panel.\n * @param info \n */\n displayInfo(info: any): void {\n const sections = this.buildSections(info);\n this.renderSections(sections);\n this.updateSectionStates();\n this.updateHeaderSubtitle(info);\n }\n\n /**\n * Build section data from info object.\n * @param info \n * @returns \n */\n buildSections(info: any): any[] {\n const sections: any[] = [];\n const settings = this.stateManager.state.settings;\n\n // Inspector section\n if (settings[\"🔍MagnifyGlass.ShowInspectorTab\"]) {\n const inspectorContent = [\n { label: 'Cursor Canvas', value: `(${Math.round(info.cursor.canvas.x)}, ${Math.round(info.cursor.canvas.y)})` },\n { label: 'Canvas Scale', value: `${(info.canvas.scale * 100).toFixed(1)}%` },\n { label: 'Magnifier Zoom', value: `${info.magnifier.zoomFactor}×` }\n ];\n\n sections.push({\n id: 'inspector',\n icon: Icons.info,\n title: 'Inspector',\n content: inspectorContent\n });\n }\n\n // Media section\n if (info.media) {\n const mediaContent = [\n { label: 'Type', value: info.media.tagName },\n { label: 'Source', value: info.media.src }\n ];\n\n if (info.media.naturalSize) {\n mediaContent.push({ label: 'Natural', value: info.media.naturalSize });\n }\n\n sections.push({\n id: 'media',\n icon: Icons.camera,\n title: 'Media',\n badge: info.media.tagName,\n content: mediaContent\n });\n }\n\n // Node Details section\n if (info.hoveredNode) {\n const nodeContent = [\n { label: 'Title', value: info.hoveredNode.title }\n ];\n\n // Check if this is a complex node that shows all widgets\n const nodeType = info.hoveredNode.type ? info.hoveredNode.type.toLowerCase() : '';\n const isSaveNode = nodeType.includes('save') &&\n !nodeType.includes('checkpoint') &&\n !nodeType.includes('model') &&\n !nodeType.includes('preview');\n\n const showAllWidgets = info.hoveredNode.type && (\n nodeType.includes('ksampler') ||\n nodeType.includes('sampler') ||\n nodeType.includes('k_samplers') ||\n nodeType.includes('checkpoint') ||\n nodeType.includes('model') ||\n nodeType.includes('lora') ||\n nodeType.includes('controlnet') ||\n nodeType.includes('advanced') ||\n nodeType.includes('detailer') ||\n nodeType.includes('inpaint') ||\n nodeType.includes('upscale') ||\n nodeType.includes('clip') ||\n nodeType.includes('text') ||\n nodeType.includes('encode')\n ) && !isSaveNode;\n\n if (!showAllWidgets) {\n // Add specific extractions for simple nodes\n const checkpointInfo = this.getCheckpointInfo(info.hoveredNode);\n if (checkpointInfo) {\n nodeContent.push({ label: 'Model', value: checkpointInfo });\n }\n\n const imageInfo = this.getImageInfo(info.hoveredNode);\n if (imageInfo) {\n if (typeof imageInfo === 'string') {\n nodeContent.push({ label: 'Image', value: imageInfo });\n } else if (typeof imageInfo === 'object' && imageInfo !== null) {\n const imgInfoAny = imageInfo as any;\n nodeContent.push({ label: 'Image Size', value: `${imgInfoAny.width}×${imgInfoAny.height}` });\n if (imgInfoAny.src) {\n nodeContent.push({ label: 'Image Source', value: imgInfoAny.src });\n }\n }\n }\n\n const textBoxContent = this.getTextBoxContent(info.hoveredNode);\n if (textBoxContent) {\n nodeContent.push({ label: 'Text', value: textBoxContent });\n }\n }\n\n const importantParameters = this.getImportantNodeParameters(info.hoveredNode);\n nodeContent.push(...importantParameters);\n\n sections.push({\n id: 'node',\n icon: Icons.box,\n title: 'Node',\n badge: info.hoveredNode.type,\n content: nodeContent\n });\n }\n\n return sections;\n }\n\n /**\n * Render sections to the panel.\n * @param sections \n */\n renderSections(sections: any[]): void {\n if (!this.elements.content) return;\n\n if (sections.length === 0) {\n this.elements.content.innerHTML = `\n
\n
${Icons.mapPin}
\n
Empty canvas area
\n
\n `;\n return;\n }\n\n this.elements.content.innerHTML = sections.map(section => `\n
\n
\n ${section.icon}\n ${section.title}\n ${section.badge ? `${section.badge}` : ''}\n ${section.id !== 'node' ? `${Icons.chevronRight}` : ''}\n
\n
\n
\n ${section.content.map((item: any) => {\n const value = this.formatValue(item.value, item.label);\n const valueClass = this.getValueClass(item.value);\n const valueAttributes = this.getValueAttributes(item.value);\n return `\n
\n ${item.label}\n ${value}\n
`;\n }).join('')}\n
\n
\n
\n `).join('');\n }\n\n /**\n * Update section expansion states.\n */\n updateSectionStates(): void {\n if (!this.elements.content) return;\n\n const sections = this.elements.content.querySelectorAll('.info-section');\n sections.forEach(section => {\n const sectionId = (section as HTMLElement).dataset.section;\n if (!sectionId) return;\n\n const header = section.querySelector('.section-header');\n const content = section.querySelector('.section-content');\n\n if (header && content) {\n if (this.stateManager.state.expandedSections.has(sectionId)) {\n header.classList.add('expanded');\n content.classList.add('expanded');\n } else {\n header.classList.remove('expanded');\n content.classList.remove('expanded');\n }\n }\n });\n }\n\n /**\n * Format a value for display.\n * @param value \n * @param label\n * @returns \n */\n formatValue(value: any, label?: string): string {\n if (value === null || value === undefined) return '';\n\n const str = String(value);\n\n // Show full text for prompts, text content, model names, and file paths\n if (label && (\n label.toLowerCase().includes('text') ||\n label.toLowerCase().includes('prompt') ||\n label.toLowerCase().includes('model') ||\n label.toLowerCase().includes('file') ||\n label.toLowerCase().includes('conditioning') ||\n label.toLowerCase().includes('positive') ||\n label.toLowerCase().includes('negative')\n )) {\n return str;\n }\n\n // Show full text for very long values (no truncation)\n return str;\n }\n\n /**\n * Get value class for styling.\n * @param value \n * @returns \n */\n getValueClass(value: any): string {\n if (!value) return '';\n\n const str = String(value);\n let classes: string[] = [];\n\n // Mark text that might benefit from special styling for readability\n if (str.length > 100) {\n classes.push('long-text');\n }\n\n return classes.join(' ');\n }\n\n /**\n * Get value attributes.\n * @param value \n * @returns \n */\n getValueAttributes(value: any): string {\n // Since we show full text now, we don't need title attributes for long text\n // Only add title for very long text that might benefit from tooltips\n if (!value) return '';\n\n const str = String(value);\n if (str.length > 500) { // Only for extremely long text\n return `title=\"${str.replace(/\"/g, '"')}\"`;\n }\n\n return '';\n }\n\n /**\n * Update header subtitle.\n * @param info \n */\n updateHeaderSubtitle(info: any): void {\n if (!this.elements.header) return;\n\n const subtitleElement = this.elements.header.querySelector('.header-subtitle') as HTMLElement;\n if (subtitleElement) {\n const accentColor = this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelAccentColor\"];\n if (info.hoveredNode) {\n subtitleElement.textContent = `Analyzing: ${info.hoveredNode.title}`;\n subtitleElement.style.color = accentColor || '';\n } else if (info.media) {\n subtitleElement.textContent = `Media: ${info.media.tagName}`;\n subtitleElement.style.color = accentColor || '';\n } else if (info.connection) {\n subtitleElement.textContent = `Connection: ${info.connection.type}`;\n subtitleElement.style.color = accentColor || '';\n } else {\n subtitleElement.textContent = 'Real-time analysis';\n subtitleElement.style.color = '';\n }\n }\n }\n\n // ============== Helper methods for node information extraction ==============\n\n /**\n * Get checkpoint/model info from a node.\n * @param nodeInfo \n * @returns \n */\n getCheckpointInfo(nodeInfo: any): string | null {\n if (nodeInfo.type && (\n nodeInfo.type.includes(\"CheckpointLoader\") ||\n nodeInfo.type.includes(\"LoadCheckpoint\") ||\n nodeInfo.type.includes(\"ModelLoader\") ||\n nodeInfo.type.includes(\"UNETLoader\") ||\n nodeInfo.type.includes(\"VAELoader\") ||\n nodeInfo.type.includes(\"LoraLoader\")\n )) {\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n for (const widget of nodeInfo.widgets) {\n if (widget.name && (\n widget.name.toLowerCase().includes(\"model\") ||\n widget.name.toLowerCase().includes(\"checkpoint\") ||\n widget.name.toLowerCase().includes(\"ckpt\") ||\n widget.name.toLowerCase().includes(\"lora\") ||\n widget.name.toLowerCase().includes(\"vae\") ||\n widget.name.toLowerCase().includes(\"file\")\n )) {\n const value = String(widget.value);\n const filename = value.split(/[\\/\\\\]/).pop();\n return filename || value;\n }\n }\n }\n }\n return null;\n }\n\n /**\n * Get image info from a node.\n * @param nodeInfo \n * @returns \n */\n getImageInfo(nodeInfo: any): string | object | null {\n if (nodeInfo.type && (\n nodeInfo.type.includes(\"SaveImage\") ||\n nodeInfo.type.includes(\"PreviewImage\") ||\n nodeInfo.type.includes(\"VisionOutput\") ||\n nodeInfo.type.includes(\"ImageOutput\") ||\n nodeInfo.type.includes(\"LoadImage\") ||\n nodeInfo.type.includes(\"Display\")\n )) {\n if (nodeInfo.widgets) {\n for (const widget of nodeInfo.widgets) {\n if (widget.name && (\n widget.name.toLowerCase().includes(\"image\") ||\n widget.name.toLowerCase().includes(\"filename\") ||\n widget.name.toLowerCase().includes(\"file\")\n )) {\n return widget.value;\n }\n }\n }\n\n if (nodeInfo.properties && nodeInfo.properties.img) {\n const img = nodeInfo.properties.img;\n return {\n width: img.width || \"unknown\",\n height: img.height || \"unknown\",\n src: img.src ? img.src.split(/[\\/\\\\]/).pop() : \"Preview available\"\n };\n }\n\n if (nodeInfo.outputs) {\n for (const output of nodeInfo.outputs) {\n if (output.links && output.links.length > 0) {\n return \"Image connected to \" + output.links.length + \" node(s)\";\n }\n }\n }\n\n return \"Image node\";\n }\n return null;\n }\n\n /**\n * Get text box content from a node.\n * @param nodeInfo \n * @returns \n */\n getTextBoxContent(nodeInfo: any): string | null {\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n if (nodeInfo.type && nodeInfo.type.includes(\"CLIPTextEncode\")) {\n for (const widget of nodeInfo.widgets) {\n if (widget.name === \"text\" && typeof widget.value === 'string') {\n return widget.value;\n }\n }\n }\n\n for (const widget of nodeInfo.widgets) {\n if ((widget.name.toLowerCase().includes(\"prompt\") ||\n widget.name.toLowerCase().includes(\"conditioning\")) &&\n typeof widget.value === 'string' && widget.value.length > 0) {\n return widget.value;\n }\n }\n\n for (const widget of nodeInfo.widgets) {\n if ((widget.type === 'text' || widget.type === 'textarea' ||\n widget.type === 'string' || widget.name.toLowerCase().includes('text')) &&\n typeof widget.value === 'string' && widget.value.length > 0) {\n\n // Show full text without truncation\n return widget.value;\n }\n }\n }\n return null;\n }\n\n /**\n * Get important node parameters based on node type.\n * @param nodeInfo \n * @returns \n */\n getImportantNodeParameters(nodeInfo: any): any[] {\n const parameters: any[] = [];\n\n // For complex nodes that have many parameters, show ALL widgets\n const nodeType = nodeInfo.type ? nodeInfo.type.toLowerCase() : '';\n const isSaveNode = nodeType.includes('save') &&\n !nodeType.includes('checkpoint') &&\n !nodeType.includes('model') &&\n !nodeType.includes('preview');\n\n const showAllWidgets = nodeInfo.type && (\n nodeType.includes('ksampler') ||\n nodeType.includes('sampler') ||\n nodeType.includes('k_samplers') ||\n nodeType.includes('checkpoint') ||\n nodeType.includes('model') ||\n nodeType.includes('lora') ||\n nodeType.includes('controlnet') ||\n nodeType.includes('advanced') ||\n nodeType.includes('detailer') ||\n nodeType.includes('inpaint') ||\n nodeType.includes('upscale') ||\n nodeType.includes('clip') ||\n nodeType.includes('text') ||\n nodeType.includes('encode')\n ) && !isSaveNode;\n\n if (showAllWidgets) {\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n for (const widget of nodeInfo.widgets) {\n // Show all widgets for complex nodes, but filter out some duplicates\n if (widget.name && widget.name !== '') {\n const widgetName = widget.name.toLowerCase();\n\n // Skip some generic widget names that might be duplicates\n if (widgetName.includes('title') ||\n widgetName === 'node' ||\n widgetName === 'id' ||\n widgetName === 'type' ||\n widgetName === 'mode') {\n continue;\n }\n\n parameters.push({\n label: widget.name,\n value: this.formatWidgetValue(widget.value)\n });\n }\n }\n }\n return parameters;\n }\n\n // Special handling for Save nodes - only show essential parameters\n let importantParams: string[];\n if (isSaveNode) {\n importantParams = [\n 'filename_prefix', 'filename', 'directory', 'path',\n 'format', 'quality', 'extension'\n ];\n } else {\n // For other nodes, use the filtered list but avoid duplicates with specific extractions\n importantParams = [\n 'seed', 'steps', 'cfg', 'scale', 'sampler', 'scheduler',\n 'positive', 'negative', 'width', 'height', 'denoise', 'strength',\n 'noise', 'count', 'batch', 'size', 'phase', 'color', 'intensity',\n // KSampler specific parameters (for other samplers)\n 'control_after_generate', 'control', 'after', 'generate',\n 'start_at_step', 'end_at_step', 'start', 'end',\n 'return_with_leftover_noise', 'leftover', 'noise_return',\n // Additional common parameters (but avoid duplicates with specific extractors)\n 'model', 'vae', 'clip', 'lora', 'checkpoint',\n 'latent', 'image', 'mask', 'filename', 'directory',\n 'prompt', 'conditioning', 'filename_prefix',\n // New detection vocabulary\n 'resolution', 'num_chunks', 'seconds', 'aspect_ratio',\n 'style_type', 'background', 'n', 'human', 'raw', 'guidance',\n 'skip_preprocessing', 'movement_amplitude', 'animation',\n 'material_type', 'b1', 'b2', 's1', 's2', 'type', 'channel',\n 'sigma', 'rho',\n // Additional detection vocabulary\n 'alpha', 'base_shift', 'shift', 'stretch', 'terminal',\n 'spacing', 'style', 'eta', 'norm_threshold', 'momentum',\n 'hypernetwork_name', 'reuse_threshold', 'verbose', 'layers',\n 'set_cond_area', 'audioui',\n // Camera and 3D parameters\n 'camera_pose', 'fx', 'cx', 'fy', 'cy'\n // Note: 'text' and 'string' removed to avoid duplication with getTextBoxContent\n ];\n }\n\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n for (const widget of nodeInfo.widgets) {\n const paramName = widget.name.toLowerCase();\n if (importantParams.some(param => paramName.includes(param))) {\n parameters.push({\n label: widget.name,\n value: this.formatWidgetValue(widget.value)\n });\n }\n }\n }\n\n return parameters;\n }\n\n /**\n * Format widget value for display.\n * @param value \n * @returns \n */\n formatWidgetValue(value: any): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (typeof value === \"string\") {\n return value; // Show full text without truncation\n }\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? value.toString() : value.toFixed(3);\n }\n if (typeof value === \"boolean\") return value.toString();\n if (Array.isArray(value)) {\n return `Array(${value.length})`;\n }\n if (typeof value === \"object\") {\n return \"Object\";\n }\n return String(value);\n }\n\n /**\n * Inject CSS styles by loading external stylesheet.\n */\n injectStyles(): void {\n if (!document.getElementById('magnify-info-panel-styles-v2')) {\n const link = document.createElement('link');\n link.id = 'magnify-info-panel-styles-v2';\n link.rel = 'stylesheet';\n link.type = 'text/css';\n link.href = 'extensions/comfyui-magnifyglass/info-panel.css';\n document.head.appendChild(link);\n }\n }\n\n cleanup(): void {\n if (this.elements.panel && this.elements.panel.parentNode) {\n this.elements.panel.parentNode.removeChild(this.elements.panel);\n }\n if (this.elements.controls && this.elements.controls.parentNode) {\n this.elements.controls.parentNode.removeChild(this.elements.controls);\n }\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"UIManager.js","sources":["../../src/info-panel/UIManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel UI Manager (TypeScript)\n * \n * Complete UI Manager extracted from magnify_info_panel.js\n * Handles all DOM manipulation and UI creation for the info panel.\n */\n\n// import { Z_INDEX } from '../shared/constants'; // Not used in original code? Kept for reference if needed\nimport { StateManager } from './StateManager';\nimport { Icons } from '../shared/icons';\n\ninterface InfoPanelElements {\n panel: HTMLDivElement | null;\n header: HTMLDivElement | null;\n content: HTMLDivElement | null;\n controls: HTMLDivElement | null;\n}\n\n/**\n * UI Manager class.\n * Handles all DOM manipulation and UI creation.\n */\nexport class UIManager {\n stateManager: StateManager;\n elements: InfoPanelElements;\n\n constructor(stateManager: StateManager) {\n this.stateManager = stateManager;\n this.elements = {\n panel: null,\n header: null,\n content: null,\n controls: null\n };\n\n this.createPanel();\n this.injectStyles();\n }\n\n /**\n * Create the main panel and its components.\n */\n createPanel(): void {\n // Main panel container\n this.elements.panel = document.createElement(\"div\");\n this.elements.panel.id = \"comfyui-magnify-info-panel-pro-v2\";\n this.elements.panel.className = `magnify-info-panel theme-${this.stateManager.state.currentTheme}`;\n\n // Header\n this.elements.header = document.createElement(\"div\");\n this.elements.header.className = \"panel-header\";\n this.elements.header.innerHTML = `\n
\n
${Icons.magnifyGlass}
\n
Inspector
\n
Real-time analysis
\n
\n
\n \n
\n `;\n\n // Content container\n this.elements.content = document.createElement(\"div\");\n this.elements.content.className = \"panel-content\";\n\n this.elements.panel.appendChild(this.elements.header);\n this.elements.panel.appendChild(this.elements.content);\n\n this.applyStyles();\n document.body.appendChild(this.elements.panel);\n\n // Add click event delegation for panel elements\n this.elements.panel.addEventListener('click', (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n\n // Handle minimize button\n const minimizeBtn = target.closest('[data-action=\"minimize\"]');\n if (minimizeBtn) {\n this.stateManager.state.isPanelMinimized = !this.stateManager.state.isPanelMinimized;\n this.updateMinimizedState();\n return;\n }\n\n // Handle section header clicks for expand/collapse\n const sectionHeader = target.closest('.section-header') as HTMLElement;\n if (sectionHeader) {\n const sectionId = sectionHeader.getAttribute('data-section');\n // Skip node section (always expanded)\n if (sectionId && sectionId !== 'node') {\n const isExpanded = this.stateManager.state.expandedSections.has(sectionId);\n if (isExpanded) {\n this.stateManager.state.expandedSections.delete(sectionId);\n } else {\n this.stateManager.state.expandedSections.add(sectionId);\n }\n\n // Update visual state\n sectionHeader.classList.toggle('expanded', !isExpanded);\n const sectionContent = sectionHeader.nextElementSibling as HTMLElement;\n if (sectionContent && sectionContent.classList.contains('section-content')) {\n sectionContent.classList.toggle('expanded', !isExpanded);\n }\n }\n }\n });\n\n // Create floating controls after panel is in DOM\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.ShowHoveringControls\"]) {\n this.createFloatingControls();\n // Update control states immediately after creation\n this.updateControlStates();\n }\n }\n\n /**\n * Create floating control buttons.\n */\n createFloatingControls(): void {\n this.elements.controls = document.createElement(\"div\");\n this.elements.controls.className = \"floating-controls vertical-layout\"; // Default to vertical\n\n /**\n * BUTTON MAPPING DOCUMENTATION:\n * =============================\n * Button 1 (Unlock Icon): data-action=\"pin\" → controls isPanelPinned\n * - Unpinned (default): Panel follows magnify glass\n * - Pinned: Panel stays at fixed position (unlocked from following glass)\n * \n * Button 2 (Pin Icon): data-action=\"lock\" → controls isPanelLocked\n * - Unlocked (default): Panel can be dragged\n * - Locked: Panel cannot be dragged (pinned in place)\n * \n * NOTE: The data-action names are counterintuitive but kept for backwards compatibility.\n * The ICONS correctly represent the functionality:\n * - Unlock icon = unlock from following glass\n * - Pin icon = pin position (prevent drag)\n */\n this.elements.controls.innerHTML = `\n \n \n \n \n `;\n\n // Insert before the panel in the document body, not as a child\n document.body.appendChild(this.elements.controls);\n\n // Hide and position off-screen initially - will be shown when magnify glass activates and positions them\n this.elements.controls.style.display = 'none';\n this.elements.controls.style.visibility = 'hidden';\n this.elements.controls.style.left = '-9999px';\n this.elements.controls.style.top = '-9999px';\n\n // Add click event delegation for control buttons\n this.elements.controls.addEventListener('click', (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n const button = target.closest('button[data-action]') as HTMLButtonElement;\n if (!button) return;\n\n const action = button.getAttribute('data-action');\n console.log(`[InfoPanel] Control button clicked: ${action}`);\n\n switch (action) {\n case 'pin':\n this.stateManager.togglePinning();\n this.updatePinnedState();\n this.updateControlStates();\n break;\n case 'lock':\n this.stateManager.state.isPanelLocked = !this.stateManager.state.isPanelLocked;\n this.updateControlStates();\n if (this.elements.panel) {\n this.elements.panel.classList.toggle('panel-locked', this.stateManager.state.isPanelLocked);\n }\n break;\n case 'toggle-panel':\n if (this.stateManager.state.isPanelVisible) {\n this.hide();\n } else {\n this.show();\n }\n this.updateControlStates();\n break;\n case 'toggle-glass':\n this.stateManager.state.isGlassPreviewVisible = !this.stateManager.state.isGlassPreviewVisible;\n this.updateControlStates();\n // Toggle glass visibility on the magnify glass\n if (window.comfyUIMagnifyGlass) {\n if (this.stateManager.state.isGlassPreviewVisible) {\n window.comfyUIMagnifyGlass.ui.show();\n } else {\n window.comfyUIMagnifyGlass.ui.hide();\n }\n }\n break;\n }\n });\n\n // Set initial layout based on settings\n const controlsPosition = this.stateManager.state.settings[\"🔍MagnifyGlass.ControlsPosition\"] || \"top-right\";\n this.updateControlsLayout(controlsPosition);\n }\n\n /**\n * Update control button states.\n */\n updateControlStates(): void {\n if (!this.elements.controls) return;\n\n const pinBtn = this.elements.controls.querySelector('[data-action=\"pin\"]') as HTMLButtonElement;\n const lockBtn = this.elements.controls.querySelector('[data-action=\"lock\"]') as HTMLButtonElement;\n const visibilityBtn = this.elements.controls.querySelector('[data-action=\"toggle-panel\"]') as HTMLButtonElement;\n const glassBtn = this.elements.controls.querySelector('[data-action=\"toggle-glass\"]') as HTMLButtonElement;\n\n if (pinBtn) {\n pinBtn.classList.toggle('active', this.stateManager.state.isPanelPinned);\n pinBtn.title = this.stateManager.state.isPanelPinned ? \"Lock Panel\" : \"Unlock Panel\";\n pinBtn.innerHTML = this.stateManager.state.isPanelPinned ? Icons.lock : Icons.unlock;\n }\n\n if (lockBtn) {\n // Only show/enable lock button when pinned\n lockBtn.style.display = this.stateManager.state.isPanelPinned ? 'flex' : 'none';\n lockBtn.classList.toggle('active', this.stateManager.state.isPanelLocked);\n lockBtn.title = this.stateManager.state.isPanelLocked ? \"Upin Panel Position\" : \"Pin Panel Position\";\n lockBtn.disabled = !this.stateManager.state.isPanelPinned;\n }\n\n if (visibilityBtn) {\n visibilityBtn.classList.toggle('active', this.stateManager.state.isPanelVisible);\n visibilityBtn.title = this.stateManager.state.isPanelVisible ? \"Show Panel\" : \"Hide Panel\";\n }\n\n if (glassBtn) {\n glassBtn.classList.toggle('active', this.stateManager.state.isGlassPreviewVisible);\n glassBtn.title = this.stateManager.state.isGlassPreviewVisible ? \"Hide Glass Preview\" : \"Show Glass Preview\";\n }\n }\n\n /**\n * Update controls layout based on position setting.\n * @param position \n */\n updateControlsLayout(position: string): void {\n if (!this.elements.controls) return;\n\n // Remove existing layout classes\n this.elements.controls.classList.remove('horizontal-layout', 'vertical-layout');\n\n // Add appropriate layout class\n if (['top', 'bottom'].includes(position)) {\n this.elements.controls.classList.add('horizontal-layout');\n } else {\n this.elements.controls.classList.add('vertical-layout');\n }\n }\n\n /**\n * Apply current styles to elements.\n */\n applyStyles(): void {\n if (!this.elements.panel) return;\n\n const settings = this.stateManager.state.settings;\n\n // Apply custom text color via CSS variable if provided\n const textColor = settings[\"🔍MagnifyGlass.InfoPanelTextColor\"];\n if (textColor) {\n // Ensure color has # symbol\n const normalizedTextColor = textColor.startsWith('#') ? textColor : `#${textColor}`;\n this.elements.panel.style.setProperty('--info-panel-text-color', normalizedTextColor);\n }\n\n // Apply custom accent color via CSS variable if provided\n const accentColor = settings[\"🔍MagnifyGlass.InfoPanelAccentColor\"];\n if (accentColor) {\n // Ensure color has # symbol\n const normalizedAccentColor = accentColor.startsWith('#') ? accentColor : `#${accentColor}`;\n this.elements.panel.style.setProperty('--info-panel-accent-color', normalizedAccentColor);\n }\n\n // Apply opacity setting if panel is visible (convert percentage to decimal)\n if (this.stateManager.state.isPanelVisible) {\n const opacityPercent = settings[\"🔍MagnifyGlass.InfoPanelOpacity\"];\n this.elements.panel.style.opacity = (opacityPercent / 100).toString();\n }\n\n this.elements.panel.style.cssText = `\n position: absolute;\n width: ${settings[\"🔍MagnifyGlass.InfoPanelWidth\"]}px;\n max-height: ${settings[\"🔍MagnifyGlass.InfoPanelMaxHeight\"]}px;\n z-index: 99999;\n display: none;\n opacity: 0;\n transform: translateY(-10px);\n transition: ${settings[\"🔍MagnifyGlass.InfoPanelAnimations\"] ? 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' : 'none'};\n pointer-events: auto;\n user-select: none;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n ${textColor ? `--info-panel-text-color: ${textColor.startsWith('#') ? textColor : `#${textColor}`};` : ''}\n ${accentColor ? `--info-panel-accent-color: ${accentColor.startsWith('#') ? accentColor : `#${accentColor}`};` : ''}\n `;\n }\n\n /**\n * Show the panel.\n */\n show(): void {\n if (!this.elements.panel) return;\n\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"]) {\n this.elements.panel.style.display = \"block\";\n // Controls are shown by positionFloatingControls() after proper positioning\n // Apply user's opacity setting when showing (convert percentage to decimal)\n const opacityPercent = this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelOpacity\"];\n this.elements.panel.style.opacity = (opacityPercent / 100).toString();\n // Trigger reflow\n this.elements.panel.offsetHeight;\n this.elements.panel.classList.add('visible');\n this.stateManager.state.isPanelVisible = true;\n }\n }\n\n /**\n * Hide the panel.\n */\n hide(): void {\n if (!this.elements.panel) return;\n\n this.elements.panel.classList.remove('visible');\n setTimeout(() => {\n if (!this.stateManager.state.isPanelVisible && this.elements.panel) {\n this.elements.panel.style.display = \"none\";\n if (this.elements.controls) {\n this.elements.controls.style.display = \"none\";\n }\n }\n }, this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelAnimations\"] ? 300 : 0);\n this.stateManager.state.isPanelVisible = false;\n }\n\n /**\n * Update minimized state.\n */\n updateMinimizedState(): void {\n if (!this.elements.panel || !this.elements.header) return;\n\n this.elements.panel.classList.toggle('panel-minimized', this.stateManager.state.isPanelMinimized);\n const minimizeBtn = this.elements.header.querySelector('.minimize-btn');\n if (minimizeBtn) {\n minimizeBtn.textContent = this.stateManager.state.isPanelMinimized ? '+' : '−';\n (minimizeBtn as HTMLElement).title = this.stateManager.state.isPanelMinimized ? 'Expand Panel' : 'Minimize Panel';\n }\n }\n\n /**\n * Update pinned state.\n */\n updatePinnedState(): void {\n if (!this.elements.panel) return;\n\n this.elements.panel.classList.toggle('panel-pinned', this.stateManager.state.isPanelPinned);\n this.elements.panel.classList.toggle('panel-locked', this.stateManager.state.isPanelLocked);\n this.updateControlStates();\n }\n\n /**\n * Update theme class.\n * @param newTheme \n */\n updateTheme(newTheme: string): void {\n if (this.elements.panel) {\n this.elements.panel.className = this.elements.panel.className.replace(/theme-\\w+/, `theme-${newTheme.toLowerCase()}`);\n }\n }\n\n /**\n * Display information in the panel.\n * @param info \n */\n displayInfo(info: any): void {\n const sections = this.buildSections(info);\n this.renderSections(sections);\n this.updateSectionStates();\n this.updateHeaderSubtitle(info);\n }\n\n /**\n * Build section data from info object.\n * @param info \n * @returns \n */\n buildSections(info: any): any[] {\n const sections: any[] = [];\n const settings = this.stateManager.state.settings;\n\n // Inspector section\n if (settings[\"🔍MagnifyGlass.ShowInspectorTab\"]) {\n const inspectorContent = [\n { label: 'Cursor Canvas', value: `(${Math.round(info.cursor.canvas.x)}, ${Math.round(info.cursor.canvas.y)})` },\n { label: 'Canvas Scale', value: `${(info.canvas.scale * 100).toFixed(1)}%` },\n { label: 'Magnifier Zoom', value: `${info.magnifier.zoomFactor}×` }\n ];\n\n sections.push({\n id: 'inspector',\n icon: Icons.info,\n title: 'Inspector',\n content: inspectorContent\n });\n }\n\n // Media section\n if (info.media) {\n const mediaContent = [\n { label: 'Type', value: info.media.tagName },\n { label: 'Source', value: info.media.src }\n ];\n\n if (info.media.naturalSize) {\n mediaContent.push({ label: 'Natural', value: info.media.naturalSize });\n }\n\n sections.push({\n id: 'media',\n icon: Icons.camera,\n title: 'Media',\n badge: info.media.tagName,\n content: mediaContent\n });\n }\n\n // Node Details section\n if (info.hoveredNode) {\n const nodeContent = [\n { label: 'Title', value: info.hoveredNode.title }\n ];\n\n // Check if this is a complex node that shows all widgets\n const nodeType = info.hoveredNode.type ? info.hoveredNode.type.toLowerCase() : '';\n const isSaveNode = nodeType.includes('save') &&\n !nodeType.includes('checkpoint') &&\n !nodeType.includes('model') &&\n !nodeType.includes('preview');\n\n const showAllWidgets = info.hoveredNode.type && (\n nodeType.includes('ksampler') ||\n nodeType.includes('sampler') ||\n nodeType.includes('k_samplers') ||\n nodeType.includes('checkpoint') ||\n nodeType.includes('model') ||\n nodeType.includes('lora') ||\n nodeType.includes('controlnet') ||\n nodeType.includes('advanced') ||\n nodeType.includes('detailer') ||\n nodeType.includes('inpaint') ||\n nodeType.includes('upscale') ||\n nodeType.includes('clip') ||\n nodeType.includes('text') ||\n nodeType.includes('encode')\n ) && !isSaveNode;\n\n if (!showAllWidgets) {\n // Add specific extractions for simple nodes\n const checkpointInfo = this.getCheckpointInfo(info.hoveredNode);\n if (checkpointInfo) {\n nodeContent.push({ label: 'Model', value: checkpointInfo });\n }\n\n const imageInfo = this.getImageInfo(info.hoveredNode);\n if (imageInfo) {\n if (typeof imageInfo === 'string') {\n nodeContent.push({ label: 'Image', value: imageInfo });\n } else if (typeof imageInfo === 'object' && imageInfo !== null) {\n const imgInfoAny = imageInfo as any;\n nodeContent.push({ label: 'Image Size', value: `${imgInfoAny.width}×${imgInfoAny.height}` });\n if (imgInfoAny.src) {\n nodeContent.push({ label: 'Image Source', value: imgInfoAny.src });\n }\n }\n }\n\n const textBoxContent = this.getTextBoxContent(info.hoveredNode);\n if (textBoxContent) {\n nodeContent.push({ label: 'Text', value: textBoxContent });\n }\n }\n\n const importantParameters = this.getImportantNodeParameters(info.hoveredNode);\n nodeContent.push(...importantParameters);\n\n sections.push({\n id: 'node',\n icon: Icons.box,\n title: 'Node',\n badge: info.hoveredNode.type,\n content: nodeContent\n });\n }\n\n return sections;\n }\n\n /**\n * Render sections to the panel.\n * @param sections \n */\n renderSections(sections: any[]): void {\n if (!this.elements.content) return;\n\n if (sections.length === 0) {\n this.elements.content.innerHTML = `\n
\n
${Icons.mapPin}
\n
Empty canvas area
\n
\n `;\n return;\n }\n\n this.elements.content.innerHTML = sections.map(section => `\n
\n
\n ${section.icon}\n ${section.title}\n ${section.badge ? `${section.badge}` : ''}\n ${section.id !== 'node' ? `${Icons.chevronRight}` : ''}\n
\n
\n
\n ${section.content.map((item: any) => {\n const value = this.formatValue(item.value, item.label);\n const valueClass = this.getValueClass(item.value);\n const valueAttributes = this.getValueAttributes(item.value);\n return `\n
\n ${item.label}\n ${value}\n
`;\n }).join('')}\n
\n
\n
\n `).join('');\n }\n\n /**\n * Update section expansion states.\n */\n updateSectionStates(): void {\n if (!this.elements.content) return;\n\n const sections = this.elements.content.querySelectorAll('.info-section');\n sections.forEach(section => {\n const sectionId = (section as HTMLElement).dataset.section;\n if (!sectionId) return;\n\n const header = section.querySelector('.section-header');\n const content = section.querySelector('.section-content');\n\n if (header && content) {\n if (this.stateManager.state.expandedSections.has(sectionId)) {\n header.classList.add('expanded');\n content.classList.add('expanded');\n } else {\n header.classList.remove('expanded');\n content.classList.remove('expanded');\n }\n }\n });\n }\n\n /**\n * Format a value for display.\n * @param value \n * @param label\n * @returns \n */\n formatValue(value: any, label?: string): string {\n if (value === null || value === undefined) return '';\n\n const str = String(value);\n\n // Show full text for prompts, text content, model names, and file paths\n if (label && (\n label.toLowerCase().includes('text') ||\n label.toLowerCase().includes('prompt') ||\n label.toLowerCase().includes('model') ||\n label.toLowerCase().includes('file') ||\n label.toLowerCase().includes('conditioning') ||\n label.toLowerCase().includes('positive') ||\n label.toLowerCase().includes('negative')\n )) {\n return str;\n }\n\n // Show full text for very long values (no truncation)\n return str;\n }\n\n /**\n * Get value class for styling.\n * @param value \n * @returns \n */\n getValueClass(value: any): string {\n if (!value) return '';\n\n const str = String(value);\n let classes: string[] = [];\n\n // Mark text that might benefit from special styling for readability\n if (str.length > 100) {\n classes.push('long-text');\n }\n\n return classes.join(' ');\n }\n\n /**\n * Get value attributes.\n * @param value \n * @returns \n */\n getValueAttributes(value: any): string {\n // Since we show full text now, we don't need title attributes for long text\n // Only add title for very long text that might benefit from tooltips\n if (!value) return '';\n\n const str = String(value);\n if (str.length > 500) { // Only for extremely long text\n return `title=\"${str.replace(/\"/g, '"')}\"`;\n }\n\n return '';\n }\n\n /**\n * Update header subtitle.\n * @param info \n */\n updateHeaderSubtitle(info: any): void {\n if (!this.elements.header) return;\n\n const subtitleElement = this.elements.header.querySelector('.header-subtitle') as HTMLElement;\n if (subtitleElement) {\n const accentColor = this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelAccentColor\"];\n if (info.hoveredNode) {\n subtitleElement.textContent = `Analyzing: ${info.hoveredNode.title}`;\n subtitleElement.style.color = accentColor || '';\n } else if (info.media) {\n subtitleElement.textContent = `Media: ${info.media.tagName}`;\n subtitleElement.style.color = accentColor || '';\n } else if (info.connection) {\n subtitleElement.textContent = `Connection: ${info.connection.type}`;\n subtitleElement.style.color = accentColor || '';\n } else {\n subtitleElement.textContent = 'Real-time analysis';\n subtitleElement.style.color = '';\n }\n }\n }\n\n // ============== Helper methods for node information extraction ==============\n\n /**\n * Get checkpoint/model info from a node.\n * @param nodeInfo \n * @returns \n */\n getCheckpointInfo(nodeInfo: any): string | null {\n if (nodeInfo.type && (\n nodeInfo.type.includes(\"CheckpointLoader\") ||\n nodeInfo.type.includes(\"LoadCheckpoint\") ||\n nodeInfo.type.includes(\"ModelLoader\") ||\n nodeInfo.type.includes(\"UNETLoader\") ||\n nodeInfo.type.includes(\"VAELoader\") ||\n nodeInfo.type.includes(\"LoraLoader\")\n )) {\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n for (const widget of nodeInfo.widgets) {\n if (widget.name && (\n widget.name.toLowerCase().includes(\"model\") ||\n widget.name.toLowerCase().includes(\"checkpoint\") ||\n widget.name.toLowerCase().includes(\"ckpt\") ||\n widget.name.toLowerCase().includes(\"lora\") ||\n widget.name.toLowerCase().includes(\"vae\") ||\n widget.name.toLowerCase().includes(\"file\")\n )) {\n const value = String(widget.value);\n const filename = value.split(/[\\/\\\\]/).pop();\n return filename || value;\n }\n }\n }\n }\n return null;\n }\n\n /**\n * Get image info from a node.\n * @param nodeInfo \n * @returns \n */\n getImageInfo(nodeInfo: any): string | object | null {\n if (nodeInfo.type && (\n nodeInfo.type.includes(\"SaveImage\") ||\n nodeInfo.type.includes(\"PreviewImage\") ||\n nodeInfo.type.includes(\"VisionOutput\") ||\n nodeInfo.type.includes(\"ImageOutput\") ||\n nodeInfo.type.includes(\"LoadImage\") ||\n nodeInfo.type.includes(\"Display\")\n )) {\n if (nodeInfo.widgets) {\n for (const widget of nodeInfo.widgets) {\n if (widget.name && (\n widget.name.toLowerCase().includes(\"image\") ||\n widget.name.toLowerCase().includes(\"filename\") ||\n widget.name.toLowerCase().includes(\"file\")\n )) {\n return widget.value;\n }\n }\n }\n\n if (nodeInfo.properties && nodeInfo.properties.img) {\n const img = nodeInfo.properties.img;\n return {\n width: img.width || \"unknown\",\n height: img.height || \"unknown\",\n src: img.src ? img.src.split(/[\\/\\\\]/).pop() : \"Preview available\"\n };\n }\n\n if (nodeInfo.outputs) {\n for (const output of nodeInfo.outputs) {\n if (output.links && output.links.length > 0) {\n return \"Image connected to \" + output.links.length + \" node(s)\";\n }\n }\n }\n\n return \"Image node\";\n }\n return null;\n }\n\n /**\n * Get text box content from a node.\n * @param nodeInfo \n * @returns \n */\n getTextBoxContent(nodeInfo: any): string | null {\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n if (nodeInfo.type && nodeInfo.type.includes(\"CLIPTextEncode\")) {\n for (const widget of nodeInfo.widgets) {\n if (widget.name === \"text\" && typeof widget.value === 'string') {\n return widget.value;\n }\n }\n }\n\n for (const widget of nodeInfo.widgets) {\n if ((widget.name.toLowerCase().includes(\"prompt\") ||\n widget.name.toLowerCase().includes(\"conditioning\")) &&\n typeof widget.value === 'string' && widget.value.length > 0) {\n return widget.value;\n }\n }\n\n for (const widget of nodeInfo.widgets) {\n if ((widget.type === 'text' || widget.type === 'textarea' ||\n widget.type === 'string' || widget.name.toLowerCase().includes('text')) &&\n typeof widget.value === 'string' && widget.value.length > 0) {\n\n // Show full text without truncation\n return widget.value;\n }\n }\n }\n return null;\n }\n\n /**\n * Get important node parameters based on node type.\n * @param nodeInfo \n * @returns \n */\n getImportantNodeParameters(nodeInfo: any): any[] {\n const parameters: any[] = [];\n\n // For complex nodes that have many parameters, show ALL widgets\n const nodeType = nodeInfo.type ? nodeInfo.type.toLowerCase() : '';\n const isSaveNode = nodeType.includes('save') &&\n !nodeType.includes('checkpoint') &&\n !nodeType.includes('model') &&\n !nodeType.includes('preview');\n\n const showAllWidgets = nodeInfo.type && (\n nodeType.includes('ksampler') ||\n nodeType.includes('sampler') ||\n nodeType.includes('k_samplers') ||\n nodeType.includes('checkpoint') ||\n nodeType.includes('model') ||\n nodeType.includes('lora') ||\n nodeType.includes('controlnet') ||\n nodeType.includes('advanced') ||\n nodeType.includes('detailer') ||\n nodeType.includes('inpaint') ||\n nodeType.includes('upscale') ||\n nodeType.includes('clip') ||\n nodeType.includes('text') ||\n nodeType.includes('encode')\n ) && !isSaveNode;\n\n if (showAllWidgets) {\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n for (const widget of nodeInfo.widgets) {\n // Show all widgets for complex nodes, but filter out some duplicates\n if (widget.name && widget.name !== '') {\n const widgetName = widget.name.toLowerCase();\n\n // Skip some generic widget names that might be duplicates\n if (widgetName.includes('title') ||\n widgetName === 'node' ||\n widgetName === 'id' ||\n widgetName === 'type' ||\n widgetName === 'mode') {\n continue;\n }\n\n parameters.push({\n label: widget.name,\n value: this.formatWidgetValue(widget.value)\n });\n }\n }\n }\n return parameters;\n }\n\n // Special handling for Save nodes - only show essential parameters\n let importantParams: string[];\n if (isSaveNode) {\n importantParams = [\n 'filename_prefix', 'filename', 'directory', 'path',\n 'format', 'quality', 'extension'\n ];\n } else {\n // For other nodes, use the filtered list but avoid duplicates with specific extractions\n importantParams = [\n 'seed', 'steps', 'cfg', 'scale', 'sampler', 'scheduler',\n 'positive', 'negative', 'width', 'height', 'denoise', 'strength',\n 'noise', 'count', 'batch', 'size', 'phase', 'color', 'intensity',\n // KSampler specific parameters (for other samplers)\n 'control_after_generate', 'control', 'after', 'generate',\n 'start_at_step', 'end_at_step', 'start', 'end',\n 'return_with_leftover_noise', 'leftover', 'noise_return',\n // Additional common parameters (but avoid duplicates with specific extractors)\n 'model', 'vae', 'clip', 'lora', 'checkpoint',\n 'latent', 'image', 'mask', 'filename', 'directory',\n 'prompt', 'conditioning', 'filename_prefix',\n // New detection vocabulary\n 'resolution', 'num_chunks', 'seconds', 'aspect_ratio',\n 'style_type', 'background', 'n', 'human', 'raw', 'guidance',\n 'skip_preprocessing', 'movement_amplitude', 'animation',\n 'material_type', 'b1', 'b2', 's1', 's2', 'type', 'channel',\n 'sigma', 'rho',\n // Additional detection vocabulary\n 'alpha', 'base_shift', 'shift', 'stretch', 'terminal',\n 'spacing', 'style', 'eta', 'norm_threshold', 'momentum',\n 'hypernetwork_name', 'reuse_threshold', 'verbose', 'layers',\n 'set_cond_area', 'audioui',\n // Camera and 3D parameters\n 'camera_pose', 'fx', 'cx', 'fy', 'cy'\n // Note: 'text' and 'string' removed to avoid duplication with getTextBoxContent\n ];\n }\n\n if (nodeInfo.widgets && nodeInfo.widgets.length > 0) {\n for (const widget of nodeInfo.widgets) {\n const paramName = widget.name.toLowerCase();\n if (importantParams.some(param => paramName.includes(param))) {\n parameters.push({\n label: widget.name,\n value: this.formatWidgetValue(widget.value)\n });\n }\n }\n }\n\n return parameters;\n }\n\n /**\n * Format widget value for display.\n * @param value \n * @returns \n */\n formatWidgetValue(value: any): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (typeof value === \"string\") {\n return value; // Show full text without truncation\n }\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? value.toString() : value.toFixed(3);\n }\n if (typeof value === \"boolean\") return value.toString();\n if (Array.isArray(value)) {\n return `Array(${value.length})`;\n }\n if (typeof value === \"object\") {\n return \"Object\";\n }\n return String(value);\n }\n\n /**\n * Inject CSS styles by loading external stylesheet.\n */\n injectStyles(): void {\n if (!document.getElementById('magnify-info-panel-styles-v2')) {\n const link = document.createElement('link');\n link.id = 'magnify-info-panel-styles-v2';\n link.rel = 'stylesheet';\n link.type = 'text/css';\n link.href = 'extensions/comfyui-magnifyglass/info-panel.css';\n document.head.appendChild(link);\n }\n }\n\n cleanup(): void {\n if (this.elements.panel && this.elements.panel.parentNode) {\n this.elements.panel.parentNode.removeChild(this.elements.panel);\n }\n if (this.elements.controls && this.elements.controls.parentNode) {\n this.elements.controls.parentNode.removeChild(this.elements.controls);\n }\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file