diff --git a/src/info-panel/UIManager.ts b/src/info-panel/UIManager.ts
index 889090f..26cf790 100644
--- a/src/info-panel/UIManager.ts
+++ b/src/info-panel/UIManager.ts
@@ -184,14 +184,30 @@ export class UIManager {
break;
case 'toggle-glass':
this.stateManager.state.isGlassPreviewVisible = !this.stateManager.state.isGlassPreviewVisible;
- this.updateControlStates();
- // Toggle glass visibility on the magnify glass
- if (window.comfyUIMagnifyGlass) {
- if (this.stateManager.state.isGlassPreviewVisible) {
- window.comfyUIMagnifyGlass.ui.show();
- } else {
- window.comfyUIMagnifyGlass.ui.hide();
+
+ // Link Glass Visibility to Panel Pinning
+ if (!this.stateManager.state.isGlassPreviewVisible) {
+ // Glass Hidden -> Enter "Unlocked Mode" (Pinned to Screen, Draggable)
+ if (this.elements.panel) {
+ const rect = this.elements.panel.getBoundingClientRect();
+ this.stateManager.state.pinnedPosition = { x: rect.left, y: rect.top };
}
+ this.stateManager.state.isPanelPinned = true;
+ this.stateManager.state.isPanelLocked = false; // Ensure dragging is allowed
+ } else {
+ // Glass Shown -> Enter "Locked Position" (Follow Glass)
+ this.stateManager.state.isPanelPinned = false;
+ // Note: Dragging is automatically disabled when !isPanelPinned by EventManager logic
+ }
+
+ this.updateControlStates();
+ this.updatePinnedState(); // Update visual class
+
+ // Toggle ONLY the visual visibility (opacity) of the glass preview
+ // The tool remains active so the inspector can track position
+ const magnifyGlass = (window as any).comfyUIMagnifyGlass;
+ if (magnifyGlass && magnifyGlass.ui?.setPreviewVisibility) {
+ magnifyGlass.ui.setPreviewVisibility(this.stateManager.state.isGlassPreviewVisible);
}
break;
}
@@ -215,19 +231,26 @@ export class UIManager {
const lockBtn = this.elements.controls.querySelector('[data-action="lock"]') as HTMLButtonElement;
const visibilityBtn = this.elements.controls.querySelector('[data-action="toggle-panel"]') as HTMLButtonElement;
const glassBtn = this.elements.controls.querySelector('[data-action="toggle-glass"]') as HTMLButtonElement;
+ const isPanelVisible = this.stateManager.state.isPanelVisible;
+ const isGlassVisible = this.stateManager.state.isGlassPreviewVisible;
if (pinBtn) {
pinBtn.classList.toggle('active', this.stateManager.state.isPanelPinned);
pinBtn.title = this.stateManager.state.isPanelPinned ? "Lock Panel" : "Unlock Panel";
pinBtn.innerHTML = this.stateManager.state.isPanelPinned ? Icons.lock : Icons.unlock;
- }
-
- // Hide unlock/pin buttons when panel is not visible
- const isPanelVisible = this.stateManager.state.isPanelVisible;
-
- if (pinBtn) {
// Hide unlock button when panel is hidden
pinBtn.style.display = isPanelVisible ? 'flex' : 'none';
+
+ // Disable unlock (unpin) button if glass is hidden
+ // This prevents entering "Follow Mouse" mode when glass is invisible
+ if (!isGlassVisible) {
+ pinBtn.disabled = true;
+ pinBtn.style.opacity = '0.5';
+ pinBtn.title = "Cannot unlock panel from screen when glass preview is hidden";
+ } else {
+ pinBtn.disabled = false;
+ pinBtn.style.opacity = '';
+ }
}
if (lockBtn) {
@@ -243,11 +266,24 @@ export class UIManager {
// Active means "Panel is Visible"
visibilityBtn.classList.toggle('active', isPanelVisible);
visibilityBtn.title = isPanelVisible ? "Hide Panel" : "Show Panel";
+
+ // Disable hide button if glass is hidden (prevent hiding everything)
+ if (!isGlassVisible) {
+ visibilityBtn.disabled = true;
+ visibilityBtn.style.opacity = '0.5';
+ visibilityBtn.title = "Cannot hide panel when glass preview is hidden";
+ } else {
+ visibilityBtn.disabled = false;
+ visibilityBtn.style.opacity = '';
+ }
}
if (glassBtn) {
- glassBtn.classList.toggle('active', this.stateManager.state.isGlassPreviewVisible);
- glassBtn.title = this.stateManager.state.isGlassPreviewVisible ? "Hide Glass Preview" : "Show Glass Preview";
+ glassBtn.classList.toggle('active', isGlassVisible);
+ glassBtn.title = isGlassVisible ? "Hide Glass Preview" : "Show Glass Preview";
+
+ // Only show glass toggle button if the inspector panel is visible
+ glassBtn.style.display = isPanelVisible ? 'flex' : 'none';
}
}
diff --git a/src/magnify-glass/UiManager.ts b/src/magnify-glass/UiManager.ts
index ebeb2b0..f8ec27f 100644
--- a/src/magnify-glass/UiManager.ts
+++ b/src/magnify-glass/UiManager.ts
@@ -116,19 +116,33 @@ export class UiManager {
*/
show(): void {
if (this.glassDiv) {
- this.glassDiv.style.display = "block";
+ this.glassDiv.style.display = 'block';
+ // We do NOT modify opacity here, allowing it to be controlled independently
}
if (this.config.debugMode && this.debugCanvas) {
this.debugCanvas.style.display = "block";
}
}
+ /**
+ * Set the visual visibility of the glass preview (opacity).
+ * This allows the tool to remain "Active" (tracking mouse) but invisible,
+ * so that the Inspector Panel can be used in "Inspector Only" mode.
+ */
+ setPreviewVisibility(visible: boolean): void {
+ if (this.glassDiv) {
+ this.glassDiv.style.opacity = visible ? '1' : '0';
+ // Disable pointer events when hidden to be safe, though usually they are none anyway
+ // this.glassDiv.style.pointerEvents = visible ? 'none' : 'none';
+ }
+ }
+
/**
* Hide the magnifying glass.
*/
hide(): void {
if (this.glassDiv) {
- this.glassDiv.style.display = "none";
+ this.glassDiv.style.display = 'none';
}
if (this.config.debugMode && this.debugCanvas) {
this.debugCanvas.style.display = "none";
diff --git a/web/info-panel/UIManager.js b/web/info-panel/UIManager.js
index c6aec0a..fd535a2 100644
--- a/web/info-panel/UIManager.js
+++ b/web/info-panel/UIManager.js
@@ -90,6 +90,7 @@ class UIManager {
this.elements.controls.style.left = "-9999px";
this.elements.controls.style.top = "-9999px";
this.elements.controls.addEventListener("click", (e) => {
+ var _a;
const target = e.target;
const button = target.closest("button[data-action]");
if (!button) return;
@@ -118,13 +119,21 @@ class UIManager {
break;
case "toggle-glass":
this.stateManager.state.isGlassPreviewVisible = !this.stateManager.state.isGlassPreviewVisible;
- this.updateControlStates();
- if (window.comfyUIMagnifyGlass) {
- if (this.stateManager.state.isGlassPreviewVisible) {
- window.comfyUIMagnifyGlass.ui.show();
- } else {
- window.comfyUIMagnifyGlass.ui.hide();
+ if (!this.stateManager.state.isGlassPreviewVisible) {
+ if (this.elements.panel) {
+ const rect = this.elements.panel.getBoundingClientRect();
+ this.stateManager.state.pinnedPosition = { x: rect.left, y: rect.top };
}
+ this.stateManager.state.isPanelPinned = true;
+ this.stateManager.state.isPanelLocked = false;
+ } else {
+ this.stateManager.state.isPanelPinned = false;
+ }
+ this.updateControlStates();
+ this.updatePinnedState();
+ const magnifyGlass = window.comfyUIMagnifyGlass;
+ if (magnifyGlass && ((_a = magnifyGlass.ui) == null ? void 0 : _a.setPreviewVisibility)) {
+ magnifyGlass.ui.setPreviewVisibility(this.stateManager.state.isGlassPreviewVisible);
}
break;
}
@@ -142,14 +151,21 @@ class UIManager {
const lockBtn = this.elements.controls.querySelector('[data-action="lock"]');
const visibilityBtn = this.elements.controls.querySelector('[data-action="toggle-panel"]');
const glassBtn = this.elements.controls.querySelector('[data-action="toggle-glass"]');
+ const isPanelVisible = this.stateManager.state.isPanelVisible;
+ const isGlassVisible = this.stateManager.state.isGlassPreviewVisible;
if (pinBtn) {
pinBtn.classList.toggle("active", this.stateManager.state.isPanelPinned);
pinBtn.title = this.stateManager.state.isPanelPinned ? "Lock Panel" : "Unlock Panel";
pinBtn.innerHTML = this.stateManager.state.isPanelPinned ? Icons.lock : Icons.unlock;
- }
- const isPanelVisible = this.stateManager.state.isPanelVisible;
- if (pinBtn) {
pinBtn.style.display = isPanelVisible ? "flex" : "none";
+ if (!isGlassVisible) {
+ pinBtn.disabled = true;
+ pinBtn.style.opacity = "0.5";
+ pinBtn.title = "Cannot unlock panel from screen when glass preview is hidden";
+ } else {
+ pinBtn.disabled = false;
+ pinBtn.style.opacity = "";
+ }
}
if (lockBtn) {
const showLockBtn = isPanelVisible && this.stateManager.state.isPanelPinned;
@@ -161,10 +177,19 @@ class UIManager {
if (visibilityBtn) {
visibilityBtn.classList.toggle("active", isPanelVisible);
visibilityBtn.title = isPanelVisible ? "Hide Panel" : "Show Panel";
+ if (!isGlassVisible) {
+ visibilityBtn.disabled = true;
+ visibilityBtn.style.opacity = "0.5";
+ visibilityBtn.title = "Cannot hide panel when glass preview is hidden";
+ } else {
+ visibilityBtn.disabled = false;
+ visibilityBtn.style.opacity = "";
+ }
}
if (glassBtn) {
- glassBtn.classList.toggle("active", this.stateManager.state.isGlassPreviewVisible);
- glassBtn.title = this.stateManager.state.isGlassPreviewVisible ? "Hide Glass Preview" : "Show Glass Preview";
+ glassBtn.classList.toggle("active", isGlassVisible);
+ glassBtn.title = isGlassVisible ? "Hide Glass Preview" : "Show Glass Preview";
+ glassBtn.style.display = isPanelVisible ? "flex" : "none";
}
}
/**
diff --git a/web/info-panel/UIManager.js.map b/web/info-panel/UIManager.js.map
index 6d93568..1fc1bd6 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 \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 // Initial state update\n this.updateControlStates();\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 // Hide unlock/pin buttons when panel is not visible\n const isPanelVisible = this.stateManager.state.isPanelVisible;\n\n if (pinBtn) {\n // Hide unlock button when panel is hidden\n pinBtn.style.display = isPanelVisible ? 'flex' : 'none';\n }\n\n if (lockBtn) {\n // Only show pin button when panel is visible AND unlocked from glass\n const showLockBtn = isPanelVisible && this.stateManager.state.isPanelPinned;\n lockBtn.style.display = showLockBtn ? 'flex' : 'none';\n lockBtn.classList.toggle('active', this.stateManager.state.isPanelLocked);\n lockBtn.title = this.stateManager.state.isPanelLocked ? \"Unpin Panel Position\" : \"Pin Panel Position\";\n lockBtn.disabled = !this.stateManager.state.isPanelPinned;\n }\n\n if (visibilityBtn) {\n // Active means \"Panel is Visible\"\n visibilityBtn.classList.toggle('active', isPanelVisible);\n visibilityBtn.title = isPanelVisible ? \"Hide Panel\" : \"Show 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 this.updateControlStates();\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 // Do NOT hide controls when panel is hidden - they should remain visible on glass\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 this.updateControlStates();\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
\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 \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\n // Link Glass Visibility to Panel Pinning\n if (!this.stateManager.state.isGlassPreviewVisible) {\n // Glass Hidden -> Enter \"Unlocked Mode\" (Pinned to Screen, Draggable)\n if (this.elements.panel) {\n const rect = this.elements.panel.getBoundingClientRect();\n this.stateManager.state.pinnedPosition = { x: rect.left, y: rect.top };\n }\n this.stateManager.state.isPanelPinned = true;\n this.stateManager.state.isPanelLocked = false; // Ensure dragging is allowed\n } else {\n // Glass Shown -> Enter \"Locked Position\" (Follow Glass)\n this.stateManager.state.isPanelPinned = false;\n // Note: Dragging is automatically disabled when !isPanelPinned by EventManager logic\n }\n\n this.updateControlStates();\n this.updatePinnedState(); // Update visual class\n\n // Toggle ONLY the visual visibility (opacity) of the glass preview\n // The tool remains active so the inspector can track position\n const magnifyGlass = (window as any).comfyUIMagnifyGlass;\n if (magnifyGlass && magnifyGlass.ui?.setPreviewVisibility) {\n magnifyGlass.ui.setPreviewVisibility(this.stateManager.state.isGlassPreviewVisible);\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 // Initial state update\n this.updateControlStates();\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 const isPanelVisible = this.stateManager.state.isPanelVisible;\n const isGlassVisible = this.stateManager.state.isGlassPreviewVisible;\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 // Hide unlock button when panel is hidden\n pinBtn.style.display = isPanelVisible ? 'flex' : 'none';\n\n // Disable unlock (unpin) button if glass is hidden\n // This prevents entering \"Follow Mouse\" mode when glass is invisible\n if (!isGlassVisible) {\n pinBtn.disabled = true;\n pinBtn.style.opacity = '0.5';\n pinBtn.title = \"Cannot unlock panel from screen when glass preview is hidden\";\n } else {\n pinBtn.disabled = false;\n pinBtn.style.opacity = '';\n }\n }\n\n if (lockBtn) {\n // Only show pin button when panel is visible AND unlocked from glass\n const showLockBtn = isPanelVisible && this.stateManager.state.isPanelPinned;\n lockBtn.style.display = showLockBtn ? 'flex' : 'none';\n lockBtn.classList.toggle('active', this.stateManager.state.isPanelLocked);\n lockBtn.title = this.stateManager.state.isPanelLocked ? \"Unpin Panel Position\" : \"Pin Panel Position\";\n lockBtn.disabled = !this.stateManager.state.isPanelPinned;\n }\n\n if (visibilityBtn) {\n // Active means \"Panel is Visible\"\n visibilityBtn.classList.toggle('active', isPanelVisible);\n visibilityBtn.title = isPanelVisible ? \"Hide Panel\" : \"Show Panel\";\n\n // Disable hide button if glass is hidden (prevent hiding everything)\n if (!isGlassVisible) {\n visibilityBtn.disabled = true;\n visibilityBtn.style.opacity = '0.5';\n visibilityBtn.title = \"Cannot hide panel when glass preview is hidden\";\n } else {\n visibilityBtn.disabled = false;\n visibilityBtn.style.opacity = '';\n }\n }\n\n if (glassBtn) {\n glassBtn.classList.toggle('active', isGlassVisible);\n glassBtn.title = isGlassVisible ? \"Hide Glass Preview\" : \"Show Glass Preview\";\n\n // Only show glass toggle button if the inspector panel is visible\n glassBtn.style.display = isPanelVisible ? 'flex' : 'none';\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 this.updateControlStates();\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 // Do NOT hide controls when panel is hidden - they should remain visible on glass\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 this.updateControlStates();\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
\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
diff --git a/web/magnify-glass/UiManager.js b/web/magnify-glass/UiManager.js
index 1a01627..dd4467f 100644
--- a/web/magnify-glass/UiManager.js
+++ b/web/magnify-glass/UiManager.js
@@ -98,6 +98,16 @@ class UiManager {
this.debugCanvas.style.display = "block";
}
}
+ /**
+ * Set the visual visibility of the glass preview (opacity).
+ * This allows the tool to remain "Active" (tracking mouse) but invisible,
+ * so that the Inspector Panel can be used in "Inspector Only" mode.
+ */
+ setPreviewVisibility(visible) {
+ if (this.glassDiv) {
+ this.glassDiv.style.opacity = visible ? "1" : "0";
+ }
+ }
/**
* Hide the magnifying glass.
*/
diff --git a/web/magnify-glass/UiManager.js.map b/web/magnify-glass/UiManager.js.map
index e852728..80c01e4 100644
--- a/web/magnify-glass/UiManager.js.map
+++ b/web/magnify-glass/UiManager.js.map
@@ -1 +1 @@
-{"version":3,"file":"UiManager.js","sources":["../../src/magnify-glass/UiManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - UiManager (TypeScript)\n * \n * Handles creation and management of DOM elements for the magnifying glass.\n */\n\nimport { Z_INDEX, DEFAULT_PADDING } from '../shared/constants';\nimport { Icons } from '../shared/icons';\nimport type { ConfigManager } from './ConfigManager';\nimport type { MagnifierState } from './MagnifierState';\n\n/**\n * UI Manager class.\n * Manages all DOM elements for the magnifying glass.\n */\nexport class UiManager {\n config: ConfigManager;\n state: MagnifierState;\n glassDiv: HTMLDivElement | null;\n glassCanvas: HTMLCanvasElement | null;\n debugCanvas: HTMLCanvasElement | null;\n debugCtx: CanvasRenderingContext2D | null;\n htmlOverlayContainer: HTMLDivElement | null;\n onToggle: (() => void) | undefined;\n\n constructor(config: ConfigManager, state: MagnifierState, onToggle?: () => void) {\n this.config = config;\n this.state = state;\n this.glassDiv = null;\n this.glassCanvas = null;\n this.debugCanvas = null;\n this.debugCtx = null;\n this.htmlOverlayContainer = null;\n this.onToggle = onToggle;\n }\n\n /**\n * Create all DOM elements for the magnifying glass.\n */\n createElements(): void {\n // Create magnifying glass container\n this.glassDiv = document.createElement(\"div\");\n this.glassDiv.id = \"comfyui-magnify-glass\";\n this.glassDiv.style.cssText = `\n position: absolute;\n width: ${this.config.glassSize}px;\n height: ${this.config.glassSize}px;\n border-radius: ${this.config.glassShape === \"Circle\" ? \"50%\" : \"0px\"};\n border: ${this.config.borderEnabled ? `${this.config.borderWidth}px solid ${this.config.borderColor}` : 'none'};\n overflow: hidden;\n pointer-events: none;\n z-index: ${Z_INDEX.GLASS};\n display: none;\n box-shadow: 0 5px 15px rgba(0,0,0,0.3);\n background-color: rgba(255,255,255,0.1);\n `;\n\n // Create WebGL canvas for the magnifying glass\n this.glassCanvas = document.createElement(\"canvas\");\n this.glassCanvas.width = this.config.glassSize;\n this.glassCanvas.height = this.config.glassSize;\n this.glassCanvas.id = \"comfyui-magnify-canvas\";\n this.glassDiv.appendChild(this.glassCanvas);\n\n // Create HTML overlay container\n this.htmlOverlayContainer = document.createElement(\"div\");\n this.htmlOverlayContainer.id = \"comfyui-magnify-html-overlay\";\n this.htmlOverlayContainer.style.cssText = `\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n overflow: hidden; \n `;\n this.glassDiv.appendChild(this.htmlOverlayContainer);\n\n document.body.appendChild(this.glassDiv);\n\n // Create debug canvas if debug mode is enabled\n if (this.config.debugMode) {\n this.createDebugCanvas();\n }\n\n // Inject menu button\n this.injectMenuButton();\n }\n\n /**\n * Create the debug canvas overlay.\n */\n createDebugCanvas(): void {\n this.debugCanvas = document.createElement(\"canvas\");\n this.debugCanvas.id = \"comfyui-magnify-debug\";\n this.debugCanvas.width = 400;\n this.debugCanvas.height = 350;\n this.debugCanvas.style.cssText = `\n position: fixed;\n top: 10px;\n right: 10px;\n background: rgba(0,0,0,0.7);\n border: 1px solid #fff;\n z-index: ${Z_INDEX.DEBUG};\n pointer-events: none;\n color: white;\n font-family: monospace;\n display: none;\n `;\n document.body.appendChild(this.debugCanvas);\n this.debugCtx = this.debugCanvas.getContext('2d');\n }\n\n /**\n * Show the magnifying glass.\n */\n show(): void {\n if (this.glassDiv) {\n this.glassDiv.style.display = \"block\";\n }\n if (this.config.debugMode && this.debugCanvas) {\n this.debugCanvas.style.display = \"block\";\n }\n }\n\n /**\n * Hide the magnifying glass.\n */\n hide(): void {\n if (this.glassDiv) {\n this.glassDiv.style.display = \"none\";\n }\n if (this.config.debugMode && this.debugCanvas) {\n this.debugCanvas.style.display = \"none\";\n }\n // Clear HTML overlays when hiding\n if (this.htmlOverlayContainer) {\n this.htmlOverlayContainer.innerHTML = '';\n }\n }\n\n /**\n * Position the glass relative to cursor.\n * @param clientX - Client X coordinate\n * @param clientY - Client Y coordinate\n */\n positionGlass(clientX: number, clientY: number): void {\n if (!this.config.followCursor || !this.glassDiv) return;\n\n const glassSize = this.config.glassSize;\n const offsetAmount = DEFAULT_PADDING;\n let newLeft: number;\n let newTop: number;\n\n switch (this.config.glassPosition) {\n case \"Top\":\n newLeft = clientX - (glassSize / 2);\n newTop = clientY - glassSize - offsetAmount;\n break;\n case \"Bottom\":\n newLeft = clientX - (glassSize / 2);\n newTop = clientY + offsetAmount;\n break;\n case \"Left\":\n newLeft = clientX - glassSize - offsetAmount;\n newTop = clientY - (glassSize / 2);\n break;\n case \"Right\":\n newLeft = clientX + offsetAmount;\n newTop = clientY - (glassSize / 2);\n break;\n case \"Top-Left\":\n newLeft = clientX - glassSize - offsetAmount;\n newTop = clientY - glassSize - offsetAmount;\n break;\n case \"Top-Right\":\n newLeft = clientX + offsetAmount;\n newTop = clientY - glassSize - offsetAmount;\n break;\n case \"Bottom-Left\":\n newLeft = clientX - glassSize - offsetAmount;\n newTop = clientY + offsetAmount;\n break;\n case \"Bottom-Right\":\n newLeft = clientX + offsetAmount;\n newTop = clientY + offsetAmount;\n break;\n default:\n newLeft = clientX - (glassSize / 2);\n newTop = clientY + offsetAmount;\n break;\n }\n\n this.glassDiv.style.left = `${newLeft}px`;\n this.glassDiv.style.top = `${newTop}px`;\n\n this.adjustForBoundaries(clientX, clientY);\n }\n\n /**\n * Adjust glass position to stay within viewport boundaries.\n * @param clientX - Client X coordinate\n * @param clientY - Client Y coordinate\n */\n adjustForBoundaries(clientX: number, clientY: number): void {\n if (!this.glassDiv) return;\n\n const glassRect = this.glassDiv.getBoundingClientRect();\n\n // Check right boundary\n if (glassRect.right > window.innerWidth) {\n this.glassDiv.style.left = `${clientX - glassRect.width - DEFAULT_PADDING}px`;\n }\n\n // Check left boundary\n const currentRectLeft = this.glassDiv.getBoundingClientRect();\n if (currentRectLeft.left < 0) {\n this.glassDiv.style.left = \"10px\";\n }\n\n // Check bottom boundary\n if (glassRect.bottom > window.innerHeight) {\n this.glassDiv.style.top = `${clientY - glassRect.height - DEFAULT_PADDING}px`;\n }\n\n // Check top boundary\n const currentRectTop = this.glassDiv.getBoundingClientRect();\n if (currentRectTop.top < 0) {\n this.glassDiv.style.top = \"10px\";\n }\n }\n\n /**\n * Apply current config to UI elements.\n */\n applyStyles(): void {\n if (this.glassDiv) {\n this.glassDiv.style.width = `${this.config.glassSize}px`;\n this.glassDiv.style.height = `${this.config.glassSize}px`;\n this.glassDiv.style.border = this.config.borderEnabled\n ? `${this.config.borderWidth}px solid ${this.config.borderColor}`\n : 'none';\n\n // Reset clip-path and set default border-radius\n this.glassDiv.style.clipPath = 'none';\n this.glassDiv.style.borderRadius = '0px';\n\n switch (this.config.glassShape) {\n case \"Circle\":\n this.glassDiv.style.borderRadius = \"50%\";\n break;\n case \"Square\":\n // borderRadius is already '0px'\n break;\n case \"Rounded Square\":\n this.glassDiv.style.borderRadius = \"20%\";\n break;\n default:\n this.glassDiv.style.borderRadius = \"50%\";\n break;\n }\n }\n\n if (this.glassCanvas) {\n this.glassCanvas.width = this.config.glassSize;\n this.glassCanvas.height = this.config.glassSize;\n }\n\n // Handle debug canvas show/hide\n if (this.config.debugMode) {\n if (!this.debugCanvas) this.createDebugCanvas();\n if (this.state.active && this.debugCanvas) this.debugCanvas.style.display = \"block\";\n } else {\n if (this.debugCanvas) this.debugCanvas.style.display = \"none\";\n }\n }\n\n /**\n * Cleanup DOM elements.\n */\n cleanup(): void {\n if (this.glassDiv) this.glassDiv.remove();\n if (this.debugCanvas) this.debugCanvas.remove();\n // Remove menu button if needed (though hard to track references here without saving it)\n const btn = document.querySelector('.magnify-toggle-btn');\n if (btn) btn.remove();\n }\n\n /**\n * Inject a quick toggle button into the ComfyUI menu.\n */\n /**\n * Inject a quick toggle button into the ComfyUI menu.\n */\n injectMenuButton(): void {\n // Stop after 30 seconds\n const stopTime = Date.now() + 30000;\n\n // Define check function\n const attemptInjection = () => {\n const buttons = Array.from(document.querySelectorAll('button'));\n\n // Try to find an anchor button (Minimap or Links)\n // Priority 1: Minimap\n let anchorBtn = buttons.find(b => {\n const title = (b.title || '').toLowerCase();\n const aria = (b.getAttribute('aria-label') || '').toLowerCase();\n const text = (b.textContent || '').toLowerCase();\n return (title.includes('map') && !title.includes('open')) ||\n (aria.includes('map') && !aria.includes('open')) ||\n (text.includes('map'));\n });\n\n // Priority 2: Link Visibility\n if (!anchorBtn) {\n anchorBtn = buttons.find(b => {\n const title = (b.title || '').toLowerCase();\n const aria = (b.getAttribute('aria-label') || '').toLowerCase();\n return title.includes('link') || aria.includes('link');\n });\n }\n\n if (anchorBtn && anchorBtn.parentElement) {\n // Determine success BEFORE creating elements to allow early return if duplicate\n if (anchorBtn.parentElement.querySelector('.magnify-toggle-btn')) return true;\n\n console.log(\"[MagnifyGlass] Found menu anchor:\", anchorBtn.title || anchorBtn.getAttribute('aria-label') || \"Unknown Button\");\n\n const btn = document.createElement('button');\n // Copy all classes to match theme/library (e.g. p-button, comfy-btn, etc)\n btn.className = anchorBtn.className + ' magnify-toggle-btn';\n\n // Copy key styles that might define shape/size\n const computed = window.getComputedStyle(anchorBtn);\n btn.style.height = computed.height;\n btn.style.minHeight = computed.minHeight;\n // btn.style.width = computed.width; // Don't copy width, let it adapt\n\n btn.title = \"Toggle Magnify Glass\";\n btn.innerHTML = Icons.magnifyGlass;\n\n // Ensure specific display props\n btn.style.display = \"inline-flex\";\n btn.style.alignItems = \"center\";\n btn.style.justifyContent = \"center\";\n btn.style.padding = \"0 8px\";\n btn.style.cursor = \"pointer\";\n btn.style.border = anchorBtn.style.border || computed.border;\n btn.style.borderRadius = anchorBtn.style.borderRadius || computed.borderRadius;\n // Use cssText if available for background/color specific overrides\n if (!btn.style.background) btn.style.background = computed.background;\n if (!btn.style.color) btn.style.color = computed.color;\n\n // Add active state tracking\n btn.addEventListener('click', () => {\n if (this.onToggle) {\n this.onToggle();\n // Try to apply common active classes found in modern UI frameworks\n btn.classList.toggle('active');\n btn.classList.toggle('p-highlight'); // PrimeVue\n btn.classList.toggle('selected');\n }\n });\n\n // Insert logic:\n const isMinimap = (anchorBtn.title || '').toLowerCase().includes('map') ||\n (anchorBtn.getAttribute('aria-label') || '').toLowerCase().includes('map');\n\n if (isMinimap) {\n // Insert AFTER Minimap\n if (anchorBtn.nextSibling) {\n anchorBtn.parentElement.insertBefore(btn, anchorBtn.nextSibling);\n } else {\n anchorBtn.parentElement.appendChild(btn);\n }\n } else {\n // It's likely links, Insert BEFORE\n anchorBtn.parentElement.insertBefore(btn, anchorBtn);\n }\n\n console.log(\"[MagnifyGlass] Menu toggle button injected successfully\");\n return true;\n }\n return false;\n };\n\n // Run immediately first\n if (attemptInjection()) return;\n\n // Then poll rapidly\n const checkForMenu = setInterval(() => {\n if (Date.now() > stopTime) {\n console.warn(\"[MagnifyGlass] Menu injection timed out. Found buttons:\",\n Array.from(document.querySelectorAll('button')).map(b => b.title || b.getAttribute('aria-label') || b.textContent || b.className).slice(0, 5));\n clearInterval(checkForMenu);\n return;\n }\n\n if (attemptInjection()) {\n clearInterval(checkForMenu);\n }\n }, 100);\n }\n}\n"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"UiManager.js","sources":["../../src/magnify-glass/UiManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - UiManager (TypeScript)\n * \n * Handles creation and management of DOM elements for the magnifying glass.\n */\n\nimport { Z_INDEX, DEFAULT_PADDING } from '../shared/constants';\nimport { Icons } from '../shared/icons';\nimport type { ConfigManager } from './ConfigManager';\nimport type { MagnifierState } from './MagnifierState';\n\n/**\n * UI Manager class.\n * Manages all DOM elements for the magnifying glass.\n */\nexport class UiManager {\n config: ConfigManager;\n state: MagnifierState;\n glassDiv: HTMLDivElement | null;\n glassCanvas: HTMLCanvasElement | null;\n debugCanvas: HTMLCanvasElement | null;\n debugCtx: CanvasRenderingContext2D | null;\n htmlOverlayContainer: HTMLDivElement | null;\n onToggle: (() => void) | undefined;\n\n constructor(config: ConfigManager, state: MagnifierState, onToggle?: () => void) {\n this.config = config;\n this.state = state;\n this.glassDiv = null;\n this.glassCanvas = null;\n this.debugCanvas = null;\n this.debugCtx = null;\n this.htmlOverlayContainer = null;\n this.onToggle = onToggle;\n }\n\n /**\n * Create all DOM elements for the magnifying glass.\n */\n createElements(): void {\n // Create magnifying glass container\n this.glassDiv = document.createElement(\"div\");\n this.glassDiv.id = \"comfyui-magnify-glass\";\n this.glassDiv.style.cssText = `\n position: absolute;\n width: ${this.config.glassSize}px;\n height: ${this.config.glassSize}px;\n border-radius: ${this.config.glassShape === \"Circle\" ? \"50%\" : \"0px\"};\n border: ${this.config.borderEnabled ? `${this.config.borderWidth}px solid ${this.config.borderColor}` : 'none'};\n overflow: hidden;\n pointer-events: none;\n z-index: ${Z_INDEX.GLASS};\n display: none;\n box-shadow: 0 5px 15px rgba(0,0,0,0.3);\n background-color: rgba(255,255,255,0.1);\n `;\n\n // Create WebGL canvas for the magnifying glass\n this.glassCanvas = document.createElement(\"canvas\");\n this.glassCanvas.width = this.config.glassSize;\n this.glassCanvas.height = this.config.glassSize;\n this.glassCanvas.id = \"comfyui-magnify-canvas\";\n this.glassDiv.appendChild(this.glassCanvas);\n\n // Create HTML overlay container\n this.htmlOverlayContainer = document.createElement(\"div\");\n this.htmlOverlayContainer.id = \"comfyui-magnify-html-overlay\";\n this.htmlOverlayContainer.style.cssText = `\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n overflow: hidden; \n `;\n this.glassDiv.appendChild(this.htmlOverlayContainer);\n\n document.body.appendChild(this.glassDiv);\n\n // Create debug canvas if debug mode is enabled\n if (this.config.debugMode) {\n this.createDebugCanvas();\n }\n\n // Inject menu button\n this.injectMenuButton();\n }\n\n /**\n * Create the debug canvas overlay.\n */\n createDebugCanvas(): void {\n this.debugCanvas = document.createElement(\"canvas\");\n this.debugCanvas.id = \"comfyui-magnify-debug\";\n this.debugCanvas.width = 400;\n this.debugCanvas.height = 350;\n this.debugCanvas.style.cssText = `\n position: fixed;\n top: 10px;\n right: 10px;\n background: rgba(0,0,0,0.7);\n border: 1px solid #fff;\n z-index: ${Z_INDEX.DEBUG};\n pointer-events: none;\n color: white;\n font-family: monospace;\n display: none;\n `;\n document.body.appendChild(this.debugCanvas);\n this.debugCtx = this.debugCanvas.getContext('2d');\n }\n\n /**\n * Show the magnifying glass.\n */\n show(): void {\n if (this.glassDiv) {\n this.glassDiv.style.display = 'block';\n // We do NOT modify opacity here, allowing it to be controlled independently\n }\n if (this.config.debugMode && this.debugCanvas) {\n this.debugCanvas.style.display = \"block\";\n }\n }\n\n /**\n * Set the visual visibility of the glass preview (opacity).\n * This allows the tool to remain \"Active\" (tracking mouse) but invisible,\n * so that the Inspector Panel can be used in \"Inspector Only\" mode.\n */\n setPreviewVisibility(visible: boolean): void {\n if (this.glassDiv) {\n this.glassDiv.style.opacity = visible ? '1' : '0';\n // Disable pointer events when hidden to be safe, though usually they are none anyway\n // this.glassDiv.style.pointerEvents = visible ? 'none' : 'none'; \n }\n }\n\n /**\n * Hide the magnifying glass.\n */\n hide(): void {\n if (this.glassDiv) {\n this.glassDiv.style.display = 'none';\n }\n if (this.config.debugMode && this.debugCanvas) {\n this.debugCanvas.style.display = \"none\";\n }\n // Clear HTML overlays when hiding\n if (this.htmlOverlayContainer) {\n this.htmlOverlayContainer.innerHTML = '';\n }\n }\n\n /**\n * Position the glass relative to cursor.\n * @param clientX - Client X coordinate\n * @param clientY - Client Y coordinate\n */\n positionGlass(clientX: number, clientY: number): void {\n if (!this.config.followCursor || !this.glassDiv) return;\n\n const glassSize = this.config.glassSize;\n const offsetAmount = DEFAULT_PADDING;\n let newLeft: number;\n let newTop: number;\n\n switch (this.config.glassPosition) {\n case \"Top\":\n newLeft = clientX - (glassSize / 2);\n newTop = clientY - glassSize - offsetAmount;\n break;\n case \"Bottom\":\n newLeft = clientX - (glassSize / 2);\n newTop = clientY + offsetAmount;\n break;\n case \"Left\":\n newLeft = clientX - glassSize - offsetAmount;\n newTop = clientY - (glassSize / 2);\n break;\n case \"Right\":\n newLeft = clientX + offsetAmount;\n newTop = clientY - (glassSize / 2);\n break;\n case \"Top-Left\":\n newLeft = clientX - glassSize - offsetAmount;\n newTop = clientY - glassSize - offsetAmount;\n break;\n case \"Top-Right\":\n newLeft = clientX + offsetAmount;\n newTop = clientY - glassSize - offsetAmount;\n break;\n case \"Bottom-Left\":\n newLeft = clientX - glassSize - offsetAmount;\n newTop = clientY + offsetAmount;\n break;\n case \"Bottom-Right\":\n newLeft = clientX + offsetAmount;\n newTop = clientY + offsetAmount;\n break;\n default:\n newLeft = clientX - (glassSize / 2);\n newTop = clientY + offsetAmount;\n break;\n }\n\n this.glassDiv.style.left = `${newLeft}px`;\n this.glassDiv.style.top = `${newTop}px`;\n\n this.adjustForBoundaries(clientX, clientY);\n }\n\n /**\n * Adjust glass position to stay within viewport boundaries.\n * @param clientX - Client X coordinate\n * @param clientY - Client Y coordinate\n */\n adjustForBoundaries(clientX: number, clientY: number): void {\n if (!this.glassDiv) return;\n\n const glassRect = this.glassDiv.getBoundingClientRect();\n\n // Check right boundary\n if (glassRect.right > window.innerWidth) {\n this.glassDiv.style.left = `${clientX - glassRect.width - DEFAULT_PADDING}px`;\n }\n\n // Check left boundary\n const currentRectLeft = this.glassDiv.getBoundingClientRect();\n if (currentRectLeft.left < 0) {\n this.glassDiv.style.left = \"10px\";\n }\n\n // Check bottom boundary\n if (glassRect.bottom > window.innerHeight) {\n this.glassDiv.style.top = `${clientY - glassRect.height - DEFAULT_PADDING}px`;\n }\n\n // Check top boundary\n const currentRectTop = this.glassDiv.getBoundingClientRect();\n if (currentRectTop.top < 0) {\n this.glassDiv.style.top = \"10px\";\n }\n }\n\n /**\n * Apply current config to UI elements.\n */\n applyStyles(): void {\n if (this.glassDiv) {\n this.glassDiv.style.width = `${this.config.glassSize}px`;\n this.glassDiv.style.height = `${this.config.glassSize}px`;\n this.glassDiv.style.border = this.config.borderEnabled\n ? `${this.config.borderWidth}px solid ${this.config.borderColor}`\n : 'none';\n\n // Reset clip-path and set default border-radius\n this.glassDiv.style.clipPath = 'none';\n this.glassDiv.style.borderRadius = '0px';\n\n switch (this.config.glassShape) {\n case \"Circle\":\n this.glassDiv.style.borderRadius = \"50%\";\n break;\n case \"Square\":\n // borderRadius is already '0px'\n break;\n case \"Rounded Square\":\n this.glassDiv.style.borderRadius = \"20%\";\n break;\n default:\n this.glassDiv.style.borderRadius = \"50%\";\n break;\n }\n }\n\n if (this.glassCanvas) {\n this.glassCanvas.width = this.config.glassSize;\n this.glassCanvas.height = this.config.glassSize;\n }\n\n // Handle debug canvas show/hide\n if (this.config.debugMode) {\n if (!this.debugCanvas) this.createDebugCanvas();\n if (this.state.active && this.debugCanvas) this.debugCanvas.style.display = \"block\";\n } else {\n if (this.debugCanvas) this.debugCanvas.style.display = \"none\";\n }\n }\n\n /**\n * Cleanup DOM elements.\n */\n cleanup(): void {\n if (this.glassDiv) this.glassDiv.remove();\n if (this.debugCanvas) this.debugCanvas.remove();\n // Remove menu button if needed (though hard to track references here without saving it)\n const btn = document.querySelector('.magnify-toggle-btn');\n if (btn) btn.remove();\n }\n\n /**\n * Inject a quick toggle button into the ComfyUI menu.\n */\n /**\n * Inject a quick toggle button into the ComfyUI menu.\n */\n injectMenuButton(): void {\n // Stop after 30 seconds\n const stopTime = Date.now() + 30000;\n\n // Define check function\n const attemptInjection = () => {\n const buttons = Array.from(document.querySelectorAll('button'));\n\n // Try to find an anchor button (Minimap or Links)\n // Priority 1: Minimap\n let anchorBtn = buttons.find(b => {\n const title = (b.title || '').toLowerCase();\n const aria = (b.getAttribute('aria-label') || '').toLowerCase();\n const text = (b.textContent || '').toLowerCase();\n return (title.includes('map') && !title.includes('open')) ||\n (aria.includes('map') && !aria.includes('open')) ||\n (text.includes('map'));\n });\n\n // Priority 2: Link Visibility\n if (!anchorBtn) {\n anchorBtn = buttons.find(b => {\n const title = (b.title || '').toLowerCase();\n const aria = (b.getAttribute('aria-label') || '').toLowerCase();\n return title.includes('link') || aria.includes('link');\n });\n }\n\n if (anchorBtn && anchorBtn.parentElement) {\n // Determine success BEFORE creating elements to allow early return if duplicate\n if (anchorBtn.parentElement.querySelector('.magnify-toggle-btn')) return true;\n\n console.log(\"[MagnifyGlass] Found menu anchor:\", anchorBtn.title || anchorBtn.getAttribute('aria-label') || \"Unknown Button\");\n\n const btn = document.createElement('button');\n // Copy all classes to match theme/library (e.g. p-button, comfy-btn, etc)\n btn.className = anchorBtn.className + ' magnify-toggle-btn';\n\n // Copy key styles that might define shape/size\n const computed = window.getComputedStyle(anchorBtn);\n btn.style.height = computed.height;\n btn.style.minHeight = computed.minHeight;\n // btn.style.width = computed.width; // Don't copy width, let it adapt\n\n btn.title = \"Toggle Magnify Glass\";\n btn.innerHTML = Icons.magnifyGlass;\n\n // Ensure specific display props\n btn.style.display = \"inline-flex\";\n btn.style.alignItems = \"center\";\n btn.style.justifyContent = \"center\";\n btn.style.padding = \"0 8px\";\n btn.style.cursor = \"pointer\";\n btn.style.border = anchorBtn.style.border || computed.border;\n btn.style.borderRadius = anchorBtn.style.borderRadius || computed.borderRadius;\n // Use cssText if available for background/color specific overrides\n if (!btn.style.background) btn.style.background = computed.background;\n if (!btn.style.color) btn.style.color = computed.color;\n\n // Add active state tracking\n btn.addEventListener('click', () => {\n if (this.onToggle) {\n this.onToggle();\n // Try to apply common active classes found in modern UI frameworks\n btn.classList.toggle('active');\n btn.classList.toggle('p-highlight'); // PrimeVue\n btn.classList.toggle('selected');\n }\n });\n\n // Insert logic:\n const isMinimap = (anchorBtn.title || '').toLowerCase().includes('map') ||\n (anchorBtn.getAttribute('aria-label') || '').toLowerCase().includes('map');\n\n if (isMinimap) {\n // Insert AFTER Minimap\n if (anchorBtn.nextSibling) {\n anchorBtn.parentElement.insertBefore(btn, anchorBtn.nextSibling);\n } else {\n anchorBtn.parentElement.appendChild(btn);\n }\n } else {\n // It's likely links, Insert BEFORE\n anchorBtn.parentElement.insertBefore(btn, anchorBtn);\n }\n\n console.log(\"[MagnifyGlass] Menu toggle button injected successfully\");\n return true;\n }\n return false;\n };\n\n // Run immediately first\n if (attemptInjection()) return;\n\n // Then poll rapidly\n const checkForMenu = setInterval(() => {\n if (Date.now() > stopTime) {\n console.warn(\"[MagnifyGlass] Menu injection timed out. Found buttons:\",\n Array.from(document.querySelectorAll('button')).map(b => b.title || b.getAttribute('aria-label') || b.textContent || b.className).slice(0, 5));\n clearInterval(checkForMenu);\n return;\n }\n\n if (attemptInjection()) {\n clearInterval(checkForMenu);\n }\n }, 100);\n }\n}\n"],"names":[],"mappings":"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"}
\ No newline at end of file