diff --git a/src/magnify-glass/UiManager.ts b/src/magnify-glass/UiManager.ts
index 07f6d72..1ce5d32 100644
--- a/src/magnify-glass/UiManager.ts
+++ b/src/magnify-glass/UiManager.ts
@@ -437,7 +437,7 @@ export class UiManager {
btn.style.minHeight = computed.minHeight;
// btn.style.width = computed.width; // Don't copy width, let it adapt
- btn.title = "Toggle Magnify Glass";
+ btn.title = "Toggle Magnify Glass (X)";
btn.innerHTML = Icons.magnifyGlass;
// Ensure specific display props
diff --git a/src/sidebar/SidebarSettings.ts b/src/sidebar/SidebarSettings.ts
index 9384c64..4f6a8f3 100644
--- a/src/sidebar/SidebarSettings.ts
+++ b/src/sidebar/SidebarSettings.ts
@@ -407,22 +407,13 @@ export function renderSettingsPanel(container: HTMLElement): void {
// The condition `if (getSettingValue("🔍MagnifyGlass.InfoPanelEnabled", true))` is removed
// as it doesn't make sense for a glass setting to depend on info panel being enabled.
// The tooltip and callback logic are kept as provided.
- glassSection.body.appendChild(createToggle('Persist Info',
- getSettingValue('🔍MagnifyGlass.InfoPanelPersist', false),
- (checked) => {
- setSettingValue('🔍MagnifyGlass.InfoPanelPersist', checked);
- // Sync with UI controls if needed
- const infoPanel = (window as any).infoPanelManager;
- if (infoPanel?.uiManager) {
- infoPanel.uiManager.updateControlStates();
- }
- }, 'Keep displaying the last node info when hovering empty space'
- ));
+ // Moved to panel settings section
+ // glassSection.body.appendChild(createToggle('Persist Info'...));
container.appendChild(glassSection.section);
- // ===== Hotkeys Section =====
- const hotkeySection = createSection('Hotkeys', true);
+ // ===== Glass Hotkeys Section =====
+ const hotkeySection = createSection('Glass Hotkeys', true);
hotkeySection.body.appendChild(createSelect('Activation Key',
getSettingValue('🔍MagnifyGlass.ActivationKey', 'x'), ACTIVATION_KEYS,
@@ -442,11 +433,23 @@ export function renderSettingsPanel(container: HTMLElement): void {
'Key to toggle follow cursor mode'
));
- hotkeySection.body.appendChild(createToggle('Require Alt Key',
+ const glassAltToggle = createToggle('Require Alt Key',
getSettingValue('🔍MagnifyGlass.AltRequired', false),
- (checked) => setSettingValue('🔍MagnifyGlass.AltRequired', checked),
+ (checked) => {
+ setSettingValue('🔍MagnifyGlass.AltRequired', checked);
+ // Sync all alt toggles
+ container.querySelectorAll('.magnify-alt-toggle .magnify-toggle').forEach(t => {
+ if (checked) t.classList.add('active');
+ else t.classList.remove('active');
+ });
+ },
'Require Alt key to be held with activation key'
- ));
+ );
+ glassAltToggle.classList.add('magnify-alt-toggle');
+ hotkeySection.body.appendChild(glassAltToggle);
+
+ // Moved to Panel Hotkeys section
+ // hotkeySection.body.appendChild(createToggle('Require Alt Key'...));
hotkeySection.body.appendChild(createSlider('Offset Step',
getSettingValue('🔍MagnifyGlass.OffsetStep', 10), 1, 50, 1, 'px',
@@ -650,6 +653,21 @@ export function renderSettingsPanel(container: HTMLElement): void {
'Key to pin/unpin the info panel position'
));
+ const panelAltToggle = createToggle('Require Alt Key',
+ getSettingValue('🔍MagnifyGlass.AltRequired', false),
+ (checked) => {
+ setSettingValue('🔍MagnifyGlass.AltRequired', checked);
+ // Sync all alt toggles
+ container.querySelectorAll('.magnify-alt-toggle .magnify-toggle').forEach(t => {
+ if (checked) t.classList.add('active');
+ else t.classList.remove('active');
+ });
+ },
+ 'Require Alt key to be held with hotkeys'
+ );
+ panelAltToggle.classList.add('magnify-alt-toggle');
+ panelHotkeySection.body.appendChild(panelAltToggle);
+
container.appendChild(panelHotkeySection.section);
diff --git a/src/sidebar/sidebar.ts b/src/sidebar/sidebar.ts
index 2a2f040..0615561 100644
--- a/src/sidebar/sidebar.ts
+++ b/src/sidebar/sidebar.ts
@@ -58,7 +58,7 @@ function renderSidebar(container: HTMLElement): void {
// Header
const header = document.createElement('div');
header.className = 'magnify-sidebar-header';
- header.innerHTML = `${MAGNIFY_ICON}
Magnify G.
`;
+ header.innerHTML = `${MAGNIFY_ICON}Magnify
`;
sidebar.appendChild(header);
// Content area
@@ -101,7 +101,7 @@ export function registerSidebar(): void {
app.extensionManager.registerSidebarTab({
id: 'magnifyglass',
icon: 'pi pi-search',
- title: 'Magnify G.',
+ title: 'Magnify',
tooltip: 'MagnifyGlass Settings',
type: 'custom',
render: (el: HTMLElement) => {
diff --git a/web/magnify-glass/UiManager.js b/web/magnify-glass/UiManager.js
index 06bb7ac..ed69c35 100644
--- a/web/magnify-glass/UiManager.js
+++ b/web/magnify-glass/UiManager.js
@@ -321,7 +321,7 @@ class UiManager {
const computed = window.getComputedStyle(anchorBtn);
btn.style.height = computed.height;
btn.style.minHeight = computed.minHeight;
- btn.title = "Toggle Magnify Glass";
+ btn.title = "Toggle Magnify Glass (X)";
btn.innerHTML = Icons.magnifyGlass;
btn.style.display = "inline-flex";
btn.style.alignItems = "center";
diff --git a/web/magnify-glass/UiManager.js.map b/web/magnify-glass/UiManager.js.map
index e878d52..ecd5307 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, DEFAULT_GLASS_Y_OFFSET } from '../shared/constants';\nimport { Icons } from '../shared/icons';\nimport { Logger } from '../shared/logger';\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 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.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 box-sizing: border-box;\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 // Inject menu button\n this.injectMenuButton();\n }\n\n /**\n * Show the magnifying glass.\n */\n show(): void {\n if (this.glassDiv) {\n this.glassDiv.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 }\n }\n\n /**\n * Enable or disable glass drag mode.\n * When enabled, the glass can be dragged to a new position.\n */\n setDragMode(enabled: boolean): void {\n if (!this.glassDiv) return;\n\n if (enabled) {\n // Use 'all-scroll' which shows a 4-way arrow consistently across browsers\n // Firefox shows 'move' as a grab/fist cursor which is confusing\n this.glassDiv.style.cursor = 'all-scroll';\n this.glassDiv.style.pointerEvents = 'auto';\n this.glassDiv.classList.add('drag-mode');\n\n // Add drag handlers\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Calculate the fixed offset between mouse and glass top-left\n // This maintains the \"grab point\" relative to the glass\n const rect = this.glassDiv!.getBoundingClientRect();\n const grabOffsetX = rect.left - e.clientX;\n const grabOffsetY = rect.top - e.clientY;\n\n // First, set left/top to current position to prevent jump\n this.glassDiv!.style.left = `${rect.left}px`;\n this.glassDiv!.style.top = `${rect.top}px`;\n\n // Then clear opposite positioning props (now safe since left/top are set)\n this.glassDiv!.style.right = 'auto';\n this.glassDiv!.style.bottom = 'auto';\n this.glassDiv!.style.transform = 'none'; // Clear any centering transforms if they exist\n\n const onMouseMove = (moveEvent: MouseEvent) => {\n moveEvent.preventDefault();\n moveEvent.stopPropagation();\n\n // 1:1 Movement: Glass moves exactly with mouse\n // New Pos = Mouse Pos + Initial Grab Offset\n const newLeft = moveEvent.clientX + grabOffsetX;\n const newTop = moveEvent.clientY + grabOffsetY;\n\n if (this.glassDiv) {\n this.glassDiv.style.left = `${newLeft}px`;\n this.glassDiv.style.top = `${newTop}px`;\n }\n };\n\n const onMouseUp = (upEvent: MouseEvent) => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n\n // Disable drag mode after drop\n this.state.isDragModeEnabled = false;\n this.setDragMode(false);\n\n // Update control states via info panel\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.uiManager) {\n infoPanel.uiManager.updateControlStates();\n }\n };\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n this.glassDiv.addEventListener('mousedown', onMouseDown);\n (this.glassDiv as any)._dragHandler = onMouseDown;\n } else {\n this.glassDiv.style.cursor = '';\n this.glassDiv.style.pointerEvents = 'none';\n this.glassDiv.classList.remove('drag-mode');\n\n // Remove drag handler\n if ((this.glassDiv as any)._dragHandler) {\n this.glassDiv.removeEventListener('mousedown', (this.glassDiv as any)._dragHandler);\n delete (this.glassDiv as any)._dragHandler;\n }\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 // 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 * Uses dynamic anchoring (Left/Right, Top/Bottom) based on screen quadrant.\n * @param clientX - Client X coordinate\n * @param clientY - Client Y coordinate\n */\n positionGlass(clientX: number, clientY: number): void {\n // Skip positioning if drag mode is active (handled manually in setDragMode)\n if (!this.config.followCursor || !this.glassDiv || this.state.isDragModeEnabled) return;\n\n const glassSize = this.config.glassSize;\n const offsetAmount = DEFAULT_PADDING;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Determine base coordinates (top-left based)\n // Add manual offsets (from drag operations)\n let targetX: number = this.config.offsetX || 0;\n let targetY: number = this.config.offsetY || 0;\n\n // Calculate standard Top-Left based positions first\n switch (this.config.glassPosition) {\n case \"Top\":\n targetX += clientX - (glassSize / 2);\n targetY += clientY - glassSize - offsetAmount;\n break;\n case \"Bottom\":\n targetX += clientX - (glassSize / 2);\n targetY += clientY + offsetAmount;\n break;\n case \"Left\":\n targetX += clientX - glassSize - offsetAmount;\n targetY += clientY - (glassSize / 2);\n break;\n case \"Right\":\n targetX += clientX + offsetAmount;\n targetY += clientY - (glassSize / 2);\n break;\n case \"Top-Left\":\n targetX += clientX - glassSize - offsetAmount;\n targetY += clientY - glassSize - offsetAmount;\n break;\n case \"Top-Right\":\n targetX += clientX + offsetAmount;\n targetY += clientY - glassSize - offsetAmount;\n break;\n case \"Bottom-Left\":\n targetX += clientX - glassSize - offsetAmount;\n targetY += clientY + offsetAmount;\n break;\n case \"Bottom-Right\":\n targetX += clientX + offsetAmount;\n targetY += clientY + offsetAmount;\n break;\n default:\n targetX += clientX - (glassSize / 2);\n targetY += clientY + offsetAmount;\n break;\n }\n\n // Apply Dynamic Anchoring based on resulting position's center relative to screen\n const glassCenterX = targetX + (glassSize / 2);\n const glassCenterY = targetY + (glassSize / 2);\n\n // Horizontal Anchor\n if (glassCenterX > vw / 2) {\n // Anchor Right\n const rightPos = vw - (targetX + glassSize);\n this.glassDiv.style.right = `${rightPos}px`;\n this.glassDiv.style.left = 'auto';\n } else {\n // Anchor Left\n this.glassDiv.style.left = `${targetX}px`;\n this.glassDiv.style.right = 'auto';\n }\n\n // Vertical Anchor\n if (glassCenterY > vh / 2) {\n // Anchor Bottom\n const bottomPos = vh - (targetY + glassSize);\n this.glassDiv.style.bottom = `${bottomPos}px`;\n this.glassDiv.style.top = 'auto';\n } else {\n // Anchor Top\n this.glassDiv.style.top = `${targetY}px`;\n this.glassDiv.style.bottom = 'auto';\n }\n\n // We run a lightweight clamp to ensure it doesn't bleed offscreen during movement\n // (Though the anchor logic implicitly handles resize, dragging near edges might need clamping)\n this.adjustForBoundaries();\n }\n\n /**\n * Adjust glass position to stay within viewport boundaries.\n * Respects the current anchor (Left/Right, Top/Bottom).\n */\n adjustForBoundaries(): void {\n if (!this.glassDiv) return;\n\n const glassSize = this.config.glassSize;\n const padding = DEFAULT_PADDING;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Check if anchored Right or Left\n const isAnchoredRight = this.glassDiv.style.right !== 'auto' && this.glassDiv.style.right !== '';\n\n if (isAnchoredRight) {\n let currentRight = parseFloat(this.glassDiv.style.right) || padding;\n // Clamp Right: max(padding, min(current, vw - size - padding))\n // Ensure it doesn't go off left side (vw - size - padding)\n currentRight = Math.max(padding, Math.min(currentRight, vw - glassSize - padding));\n this.glassDiv.style.right = `${currentRight}px`;\n } else {\n // Anchor Left\n let currentLeft = parseFloat(this.glassDiv.style.left) || padding;\n currentLeft = Math.max(padding, Math.min(currentLeft, vw - glassSize - padding));\n this.glassDiv.style.left = `${currentLeft}px`;\n }\n\n // Check if anchored Bottom or Top\n const isAnchoredBottom = this.glassDiv.style.bottom !== 'auto' && this.glassDiv.style.bottom !== '';\n\n if (isAnchoredBottom) {\n let currentBottom = parseFloat(this.glassDiv.style.bottom) || padding;\n currentBottom = Math.max(padding, Math.min(currentBottom, vh - glassSize - padding));\n this.glassDiv.style.bottom = `${currentBottom}px`;\n } else {\n // Anchor Top\n let currentTop = parseFloat(this.glassDiv.style.top) || padding;\n currentTop = Math.max(padding, Math.min(currentTop, vh - glassSize - padding));\n this.glassDiv.style.top = `${currentTop}px`;\n }\n }\n\n /**\n * Update position safely on resize.\n * Since we use dynamic anchoring in positionGlass, CSS handles most resize cases.\n * This method ensures we verify boundaries (clamping) and fix anchors if we cross thresholds drastically.\n */\n updateResponsivePosition(): void {\n if (!this.glassDiv) return;\n // Re-run boundary adjustments to clamp if resize pushed us offscreen\n this.adjustForBoundaries();\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\n /**\n * Cleanup DOM elements.\n */\n cleanup(): void {\n if (this.glassDiv) this.glassDiv.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 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 Logger.debug('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 // Copy border radius if available, but avoid copying colors/backgrounds to allow theme switching\n if (anchorBtn.style.borderRadius) {\n btn.style.borderRadius = anchorBtn.style.borderRadius;\n } else {\n // Fallback to computed only for structural properties, not colors\n btn.style.borderRadius = computed.borderRadius;\n }\n\n // Do NOT copy background/color/border from computed styles as they freeze the theme\n // Rely on className to handle theme styling\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 Logger.debug('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, DEFAULT_GLASS_Y_OFFSET } from '../shared/constants';\nimport { Icons } from '../shared/icons';\nimport { Logger } from '../shared/logger';\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 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.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 box-sizing: border-box;\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 // Inject menu button\n this.injectMenuButton();\n }\n\n /**\n * Show the magnifying glass.\n */\n show(): void {\n if (this.glassDiv) {\n this.glassDiv.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 }\n }\n\n /**\n * Enable or disable glass drag mode.\n * When enabled, the glass can be dragged to a new position.\n */\n setDragMode(enabled: boolean): void {\n if (!this.glassDiv) return;\n\n if (enabled) {\n // Use 'all-scroll' which shows a 4-way arrow consistently across browsers\n // Firefox shows 'move' as a grab/fist cursor which is confusing\n this.glassDiv.style.cursor = 'all-scroll';\n this.glassDiv.style.pointerEvents = 'auto';\n this.glassDiv.classList.add('drag-mode');\n\n // Add drag handlers\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Calculate the fixed offset between mouse and glass top-left\n // This maintains the \"grab point\" relative to the glass\n const rect = this.glassDiv!.getBoundingClientRect();\n const grabOffsetX = rect.left - e.clientX;\n const grabOffsetY = rect.top - e.clientY;\n\n // First, set left/top to current position to prevent jump\n this.glassDiv!.style.left = `${rect.left}px`;\n this.glassDiv!.style.top = `${rect.top}px`;\n\n // Then clear opposite positioning props (now safe since left/top are set)\n this.glassDiv!.style.right = 'auto';\n this.glassDiv!.style.bottom = 'auto';\n this.glassDiv!.style.transform = 'none'; // Clear any centering transforms if they exist\n\n const onMouseMove = (moveEvent: MouseEvent) => {\n moveEvent.preventDefault();\n moveEvent.stopPropagation();\n\n // 1:1 Movement: Glass moves exactly with mouse\n // New Pos = Mouse Pos + Initial Grab Offset\n const newLeft = moveEvent.clientX + grabOffsetX;\n const newTop = moveEvent.clientY + grabOffsetY;\n\n if (this.glassDiv) {\n this.glassDiv.style.left = `${newLeft}px`;\n this.glassDiv.style.top = `${newTop}px`;\n }\n };\n\n const onMouseUp = (upEvent: MouseEvent) => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n\n // Disable drag mode after drop\n this.state.isDragModeEnabled = false;\n this.setDragMode(false);\n\n // Update control states via info panel\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.uiManager) {\n infoPanel.uiManager.updateControlStates();\n }\n };\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n this.glassDiv.addEventListener('mousedown', onMouseDown);\n (this.glassDiv as any)._dragHandler = onMouseDown;\n } else {\n this.glassDiv.style.cursor = '';\n this.glassDiv.style.pointerEvents = 'none';\n this.glassDiv.classList.remove('drag-mode');\n\n // Remove drag handler\n if ((this.glassDiv as any)._dragHandler) {\n this.glassDiv.removeEventListener('mousedown', (this.glassDiv as any)._dragHandler);\n delete (this.glassDiv as any)._dragHandler;\n }\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 // 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 * Uses dynamic anchoring (Left/Right, Top/Bottom) based on screen quadrant.\n * @param clientX - Client X coordinate\n * @param clientY - Client Y coordinate\n */\n positionGlass(clientX: number, clientY: number): void {\n // Skip positioning if drag mode is active (handled manually in setDragMode)\n if (!this.config.followCursor || !this.glassDiv || this.state.isDragModeEnabled) return;\n\n const glassSize = this.config.glassSize;\n const offsetAmount = DEFAULT_PADDING;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Determine base coordinates (top-left based)\n // Add manual offsets (from drag operations)\n let targetX: number = this.config.offsetX || 0;\n let targetY: number = this.config.offsetY || 0;\n\n // Calculate standard Top-Left based positions first\n switch (this.config.glassPosition) {\n case \"Top\":\n targetX += clientX - (glassSize / 2);\n targetY += clientY - glassSize - offsetAmount;\n break;\n case \"Bottom\":\n targetX += clientX - (glassSize / 2);\n targetY += clientY + offsetAmount;\n break;\n case \"Left\":\n targetX += clientX - glassSize - offsetAmount;\n targetY += clientY - (glassSize / 2);\n break;\n case \"Right\":\n targetX += clientX + offsetAmount;\n targetY += clientY - (glassSize / 2);\n break;\n case \"Top-Left\":\n targetX += clientX - glassSize - offsetAmount;\n targetY += clientY - glassSize - offsetAmount;\n break;\n case \"Top-Right\":\n targetX += clientX + offsetAmount;\n targetY += clientY - glassSize - offsetAmount;\n break;\n case \"Bottom-Left\":\n targetX += clientX - glassSize - offsetAmount;\n targetY += clientY + offsetAmount;\n break;\n case \"Bottom-Right\":\n targetX += clientX + offsetAmount;\n targetY += clientY + offsetAmount;\n break;\n default:\n targetX += clientX - (glassSize / 2);\n targetY += clientY + offsetAmount;\n break;\n }\n\n // Apply Dynamic Anchoring based on resulting position's center relative to screen\n const glassCenterX = targetX + (glassSize / 2);\n const glassCenterY = targetY + (glassSize / 2);\n\n // Horizontal Anchor\n if (glassCenterX > vw / 2) {\n // Anchor Right\n const rightPos = vw - (targetX + glassSize);\n this.glassDiv.style.right = `${rightPos}px`;\n this.glassDiv.style.left = 'auto';\n } else {\n // Anchor Left\n this.glassDiv.style.left = `${targetX}px`;\n this.glassDiv.style.right = 'auto';\n }\n\n // Vertical Anchor\n if (glassCenterY > vh / 2) {\n // Anchor Bottom\n const bottomPos = vh - (targetY + glassSize);\n this.glassDiv.style.bottom = `${bottomPos}px`;\n this.glassDiv.style.top = 'auto';\n } else {\n // Anchor Top\n this.glassDiv.style.top = `${targetY}px`;\n this.glassDiv.style.bottom = 'auto';\n }\n\n // We run a lightweight clamp to ensure it doesn't bleed offscreen during movement\n // (Though the anchor logic implicitly handles resize, dragging near edges might need clamping)\n this.adjustForBoundaries();\n }\n\n /**\n * Adjust glass position to stay within viewport boundaries.\n * Respects the current anchor (Left/Right, Top/Bottom).\n */\n adjustForBoundaries(): void {\n if (!this.glassDiv) return;\n\n const glassSize = this.config.glassSize;\n const padding = DEFAULT_PADDING;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Check if anchored Right or Left\n const isAnchoredRight = this.glassDiv.style.right !== 'auto' && this.glassDiv.style.right !== '';\n\n if (isAnchoredRight) {\n let currentRight = parseFloat(this.glassDiv.style.right) || padding;\n // Clamp Right: max(padding, min(current, vw - size - padding))\n // Ensure it doesn't go off left side (vw - size - padding)\n currentRight = Math.max(padding, Math.min(currentRight, vw - glassSize - padding));\n this.glassDiv.style.right = `${currentRight}px`;\n } else {\n // Anchor Left\n let currentLeft = parseFloat(this.glassDiv.style.left) || padding;\n currentLeft = Math.max(padding, Math.min(currentLeft, vw - glassSize - padding));\n this.glassDiv.style.left = `${currentLeft}px`;\n }\n\n // Check if anchored Bottom or Top\n const isAnchoredBottom = this.glassDiv.style.bottom !== 'auto' && this.glassDiv.style.bottom !== '';\n\n if (isAnchoredBottom) {\n let currentBottom = parseFloat(this.glassDiv.style.bottom) || padding;\n currentBottom = Math.max(padding, Math.min(currentBottom, vh - glassSize - padding));\n this.glassDiv.style.bottom = `${currentBottom}px`;\n } else {\n // Anchor Top\n let currentTop = parseFloat(this.glassDiv.style.top) || padding;\n currentTop = Math.max(padding, Math.min(currentTop, vh - glassSize - padding));\n this.glassDiv.style.top = `${currentTop}px`;\n }\n }\n\n /**\n * Update position safely on resize.\n * Since we use dynamic anchoring in positionGlass, CSS handles most resize cases.\n * This method ensures we verify boundaries (clamping) and fix anchors if we cross thresholds drastically.\n */\n updateResponsivePosition(): void {\n if (!this.glassDiv) return;\n // Re-run boundary adjustments to clamp if resize pushed us offscreen\n this.adjustForBoundaries();\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\n /**\n * Cleanup DOM elements.\n */\n cleanup(): void {\n if (this.glassDiv) this.glassDiv.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 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 Logger.debug('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 (X)\";\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 // Copy border radius if available, but avoid copying colors/backgrounds to allow theme switching\n if (anchorBtn.style.borderRadius) {\n btn.style.borderRadius = anchorBtn.style.borderRadius;\n } else {\n // Fallback to computed only for structural properties, not colors\n btn.style.borderRadius = computed.borderRadius;\n }\n\n // Do NOT copy background/color/border from computed styles as they freeze the theme\n // Rely on className to handle theme styling\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 Logger.debug('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
diff --git a/web/sidebar/SidebarSettings.js b/web/sidebar/SidebarSettings.js
index 5b47039..d8e6fd7 100644
--- a/web/sidebar/SidebarSettings.js
+++ b/web/sidebar/SidebarSettings.js
@@ -275,20 +275,8 @@ function renderSettingsPanel(container) {
(checked) => setSettingValue("🔍MagnifyGlass.AlwaysActiveMode", checked),
`When enabled, glass stays visible. Toggle with ${activationKey}`
));
- glassSection.body.appendChild(createToggle(
- "Persist Info",
- getSettingValue("🔍MagnifyGlass.InfoPanelPersist", false),
- (checked) => {
- setSettingValue("🔍MagnifyGlass.InfoPanelPersist", checked);
- const infoPanel = window.infoPanelManager;
- if (infoPanel == null ? void 0 : infoPanel.uiManager) {
- infoPanel.uiManager.updateControlStates();
- }
- },
- "Keep displaying the last node info when hovering empty space"
- ));
container.appendChild(glassSection.section);
- const hotkeySection = createSection("Hotkeys", true);
+ const hotkeySection = createSection("Glass Hotkeys", true);
hotkeySection.body.appendChild(createSelect(
"Activation Key",
getSettingValue("🔍MagnifyGlass.ActivationKey", "x"),
@@ -310,12 +298,20 @@ function renderSettingsPanel(container) {
(value) => setSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", value),
"Key to toggle follow cursor mode"
));
- hotkeySection.body.appendChild(createToggle(
+ const glassAltToggle = createToggle(
"Require Alt Key",
getSettingValue("🔍MagnifyGlass.AltRequired", false),
- (checked) => setSettingValue("🔍MagnifyGlass.AltRequired", checked),
+ (checked) => {
+ setSettingValue("🔍MagnifyGlass.AltRequired", checked);
+ container.querySelectorAll(".magnify-alt-toggle .magnify-toggle").forEach((t) => {
+ if (checked) t.classList.add("active");
+ else t.classList.remove("active");
+ });
+ },
"Require Alt key to be held with activation key"
- ));
+ );
+ glassAltToggle.classList.add("magnify-alt-toggle");
+ hotkeySection.body.appendChild(glassAltToggle);
hotkeySection.body.appendChild(createSlider(
"Offset Step",
getSettingValue("🔍MagnifyGlass.OffsetStep", 10),
@@ -546,6 +542,20 @@ function renderSettingsPanel(container) {
(value) => setSettingValue("🔍MagnifyGlass.PinPanelHotkey", value),
"Key to pin/unpin the info panel position"
));
+ const panelAltToggle = createToggle(
+ "Require Alt Key",
+ getSettingValue("🔍MagnifyGlass.AltRequired", false),
+ (checked) => {
+ setSettingValue("🔍MagnifyGlass.AltRequired", checked);
+ container.querySelectorAll(".magnify-alt-toggle .magnify-toggle").forEach((t) => {
+ if (checked) t.classList.add("active");
+ else t.classList.remove("active");
+ });
+ },
+ "Require Alt key to be held with hotkeys"
+ );
+ panelAltToggle.classList.add("magnify-alt-toggle");
+ panelHotkeySection.body.appendChild(panelAltToggle);
container.appendChild(panelHotkeySection.section);
const buttonRow = document.createElement("div");
buttonRow.style.padding = "16px";
diff --git a/web/sidebar/SidebarSettings.js.map b/web/sidebar/SidebarSettings.js.map
index a544ec2..d542656 100644
--- a/web/sidebar/SidebarSettings.js.map
+++ b/web/sidebar/SidebarSettings.js.map
@@ -1 +1 @@
-{"version":3,"file":"SidebarSettings.js","sources":["../../src/sidebar/SidebarSettings.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Sidebar Settings Panel\n * \n * All settings controls for the sidebar.\n */\n\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\nimport {\n GLASS_POSITIONS,\n GLASS_SHAPES,\n ACTIVATION_KEYS,\n RESET_KEYS,\n TOGGLE_FOLLOW_KEYS,\n PANEL_POSITIONS\n} from '../shared/constants';\n\n/**\n * Icons for the settings panel\n */\nconst Icons = {\n chevronDown: ``,\n reset: ``,\n};\n\n/**\n * Create a slider control with tooltip\n */\n/**\n * Create a slider control with tooltip\n */\nfunction createSlider(\n label: string,\n value: number,\n min: number,\n max: number,\n step: number,\n unit: string,\n onChange: (value: number) => void,\n tooltip?: string,\n onInput?: (value: number) => void\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-control-row';\n if (tooltip) row.title = tooltip;\n\n const labelRow = document.createElement('div');\n labelRow.className = 'magnify-control-label-row';\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const valueEl = document.createElement('span');\n valueEl.className = 'control-value';\n valueEl.textContent = `${value}${unit}`;\n\n labelRow.appendChild(labelEl);\n labelRow.appendChild(valueEl);\n\n const slider = document.createElement('input');\n slider.type = 'range';\n slider.className = 'magnify-slider';\n slider.min = String(min);\n slider.max = String(max);\n slider.step = String(step);\n slider.value = String(value);\n\n // Prevent ComfyUI from capturing mouse events during drag\n slider.addEventListener('mousedown', (e) => e.stopPropagation());\n slider.addEventListener('touchstart', (e) => e.stopPropagation());\n slider.addEventListener('pointerdown', (e) => e.stopPropagation());\n\n slider.addEventListener('input', (e) => {\n e.stopPropagation();\n const newValue = parseFloat(slider.value);\n valueEl.textContent = `${newValue}${unit}`;\n if (onInput) onInput(newValue);\n });\n\n slider.addEventListener('change', (e) => {\n e.stopPropagation();\n const newValue = parseFloat(slider.value);\n onChange(newValue);\n });\n\n row.appendChild(labelRow);\n row.appendChild(slider);\n\n return row;\n}\n\n/**\n * Create a toggle control with tooltip\n */\nfunction createToggle(\n label: string,\n checked: boolean,\n onChange: (checked: boolean) => void,\n tooltip?: string\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-toggle-row';\n if (tooltip) row.title = tooltip;\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const toggle = document.createElement('div');\n toggle.className = `magnify-toggle${checked ? ' active' : ''}`;\n\n toggle.addEventListener('click', () => {\n const isActive = toggle.classList.toggle('active');\n onChange(isActive);\n });\n\n row.appendChild(labelEl);\n row.appendChild(toggle);\n\n return row;\n}\n\n/**\n * Create a select dropdown control with tooltip\n */\nfunction createSelect(\n label: string,\n value: string,\n options: readonly string[] | string[],\n onChange: (value: string) => void,\n tooltip?: string,\n onInput?: (value: string) => void\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-control-row';\n if (tooltip) row.title = tooltip;\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const select = document.createElement('select');\n select.className = 'magnify-select';\n\n options.forEach(opt => {\n const option = document.createElement('option');\n option.value = opt;\n option.textContent = opt;\n if (opt === value) option.selected = true;\n select.appendChild(option);\n });\n\n select.addEventListener('change', () => {\n // Call onInput first for live preview\n if (onInput) {\n onInput(select.value);\n }\n // Then persist the setting\n onChange(select.value);\n });\n\n row.appendChild(labelEl);\n row.appendChild(select);\n\n return row;\n}\n\n/**\n * Create a color picker control with tooltip\n */\n/**\n * Create a color picker control with tooltip\n */\nfunction createColorPicker(\n label: string,\n value: string,\n onChange: (value: string) => void,\n tooltip?: string,\n onInput?: (value: string) => void\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-control-row magnify-color-row';\n if (tooltip) row.title = tooltip;\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const colorWrapper = document.createElement('div');\n colorWrapper.className = 'magnify-color-wrapper';\n\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.className = 'magnify-color-input';\n colorInput.value = value;\n\n const colorPreview = document.createElement('span');\n colorPreview.className = 'magnify-color-preview';\n colorPreview.textContent = value;\n\n colorInput.addEventListener('input', () => {\n colorPreview.textContent = colorInput.value;\n if (onInput) onInput(colorInput.value);\n });\n\n colorInput.addEventListener('change', () => {\n onChange(colorInput.value);\n });\n\n colorWrapper.appendChild(colorInput);\n colorWrapper.appendChild(colorPreview);\n\n row.appendChild(labelEl);\n row.appendChild(colorWrapper);\n\n return row;\n}\n\n/**\n * Get current MagnifyGlass settings from app.ui.settings\n */\nfunction getSettingValue(id: string, defaultValue: any): any {\n try {\n const value = app.ui.settings.getSettingValue(id);\n return value !== undefined ? value : defaultValue;\n } catch {\n return defaultValue;\n }\n}\n\n/**\n * Set a MagnifyGlass setting via app.ui.settings\n */\nfunction setSettingValue(id: string, value: any): void {\n try {\n app.ui.settings.setSettingValue(id, value);\n } catch (e) {\n console.warn(`Failed to set setting ${id}:`, e);\n }\n}\n\n/** Storage key for section states */\nconst SECTION_STATE_KEY = 'magnifyglass-sidebar-sections';\n\n/**\n * Get saved section states from localStorage\n */\nfunction getSectionStates(): Record {\n try {\n const saved = localStorage.getItem(SECTION_STATE_KEY);\n return saved ? JSON.parse(saved) : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Save section state to localStorage\n */\nfunction saveSectionState(title: string, collapsed: boolean): void {\n try {\n const states = getSectionStates();\n states[title] = collapsed;\n localStorage.setItem(SECTION_STATE_KEY, JSON.stringify(states));\n } catch {\n // Ignore storage errors\n }\n}\n\n/**\n * Create a collapsible section with persistent state\n */\nfunction createSection(title: string, defaultCollapsed: boolean = false): { section: HTMLElement; body: HTMLElement } {\n const section = document.createElement('div');\n section.className = 'magnify-sidebar-section';\n\n // Check if we have a saved state for this section\n const savedStates = getSectionStates();\n const collapsed = savedStates[title] !== undefined ? savedStates[title] : defaultCollapsed;\n\n const header = document.createElement('div');\n header.className = `magnify-sidebar-section-header${collapsed ? ' collapsed' : ''}`;\n header.innerHTML = `${Icons.chevronDown}${title}`;\n\n const body = document.createElement('div');\n body.className = `magnify-sidebar-section-body${collapsed ? ' collapsed' : ''}`;\n // Apply inline style to prevent flash before CSS loads\n if (collapsed) {\n body.style.display = 'none';\n }\n\n header.addEventListener('click', () => {\n const isCollapsed = header.classList.toggle('collapsed');\n body.classList.toggle('collapsed');\n // Also toggle inline style for immediate effect\n body.style.display = isCollapsed ? 'none' : '';\n // Save the new state\n saveSectionState(title, isCollapsed);\n });\n\n section.appendChild(header);\n section.appendChild(body);\n\n return { section, body };\n}\n\n/**\n * Render the settings panel into the container\n */\nexport function renderSettingsPanel(container: HTMLElement): void {\n // Get current hotkey values for tooltips\n const activationKey = getSettingValue('🔍MagnifyGlass.ActivationKey', 'x').toUpperCase();\n const resetKey = getSettingValue('🔍MagnifyGlass.ResetKey', 'o').toUpperCase();\n const toggleFollowKey = getSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h').toUpperCase();\n\n // ===== Glass Settings Section =====\n const glassSection = createSection('Glass Settings');\n\n glassSection.body.appendChild(createSlider('Zoom Factor',\n getSettingValue('🔍MagnifyGlass.ZoomFactor', 300), 100, 1000, 25, '%',\n (value) => setSettingValue('🔍MagnifyGlass.ZoomFactor', value),\n 'Magnification level of the glass (100-1000%)',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.zoomFactor = value / 100;\n if (mg.state.active) mg.updateMagnifiedView();\n }\n }\n ));\n\n glassSection.body.appendChild(createSlider('Glass Size',\n getSettingValue('🔍MagnifyGlass.GlassSize', 250), 50, 500, 10, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.GlassSize', value),\n 'Size of the magnifying glass in pixels',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.glassSize = value;\n mg.applyUiChanges();\n if (mg.state.active) mg.updateMagnifiedView();\n }\n }\n ));\n\n glassSection.body.appendChild(createSelect('Shape',\n getSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square'), GLASS_SHAPES,\n (value) => setSettingValue('🔍MagnifyGlass.GlassShape', value),\n 'Shape of the magnifying glass'\n ));\n\n glassSection.body.appendChild(createSelect('Position',\n getSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right'), GLASS_POSITIONS,\n (value) => setSettingValue('🔍MagnifyGlass.GlassPosition', value),\n `Default position of the glass. Press ${resetKey} to reset.`\n ));\n\n glassSection.body.appendChild(createSelect('Filtering',\n getSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear'), ['Linear', 'Nearest'],\n (value) => setSettingValue('🔍MagnifyGlass.TextureFiltering', value),\n 'Texture filtering: Linear (smooth) or Nearest (pixelated)'\n ));\n\n glassSection.body.appendChild(createSlider('Border Width',\n getSettingValue('🔍MagnifyGlass.BorderWidth', 2), 0, 10, 0.5, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.BorderWidth', value),\n 'Width of the glass border in pixels',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.borderWidth = value;\n mg.applyUiChanges();\n }\n }\n ));\n\n glassSection.body.appendChild(createColorPicker('Border Color',\n getSettingValue('🔍MagnifyGlass.BorderColor', '#ffffff'),\n (value) => setSettingValue('🔍MagnifyGlass.BorderColor', value),\n 'Color of the glass border',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.borderColor = value;\n mg.applyUiChanges();\n }\n }\n ));\n\n glassSection.body.appendChild(createToggle('Show Border',\n getSettingValue('🔍MagnifyGlass.BorderEnabled', true),\n (checked) => setSettingValue('🔍MagnifyGlass.BorderEnabled', checked),\n 'Show or hide the glass border'\n ));\n\n glassSection.body.appendChild(createToggle('Follow Cursor',\n getSettingValue('🔍MagnifyGlass.FollowCursor', false),\n (checked) => setSettingValue('🔍MagnifyGlass.FollowCursor', checked),\n `Glass follows the cursor. Hotkey: ${toggleFollowKey}`\n ));\n\n glassSection.body.appendChild(createToggle('Always Active Mode',\n getSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true),\n (checked) => setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', checked),\n `When enabled, glass stays visible. Toggle with ${activationKey}`\n ));\n\n // The instruction implies this should be in glassSection, but the content suggests info panel.\n // Placing it in glassSection as per instruction, assuming createToggle is intended.\n // The condition `if (getSettingValue(\"🔍MagnifyGlass.InfoPanelEnabled\", true))` is removed\n // as it doesn't make sense for a glass setting to depend on info panel being enabled.\n // The tooltip and callback logic are kept as provided.\n glassSection.body.appendChild(createToggle('Persist Info',\n getSettingValue('🔍MagnifyGlass.InfoPanelPersist', false),\n (checked) => {\n setSettingValue('🔍MagnifyGlass.InfoPanelPersist', checked);\n // Sync with UI controls if needed\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.uiManager) {\n infoPanel.uiManager.updateControlStates();\n }\n }, 'Keep displaying the last node info when hovering empty space'\n ));\n\n container.appendChild(glassSection.section);\n\n // ===== Hotkeys Section =====\n const hotkeySection = createSection('Hotkeys', true);\n\n hotkeySection.body.appendChild(createSelect('Activation Key',\n getSettingValue('🔍MagnifyGlass.ActivationKey', 'x'), ACTIVATION_KEYS,\n (value) => setSettingValue('🔍MagnifyGlass.ActivationKey', value),\n 'Key to toggle or hold to activate the magnifying glass'\n ));\n\n hotkeySection.body.appendChild(createSelect('Reset Key',\n getSettingValue('🔍MagnifyGlass.ResetKey', 'o'), RESET_KEYS,\n (value) => setSettingValue('🔍MagnifyGlass.ResetKey', value),\n 'Key to reset glass position and disable follow cursor'\n ));\n\n hotkeySection.body.appendChild(createSelect('Toggle Follow Key',\n getSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h'), TOGGLE_FOLLOW_KEYS,\n (value) => setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', value),\n 'Key to toggle follow cursor mode'\n ));\n\n hotkeySection.body.appendChild(createToggle('Require Alt Key',\n getSettingValue('🔍MagnifyGlass.AltRequired', false),\n (checked) => setSettingValue('🔍MagnifyGlass.AltRequired', checked),\n 'Require Alt key to be held with activation key'\n ));\n\n hotkeySection.body.appendChild(createSlider('Offset Step',\n getSettingValue('🔍MagnifyGlass.OffsetStep', 10), 1, 50, 1, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.OffsetStep', value),\n 'Amount to move glass per arrow key press (pixels)'\n ));\n\n container.appendChild(hotkeySection.section);\n\n // ===== Info Panel Section =====\n const panelSection = createSection('Info Panel', true);\n\n const toggleHotkey = getSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i').toUpperCase();\n const glassToggleKey = getSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g').toUpperCase();\n const pinHotkeyVal = getSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u').toUpperCase();\n\n panelSection.body.appendChild(createToggle('Enable Panel',\n getSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true),\n (checked) => setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', checked),\n `Show info panel with node details. Toggle: ${toggleHotkey}`\n ));\n\n panelSection.body.appendChild(createToggle('Persist Info',\n getSettingValue('🔍MagnifyGlass.InfoPanelPersist', false),\n (checked) => {\n setSettingValue('🔍MagnifyGlass.InfoPanelPersist', checked);\n // Sync with UI controls if needed\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.uiManager) {\n infoPanel.uiManager.updateControlStates();\n }\n },\n 'Keep displaying the last node info when hovering empty space'\n ));\n\n panelSection.body.appendChild(createSelect('Position',\n getSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom'), PANEL_POSITIONS,\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelPosition', value),\n 'Position of the info panel relative to the glass'\n ));\n\n panelSection.body.appendChild(createSlider('Width',\n getSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300), 200, 600, 20, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelWidth', value),\n 'Width of the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelWidth'] = value;\n infoPanel.uiManager.applyStyles();\n\n // Ensure visible and reflow if active\n if (infoPanel.magnifyGlass.state.active) {\n infoPanel.uiManager.show();\n infoPanel.positionManager.positionPanel();\n }\n }\n }\n ));\n\n panelSection.body.appendChild(createSlider('Max Height',\n getSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300), 200, 1500, 50, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', value),\n 'Maximum height of the info panel',\n (value) => {\n console.log('[MaxHeight] Handler called with value:', value);\n const infoPanel = (window as any).infoPanelManager;\n console.log('[MaxHeight] infoPanelManager:', infoPanel ? 'found' : 'NOT FOUND');\n if (infoPanel?.stateManager?.state?.settings) {\n console.log('[MaxHeight] Updating setting and calling applyStyles');\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelMaxHeight'] = value;\n infoPanel.uiManager.applyStyles();\n\n // Ensure visible and reflow if active\n if (infoPanel.magnifyGlass.state.active) {\n console.log('[MaxHeight] Glass is active, calling show() and positionPanel()');\n infoPanel.uiManager.show();\n infoPanel.positionManager.positionPanel();\n } else {\n console.log('[MaxHeight] Glass is NOT active, skipping show()');\n }\n } else {\n console.warn('[MaxHeight] Check failed - stateManager/state/settings missing');\n }\n }\n ));\n\n panelSection.body.appendChild(createSlider('Font Size',\n getSettingValue('🔍MagnifyGlass.InfoPanelFontSize', 14), 8, 24, 1, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelFontSize', value),\n 'Font size of text in the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelFontSize'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createSlider('Opacity',\n getSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 95), 10, 100, 5, '%',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', value),\n 'Background opacity of the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelOpacity'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n const fontFamilyOptions = [\n 'System Default', 'Inter', 'Roboto', 'JetBrains Mono', 'Fira Code',\n 'IBM Plex Sans', 'Space Grotesk', 'Lexend', 'Outfit', 'monospace'\n ];\n panelSection.body.appendChild(createSelect('Font Family',\n getSettingValue('🔍MagnifyGlass.InfoPanelFontFamily', 'System Default'), fontFamilyOptions,\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelFontFamily', value),\n 'Font family for the info panel text',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelFontFamily'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createColorPicker('Text Color',\n getSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280'),\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', value),\n 'Text color in the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelTextColor'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createColorPicker('Accent Color',\n getSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6'),\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', value),\n 'Accent color for highlights',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelAccentColor'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createToggle('Hover Controls',\n getSettingValue('🔍MagnifyGlass.ShowHoveringControls', true),\n (checked) => setSettingValue('🔍MagnifyGlass.ShowHoveringControls', checked),\n 'Show floating control buttons near the panel'\n ));\n\n panelSection.body.appendChild(createSelect('Controls Position',\n getSettingValue('🔍MagnifyGlass.ControlsPosition', 'left'),\n ['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],\n (value) => setSettingValue('🔍MagnifyGlass.ControlsPosition', value),\n 'Position of floating control buttons',\n (value) => {\n // Live update controls position\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.ControlsPosition'] = value;\n infoPanel.uiManager.updateControlsLayout(value);\n if (infoPanel.magnifyGlass.state.active) {\n infoPanel.positionManager.positionFloatingControls();\n }\n }\n }\n ));\n\n container.appendChild(panelSection.section);\n\n // ===== Panel Hotkeys Section =====\n const panelHotkeySection = createSection('Panel Hotkeys', true);\n\n panelHotkeySection.body.appendChild(createSelect('Toggle Key',\n getSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i'), ['i', 'j', 'k', 'l', 'n', 'm'],\n (value) => setSettingValue('🔍MagnifyGlass.ToggleHotkey', value),\n 'Key to toggle info panel visibility'\n ));\n\n panelHotkeySection.body.appendChild(createSelect('Glass Toggle Key',\n getSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g'), ['g', 'f', 'v', 'b'],\n (value) => setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', value),\n 'Key to toggle glass preview in panel'\n ));\n\n panelHotkeySection.body.appendChild(createSelect('Pin Key',\n getSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u'), ['u', 'p', 'y', 't'],\n (value) => setSettingValue('🔍MagnifyGlass.PinPanelHotkey', value),\n 'Key to pin/unpin the info panel position'\n ));\n\n container.appendChild(panelHotkeySection.section);\n\n\n\n // ===== Button Row =====\n const buttonRow = document.createElement('div');\n buttonRow.style.padding = '16px';\n buttonRow.style.borderTop = '1px solid var(--border-color, #333)';\n buttonRow.style.display = 'flex';\n buttonRow.style.flexDirection = 'column';\n buttonRow.style.gap = '8px';\n\n // Reset Position Button\n const resetBtn = document.createElement('button');\n resetBtn.className = 'magnify-btn';\n resetBtn.style.width = '100%';\n resetBtn.innerHTML = `${Icons.reset} Reset Position`;\n resetBtn.title = `Reset glass position and disable follow cursor. Hotkey: ${resetKey}`;\n\n resetBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n const magnifyGlass = window.comfyUIMagnifyGlass;\n if (magnifyGlass && magnifyGlass.resetOffsets) {\n magnifyGlass.resetOffsets();\n // Update follow cursor toggle in UI\n const followToggles = container.querySelectorAll('.magnify-toggle-row');\n followToggles.forEach(row => {\n const label = row.querySelector('label');\n if (label && label.textContent === 'Follow Cursor') {\n const toggle = row.querySelector('.magnify-toggle');\n if (toggle) toggle.classList.remove('active');\n }\n });\n }\n });\n\n buttonRow.appendChild(resetBtn);\n\n // Reset All Settings Button\n const resetAllBtn = document.createElement('button');\n resetAllBtn.className = 'magnify-btn magnify-btn-secondary';\n resetAllBtn.style.width = '100%';\n resetAllBtn.innerHTML = `${Icons.reset} Reset All Settings`;\n resetAllBtn.title = 'Reset all settings to their default values';\n\n resetAllBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n\n // Confirm with user\n if (!confirm('Reset all MagnifyGlass settings to defaults?')) {\n return;\n }\n\n // Default glass settings\n setSettingValue('🔍MagnifyGlass.ZoomFactor', 300);\n setSettingValue('🔍MagnifyGlass.GlassSize', 300);\n setSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square');\n setSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right');\n setSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear');\n setSettingValue('🔍MagnifyGlass.BorderWidth', 1);\n setSettingValue('🔍MagnifyGlass.BorderColor', '#6b7280');\n setSettingValue('🔍MagnifyGlass.BorderEnabled', true);\n setSettingValue('🔍MagnifyGlass.FollowCursor', false);\n setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true);\n setSettingValue('🔍MagnifyGlass.ActivationKey', 'x');\n setSettingValue('🔍MagnifyGlass.ResetKey', 'o');\n setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h');\n setSettingValue('🔍MagnifyGlass.AltRequired', false);\n setSettingValue('🔍MagnifyGlass.OffsetStep', 5);\n\n\n // Default panel settings\n setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true);\n setSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom');\n setSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300);\n setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300);\n setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 100);\n setSettingValue('🔍MagnifyGlass.InfoPanelFontSize', 14);\n setSettingValue('🔍MagnifyGlass.InfoPanelFontFamily', 'System Default');\n setSettingValue('🔍MagnifyGlass.InfoPanelPersist', false);\n setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280');\n setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6');\n setSettingValue('🔍MagnifyGlass.InfoPanelAnimations', false);\n setSettingValue('🔍MagnifyGlass.ShowHoveringControls', true);\n setSettingValue('🔍MagnifyGlass.ControlsPosition', 'left');\n setSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i');\n setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g');\n setSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u');\n\n // Also reset position\n const magnifyGlass = window.comfyUIMagnifyGlass;\n if (magnifyGlass && magnifyGlass.resetOffsets) {\n magnifyGlass.resetOffsets();\n }\n });\n\n buttonRow.appendChild(resetAllBtn);\n container.appendChild(buttonRow);\n}\n"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"SidebarSettings.js","sources":["../../src/sidebar/SidebarSettings.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Sidebar Settings Panel\n * \n * All settings controls for the sidebar.\n */\n\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\nimport {\n GLASS_POSITIONS,\n GLASS_SHAPES,\n ACTIVATION_KEYS,\n RESET_KEYS,\n TOGGLE_FOLLOW_KEYS,\n PANEL_POSITIONS\n} from '../shared/constants';\n\n/**\n * Icons for the settings panel\n */\nconst Icons = {\n chevronDown: ``,\n reset: ``,\n};\n\n/**\n * Create a slider control with tooltip\n */\n/**\n * Create a slider control with tooltip\n */\nfunction createSlider(\n label: string,\n value: number,\n min: number,\n max: number,\n step: number,\n unit: string,\n onChange: (value: number) => void,\n tooltip?: string,\n onInput?: (value: number) => void\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-control-row';\n if (tooltip) row.title = tooltip;\n\n const labelRow = document.createElement('div');\n labelRow.className = 'magnify-control-label-row';\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const valueEl = document.createElement('span');\n valueEl.className = 'control-value';\n valueEl.textContent = `${value}${unit}`;\n\n labelRow.appendChild(labelEl);\n labelRow.appendChild(valueEl);\n\n const slider = document.createElement('input');\n slider.type = 'range';\n slider.className = 'magnify-slider';\n slider.min = String(min);\n slider.max = String(max);\n slider.step = String(step);\n slider.value = String(value);\n\n // Prevent ComfyUI from capturing mouse events during drag\n slider.addEventListener('mousedown', (e) => e.stopPropagation());\n slider.addEventListener('touchstart', (e) => e.stopPropagation());\n slider.addEventListener('pointerdown', (e) => e.stopPropagation());\n\n slider.addEventListener('input', (e) => {\n e.stopPropagation();\n const newValue = parseFloat(slider.value);\n valueEl.textContent = `${newValue}${unit}`;\n if (onInput) onInput(newValue);\n });\n\n slider.addEventListener('change', (e) => {\n e.stopPropagation();\n const newValue = parseFloat(slider.value);\n onChange(newValue);\n });\n\n row.appendChild(labelRow);\n row.appendChild(slider);\n\n return row;\n}\n\n/**\n * Create a toggle control with tooltip\n */\nfunction createToggle(\n label: string,\n checked: boolean,\n onChange: (checked: boolean) => void,\n tooltip?: string\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-toggle-row';\n if (tooltip) row.title = tooltip;\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const toggle = document.createElement('div');\n toggle.className = `magnify-toggle${checked ? ' active' : ''}`;\n\n toggle.addEventListener('click', () => {\n const isActive = toggle.classList.toggle('active');\n onChange(isActive);\n });\n\n row.appendChild(labelEl);\n row.appendChild(toggle);\n\n return row;\n}\n\n/**\n * Create a select dropdown control with tooltip\n */\nfunction createSelect(\n label: string,\n value: string,\n options: readonly string[] | string[],\n onChange: (value: string) => void,\n tooltip?: string,\n onInput?: (value: string) => void\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-control-row';\n if (tooltip) row.title = tooltip;\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const select = document.createElement('select');\n select.className = 'magnify-select';\n\n options.forEach(opt => {\n const option = document.createElement('option');\n option.value = opt;\n option.textContent = opt;\n if (opt === value) option.selected = true;\n select.appendChild(option);\n });\n\n select.addEventListener('change', () => {\n // Call onInput first for live preview\n if (onInput) {\n onInput(select.value);\n }\n // Then persist the setting\n onChange(select.value);\n });\n\n row.appendChild(labelEl);\n row.appendChild(select);\n\n return row;\n}\n\n/**\n * Create a color picker control with tooltip\n */\n/**\n * Create a color picker control with tooltip\n */\nfunction createColorPicker(\n label: string,\n value: string,\n onChange: (value: string) => void,\n tooltip?: string,\n onInput?: (value: string) => void\n): HTMLElement {\n const row = document.createElement('div');\n row.className = 'magnify-control-row magnify-color-row';\n if (tooltip) row.title = tooltip;\n\n const labelEl = document.createElement('label');\n labelEl.textContent = label;\n\n const colorWrapper = document.createElement('div');\n colorWrapper.className = 'magnify-color-wrapper';\n\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.className = 'magnify-color-input';\n colorInput.value = value;\n\n const colorPreview = document.createElement('span');\n colorPreview.className = 'magnify-color-preview';\n colorPreview.textContent = value;\n\n colorInput.addEventListener('input', () => {\n colorPreview.textContent = colorInput.value;\n if (onInput) onInput(colorInput.value);\n });\n\n colorInput.addEventListener('change', () => {\n onChange(colorInput.value);\n });\n\n colorWrapper.appendChild(colorInput);\n colorWrapper.appendChild(colorPreview);\n\n row.appendChild(labelEl);\n row.appendChild(colorWrapper);\n\n return row;\n}\n\n/**\n * Get current MagnifyGlass settings from app.ui.settings\n */\nfunction getSettingValue(id: string, defaultValue: any): any {\n try {\n const value = app.ui.settings.getSettingValue(id);\n return value !== undefined ? value : defaultValue;\n } catch {\n return defaultValue;\n }\n}\n\n/**\n * Set a MagnifyGlass setting via app.ui.settings\n */\nfunction setSettingValue(id: string, value: any): void {\n try {\n app.ui.settings.setSettingValue(id, value);\n } catch (e) {\n console.warn(`Failed to set setting ${id}:`, e);\n }\n}\n\n/** Storage key for section states */\nconst SECTION_STATE_KEY = 'magnifyglass-sidebar-sections';\n\n/**\n * Get saved section states from localStorage\n */\nfunction getSectionStates(): Record {\n try {\n const saved = localStorage.getItem(SECTION_STATE_KEY);\n return saved ? JSON.parse(saved) : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Save section state to localStorage\n */\nfunction saveSectionState(title: string, collapsed: boolean): void {\n try {\n const states = getSectionStates();\n states[title] = collapsed;\n localStorage.setItem(SECTION_STATE_KEY, JSON.stringify(states));\n } catch {\n // Ignore storage errors\n }\n}\n\n/**\n * Create a collapsible section with persistent state\n */\nfunction createSection(title: string, defaultCollapsed: boolean = false): { section: HTMLElement; body: HTMLElement } {\n const section = document.createElement('div');\n section.className = 'magnify-sidebar-section';\n\n // Check if we have a saved state for this section\n const savedStates = getSectionStates();\n const collapsed = savedStates[title] !== undefined ? savedStates[title] : defaultCollapsed;\n\n const header = document.createElement('div');\n header.className = `magnify-sidebar-section-header${collapsed ? ' collapsed' : ''}`;\n header.innerHTML = `${Icons.chevronDown}${title}`;\n\n const body = document.createElement('div');\n body.className = `magnify-sidebar-section-body${collapsed ? ' collapsed' : ''}`;\n // Apply inline style to prevent flash before CSS loads\n if (collapsed) {\n body.style.display = 'none';\n }\n\n header.addEventListener('click', () => {\n const isCollapsed = header.classList.toggle('collapsed');\n body.classList.toggle('collapsed');\n // Also toggle inline style for immediate effect\n body.style.display = isCollapsed ? 'none' : '';\n // Save the new state\n saveSectionState(title, isCollapsed);\n });\n\n section.appendChild(header);\n section.appendChild(body);\n\n return { section, body };\n}\n\n/**\n * Render the settings panel into the container\n */\nexport function renderSettingsPanel(container: HTMLElement): void {\n // Get current hotkey values for tooltips\n const activationKey = getSettingValue('🔍MagnifyGlass.ActivationKey', 'x').toUpperCase();\n const resetKey = getSettingValue('🔍MagnifyGlass.ResetKey', 'o').toUpperCase();\n const toggleFollowKey = getSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h').toUpperCase();\n\n // ===== Glass Settings Section =====\n const glassSection = createSection('Glass Settings');\n\n glassSection.body.appendChild(createSlider('Zoom Factor',\n getSettingValue('🔍MagnifyGlass.ZoomFactor', 300), 100, 1000, 25, '%',\n (value) => setSettingValue('🔍MagnifyGlass.ZoomFactor', value),\n 'Magnification level of the glass (100-1000%)',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.zoomFactor = value / 100;\n if (mg.state.active) mg.updateMagnifiedView();\n }\n }\n ));\n\n glassSection.body.appendChild(createSlider('Glass Size',\n getSettingValue('🔍MagnifyGlass.GlassSize', 250), 50, 500, 10, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.GlassSize', value),\n 'Size of the magnifying glass in pixels',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.glassSize = value;\n mg.applyUiChanges();\n if (mg.state.active) mg.updateMagnifiedView();\n }\n }\n ));\n\n glassSection.body.appendChild(createSelect('Shape',\n getSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square'), GLASS_SHAPES,\n (value) => setSettingValue('🔍MagnifyGlass.GlassShape', value),\n 'Shape of the magnifying glass'\n ));\n\n glassSection.body.appendChild(createSelect('Position',\n getSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right'), GLASS_POSITIONS,\n (value) => setSettingValue('🔍MagnifyGlass.GlassPosition', value),\n `Default position of the glass. Press ${resetKey} to reset.`\n ));\n\n glassSection.body.appendChild(createSelect('Filtering',\n getSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear'), ['Linear', 'Nearest'],\n (value) => setSettingValue('🔍MagnifyGlass.TextureFiltering', value),\n 'Texture filtering: Linear (smooth) or Nearest (pixelated)'\n ));\n\n glassSection.body.appendChild(createSlider('Border Width',\n getSettingValue('🔍MagnifyGlass.BorderWidth', 2), 0, 10, 0.5, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.BorderWidth', value),\n 'Width of the glass border in pixels',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.borderWidth = value;\n mg.applyUiChanges();\n }\n }\n ));\n\n glassSection.body.appendChild(createColorPicker('Border Color',\n getSettingValue('🔍MagnifyGlass.BorderColor', '#ffffff'),\n (value) => setSettingValue('🔍MagnifyGlass.BorderColor', value),\n 'Color of the glass border',\n (value) => {\n const mg = (window as any).comfyUIMagnifyGlass;\n if (mg?.config) {\n mg.config.borderColor = value;\n mg.applyUiChanges();\n }\n }\n ));\n\n glassSection.body.appendChild(createToggle('Show Border',\n getSettingValue('🔍MagnifyGlass.BorderEnabled', true),\n (checked) => setSettingValue('🔍MagnifyGlass.BorderEnabled', checked),\n 'Show or hide the glass border'\n ));\n\n glassSection.body.appendChild(createToggle('Follow Cursor',\n getSettingValue('🔍MagnifyGlass.FollowCursor', false),\n (checked) => setSettingValue('🔍MagnifyGlass.FollowCursor', checked),\n `Glass follows the cursor. Hotkey: ${toggleFollowKey}`\n ));\n\n glassSection.body.appendChild(createToggle('Always Active Mode',\n getSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true),\n (checked) => setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', checked),\n `When enabled, glass stays visible. Toggle with ${activationKey}`\n ));\n\n // The instruction implies this should be in glassSection, but the content suggests info panel.\n // Placing it in glassSection as per instruction, assuming createToggle is intended.\n // The condition `if (getSettingValue(\"🔍MagnifyGlass.InfoPanelEnabled\", true))` is removed\n // as it doesn't make sense for a glass setting to depend on info panel being enabled.\n // The tooltip and callback logic are kept as provided.\n // Moved to panel settings section\n // glassSection.body.appendChild(createToggle('Persist Info'...));\n\n container.appendChild(glassSection.section);\n\n // ===== Glass Hotkeys Section =====\n const hotkeySection = createSection('Glass Hotkeys', true);\n\n hotkeySection.body.appendChild(createSelect('Activation Key',\n getSettingValue('🔍MagnifyGlass.ActivationKey', 'x'), ACTIVATION_KEYS,\n (value) => setSettingValue('🔍MagnifyGlass.ActivationKey', value),\n 'Key to toggle or hold to activate the magnifying glass'\n ));\n\n hotkeySection.body.appendChild(createSelect('Reset Key',\n getSettingValue('🔍MagnifyGlass.ResetKey', 'o'), RESET_KEYS,\n (value) => setSettingValue('🔍MagnifyGlass.ResetKey', value),\n 'Key to reset glass position and disable follow cursor'\n ));\n\n hotkeySection.body.appendChild(createSelect('Toggle Follow Key',\n getSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h'), TOGGLE_FOLLOW_KEYS,\n (value) => setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', value),\n 'Key to toggle follow cursor mode'\n ));\n\n const glassAltToggle = createToggle('Require Alt Key',\n getSettingValue('🔍MagnifyGlass.AltRequired', false),\n (checked) => {\n setSettingValue('🔍MagnifyGlass.AltRequired', checked);\n // Sync all alt toggles\n container.querySelectorAll('.magnify-alt-toggle .magnify-toggle').forEach(t => {\n if (checked) t.classList.add('active');\n else t.classList.remove('active');\n });\n },\n 'Require Alt key to be held with activation key'\n );\n glassAltToggle.classList.add('magnify-alt-toggle');\n hotkeySection.body.appendChild(glassAltToggle);\n\n // Moved to Panel Hotkeys section\n // hotkeySection.body.appendChild(createToggle('Require Alt Key'...));\n\n hotkeySection.body.appendChild(createSlider('Offset Step',\n getSettingValue('🔍MagnifyGlass.OffsetStep', 10), 1, 50, 1, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.OffsetStep', value),\n 'Amount to move glass per arrow key press (pixels)'\n ));\n\n container.appendChild(hotkeySection.section);\n\n // ===== Info Panel Section =====\n const panelSection = createSection('Info Panel', true);\n\n const toggleHotkey = getSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i').toUpperCase();\n const glassToggleKey = getSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g').toUpperCase();\n const pinHotkeyVal = getSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u').toUpperCase();\n\n panelSection.body.appendChild(createToggle('Enable Panel',\n getSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true),\n (checked) => setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', checked),\n `Show info panel with node details. Toggle: ${toggleHotkey}`\n ));\n\n panelSection.body.appendChild(createToggle('Persist Info',\n getSettingValue('🔍MagnifyGlass.InfoPanelPersist', false),\n (checked) => {\n setSettingValue('🔍MagnifyGlass.InfoPanelPersist', checked);\n // Sync with UI controls if needed\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.uiManager) {\n infoPanel.uiManager.updateControlStates();\n }\n },\n 'Keep displaying the last node info when hovering empty space'\n ));\n\n panelSection.body.appendChild(createSelect('Position',\n getSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom'), PANEL_POSITIONS,\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelPosition', value),\n 'Position of the info panel relative to the glass'\n ));\n\n panelSection.body.appendChild(createSlider('Width',\n getSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300), 200, 600, 20, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelWidth', value),\n 'Width of the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelWidth'] = value;\n infoPanel.uiManager.applyStyles();\n\n // Ensure visible and reflow if active\n if (infoPanel.magnifyGlass.state.active) {\n infoPanel.uiManager.show();\n infoPanel.positionManager.positionPanel();\n }\n }\n }\n ));\n\n panelSection.body.appendChild(createSlider('Max Height',\n getSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300), 200, 1500, 50, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', value),\n 'Maximum height of the info panel',\n (value) => {\n console.log('[MaxHeight] Handler called with value:', value);\n const infoPanel = (window as any).infoPanelManager;\n console.log('[MaxHeight] infoPanelManager:', infoPanel ? 'found' : 'NOT FOUND');\n if (infoPanel?.stateManager?.state?.settings) {\n console.log('[MaxHeight] Updating setting and calling applyStyles');\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelMaxHeight'] = value;\n infoPanel.uiManager.applyStyles();\n\n // Ensure visible and reflow if active\n if (infoPanel.magnifyGlass.state.active) {\n console.log('[MaxHeight] Glass is active, calling show() and positionPanel()');\n infoPanel.uiManager.show();\n infoPanel.positionManager.positionPanel();\n } else {\n console.log('[MaxHeight] Glass is NOT active, skipping show()');\n }\n } else {\n console.warn('[MaxHeight] Check failed - stateManager/state/settings missing');\n }\n }\n ));\n\n panelSection.body.appendChild(createSlider('Font Size',\n getSettingValue('🔍MagnifyGlass.InfoPanelFontSize', 14), 8, 24, 1, 'px',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelFontSize', value),\n 'Font size of text in the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelFontSize'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createSlider('Opacity',\n getSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 95), 10, 100, 5, '%',\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', value),\n 'Background opacity of the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelOpacity'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n const fontFamilyOptions = [\n 'System Default', 'Inter', 'Roboto', 'JetBrains Mono', 'Fira Code',\n 'IBM Plex Sans', 'Space Grotesk', 'Lexend', 'Outfit', 'monospace'\n ];\n panelSection.body.appendChild(createSelect('Font Family',\n getSettingValue('🔍MagnifyGlass.InfoPanelFontFamily', 'System Default'), fontFamilyOptions,\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelFontFamily', value),\n 'Font family for the info panel text',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelFontFamily'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createColorPicker('Text Color',\n getSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280'),\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', value),\n 'Text color in the info panel',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelTextColor'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createColorPicker('Accent Color',\n getSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6'),\n (value) => setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', value),\n 'Accent color for highlights',\n (value) => {\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelAccentColor'] = value;\n infoPanel.uiManager.applyStyles();\n }\n }\n ));\n\n panelSection.body.appendChild(createToggle('Hover Controls',\n getSettingValue('🔍MagnifyGlass.ShowHoveringControls', true),\n (checked) => setSettingValue('🔍MagnifyGlass.ShowHoveringControls', checked),\n 'Show floating control buttons near the panel'\n ));\n\n panelSection.body.appendChild(createSelect('Controls Position',\n getSettingValue('🔍MagnifyGlass.ControlsPosition', 'left'),\n ['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],\n (value) => setSettingValue('🔍MagnifyGlass.ControlsPosition', value),\n 'Position of floating control buttons',\n (value) => {\n // Live update controls position\n const infoPanel = (window as any).infoPanelManager;\n if (infoPanel?.stateManager?.state?.settings) {\n infoPanel.stateManager.state.settings['🔍MagnifyGlass.ControlsPosition'] = value;\n infoPanel.uiManager.updateControlsLayout(value);\n if (infoPanel.magnifyGlass.state.active) {\n infoPanel.positionManager.positionFloatingControls();\n }\n }\n }\n ));\n\n container.appendChild(panelSection.section);\n\n // ===== Panel Hotkeys Section =====\n const panelHotkeySection = createSection('Panel Hotkeys', true);\n\n panelHotkeySection.body.appendChild(createSelect('Toggle Key',\n getSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i'), ['i', 'j', 'k', 'l', 'n', 'm'],\n (value) => setSettingValue('🔍MagnifyGlass.ToggleHotkey', value),\n 'Key to toggle info panel visibility'\n ));\n\n panelHotkeySection.body.appendChild(createSelect('Glass Toggle Key',\n getSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g'), ['g', 'f', 'v', 'b'],\n (value) => setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', value),\n 'Key to toggle glass preview in panel'\n ));\n\n panelHotkeySection.body.appendChild(createSelect('Pin Key',\n getSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u'), ['u', 'p', 'y', 't'],\n (value) => setSettingValue('🔍MagnifyGlass.PinPanelHotkey', value),\n 'Key to pin/unpin the info panel position'\n ));\n\n const panelAltToggle = createToggle('Require Alt Key',\n getSettingValue('🔍MagnifyGlass.AltRequired', false),\n (checked) => {\n setSettingValue('🔍MagnifyGlass.AltRequired', checked);\n // Sync all alt toggles\n container.querySelectorAll('.magnify-alt-toggle .magnify-toggle').forEach(t => {\n if (checked) t.classList.add('active');\n else t.classList.remove('active');\n });\n },\n 'Require Alt key to be held with hotkeys'\n );\n panelAltToggle.classList.add('magnify-alt-toggle');\n panelHotkeySection.body.appendChild(panelAltToggle);\n\n container.appendChild(panelHotkeySection.section);\n\n\n\n // ===== Button Row =====\n const buttonRow = document.createElement('div');\n buttonRow.style.padding = '16px';\n buttonRow.style.borderTop = '1px solid var(--border-color, #333)';\n buttonRow.style.display = 'flex';\n buttonRow.style.flexDirection = 'column';\n buttonRow.style.gap = '8px';\n\n // Reset Position Button\n const resetBtn = document.createElement('button');\n resetBtn.className = 'magnify-btn';\n resetBtn.style.width = '100%';\n resetBtn.innerHTML = `${Icons.reset} Reset Position`;\n resetBtn.title = `Reset glass position and disable follow cursor. Hotkey: ${resetKey}`;\n\n resetBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n const magnifyGlass = window.comfyUIMagnifyGlass;\n if (magnifyGlass && magnifyGlass.resetOffsets) {\n magnifyGlass.resetOffsets();\n // Update follow cursor toggle in UI\n const followToggles = container.querySelectorAll('.magnify-toggle-row');\n followToggles.forEach(row => {\n const label = row.querySelector('label');\n if (label && label.textContent === 'Follow Cursor') {\n const toggle = row.querySelector('.magnify-toggle');\n if (toggle) toggle.classList.remove('active');\n }\n });\n }\n });\n\n buttonRow.appendChild(resetBtn);\n\n // Reset All Settings Button\n const resetAllBtn = document.createElement('button');\n resetAllBtn.className = 'magnify-btn magnify-btn-secondary';\n resetAllBtn.style.width = '100%';\n resetAllBtn.innerHTML = `${Icons.reset} Reset All Settings`;\n resetAllBtn.title = 'Reset all settings to their default values';\n\n resetAllBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n\n // Confirm with user\n if (!confirm('Reset all MagnifyGlass settings to defaults?')) {\n return;\n }\n\n // Default glass settings\n setSettingValue('🔍MagnifyGlass.ZoomFactor', 300);\n setSettingValue('🔍MagnifyGlass.GlassSize', 300);\n setSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square');\n setSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right');\n setSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear');\n setSettingValue('🔍MagnifyGlass.BorderWidth', 1);\n setSettingValue('🔍MagnifyGlass.BorderColor', '#6b7280');\n setSettingValue('🔍MagnifyGlass.BorderEnabled', true);\n setSettingValue('🔍MagnifyGlass.FollowCursor', false);\n setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true);\n setSettingValue('🔍MagnifyGlass.ActivationKey', 'x');\n setSettingValue('🔍MagnifyGlass.ResetKey', 'o');\n setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h');\n setSettingValue('🔍MagnifyGlass.AltRequired', false);\n setSettingValue('🔍MagnifyGlass.OffsetStep', 5);\n\n\n // Default panel settings\n setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true);\n setSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom');\n setSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300);\n setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300);\n setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 100);\n setSettingValue('🔍MagnifyGlass.InfoPanelFontSize', 14);\n setSettingValue('🔍MagnifyGlass.InfoPanelFontFamily', 'System Default');\n setSettingValue('🔍MagnifyGlass.InfoPanelPersist', false);\n setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280');\n setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6');\n setSettingValue('🔍MagnifyGlass.InfoPanelAnimations', false);\n setSettingValue('🔍MagnifyGlass.ShowHoveringControls', true);\n setSettingValue('🔍MagnifyGlass.ControlsPosition', 'left');\n setSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i');\n setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g');\n setSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u');\n\n // Also reset position\n const magnifyGlass = window.comfyUIMagnifyGlass;\n if (magnifyGlass && magnifyGlass.resetOffsets) {\n magnifyGlass.resetOffsets();\n }\n });\n\n buttonRow.appendChild(resetAllBtn);\n container.appendChild(buttonRow);\n}\n"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/web/sidebar/sidebar.js b/web/sidebar/sidebar.js
index 48e6adc..f3d4189 100644
--- a/web/sidebar/sidebar.js
+++ b/web/sidebar/sidebar.js
@@ -27,7 +27,7 @@ function renderSidebar(container) {
sidebar.style.opacity = "0";
const header = document.createElement("div");
header.className = "magnify-sidebar-header";
- header.innerHTML = `${MAGNIFY_ICON}Magnify G.
`;
+ header.innerHTML = `${MAGNIFY_ICON}Magnify
`;
sidebar.appendChild(header);
const content = document.createElement("div");
content.className = "magnify-sidebar-content";
@@ -54,7 +54,7 @@ function registerSidebar() {
app.extensionManager.registerSidebarTab({
id: "magnifyglass",
icon: "pi pi-search",
- title: "Magnify G.",
+ title: "Magnify",
tooltip: "MagnifyGlass Settings",
type: "custom",
render: (el) => {
diff --git a/web/sidebar/sidebar.js.map b/web/sidebar/sidebar.js.map
index 84ffdbd..3be734c 100644
--- a/web/sidebar/sidebar.js.map
+++ b/web/sidebar/sidebar.js.map
@@ -1 +1 @@
-{"version":3,"file":"sidebar.js","sources":["../../src/sidebar/sidebar.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Sidebar Entry Point\n * \n * Registers the MagnifyGlass sidebar tab with ComfyUI.\n */\n\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\nimport { renderSettingsPanel } from './SidebarSettings';\n\n/**\n * Magnify glass icon SVG\n */\nconst MAGNIFY_ICON = ``;\n\n/** Track if sidebar has been registered */\nlet sidebarRegistered = false;\n\n/**\n * Load sidebar CSS\n */\nfunction loadSidebarStyles(onLoaded?: () => void): void {\n const existingLink = document.getElementById('magnify-sidebar-styles') as HTMLLinkElement;\n\n if (existingLink) {\n // Already exists, call callback immediately\n if (onLoaded) onLoaded();\n return;\n }\n\n const link = document.createElement('link');\n link.id = 'magnify-sidebar-styles';\n link.rel = 'stylesheet';\n link.type = 'text/css';\n link.href = 'extensions/comfyui-magnifyglass/sidebar.css';\n\n if (onLoaded) {\n link.onload = onLoaded;\n link.onerror = onLoaded; // Still show content even if CSS fails\n }\n\n document.head.appendChild(link);\n}\n\n/**\n * Render the sidebar content\n */\nfunction renderSidebar(container: HTMLElement): void {\n // Clear existing content to prevent duplicates\n container.innerHTML = '';\n\n // Create main container (hidden initially to prevent flash of unstyled content)\n const sidebar = document.createElement('div');\n sidebar.className = 'magnify-sidebar';\n sidebar.style.visibility = 'hidden';\n sidebar.style.opacity = '0';\n\n // Header\n const header = document.createElement('div');\n header.className = 'magnify-sidebar-header';\n header.innerHTML = `${MAGNIFY_ICON}Magnify G.
`;\n sidebar.appendChild(header);\n\n // Content area\n const content = document.createElement('div');\n content.className = 'magnify-sidebar-content';\n\n // Render settings panel (contains all settings)\n renderSettingsPanel(content);\n\n sidebar.appendChild(content);\n container.appendChild(sidebar);\n\n // Load styles and show sidebar after loaded\n loadSidebarStyles(() => {\n // Small delay to ensure styles are applied\n requestAnimationFrame(() => {\n sidebar.style.visibility = 'visible';\n sidebar.style.opacity = '1';\n sidebar.style.transition = 'opacity 0.1s ease-in';\n });\n });\n}\n\n/**\n * Register the sidebar tab with ComfyUI\n */\nexport function registerSidebar(): void {\n // Prevent duplicate registration\n if (sidebarRegistered) {\n return;\n }\n\n // Wait for app to be ready\n if (!app.extensionManager) {\n console.warn('MagnifyGlass: extensionManager not available, sidebar registration skipped');\n return;\n }\n\n try {\n app.extensionManager.registerSidebarTab({\n id: 'magnifyglass',\n icon: 'pi pi-search',\n title: 'Magnify G.',\n tooltip: 'MagnifyGlass Settings',\n type: 'custom',\n render: (el: HTMLElement) => {\n renderSidebar(el);\n }\n });\n\n sidebarRegistered = true;\n console.log('MagnifyGlass: Sidebar registered successfully');\n } catch (e) {\n console.warn('MagnifyGlass: Failed to register sidebar:', e);\n }\n}\n\n/**\n * Initialize sidebar (called after app setup)\n */\nexport function initSidebar(): void {\n // Register on next tick to ensure app is ready\n setTimeout(() => {\n registerSidebar();\n }, 100);\n}\n"],"names":[],"mappings":";;AAaA,MAAM,eAAe;AAGrB,IAAI,oBAAoB;AAKxB,SAAS,kBAAkB,UAA6B;AACpD,QAAM,eAAe,SAAS,eAAe,wBAAwB;AAErE,MAAI,cAAc;AAEd,QAAI,SAAU,UAAA;AACd;AAAA,EACJ;AAEA,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,KAAK;AACV,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,OAAK,OAAO;AAEZ,MAAI,UAAU;AACV,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACnB;AAEA,WAAS,KAAK,YAAY,IAAI;AAClC;AAKA,SAAS,cAAc,WAA8B;AAEjD,YAAU,YAAY;AAGtB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,UAAQ,MAAM,aAAa;AAC3B,UAAQ,MAAM,UAAU;AAGxB,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,YAAY,GAAG,YAAY;AAClC,UAAQ,YAAY,MAAM;AAG1B,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AAGpB,sBAAoB,OAAO;AAE3B,UAAQ,YAAY,OAAO;AAC3B,YAAU,YAAY,OAAO;AAG7B,oBAAkB,MAAM;AAEpB,0BAAsB,MAAM;AACxB,cAAQ,MAAM,aAAa;AAC3B,cAAQ,MAAM,UAAU;AACxB,cAAQ,MAAM,aAAa;AAAA,IAC/B,CAAC;AAAA,EACL,CAAC;AACL;AAKO,SAAS,kBAAwB;AAEpC,MAAI,mBAAmB;AACnB;AAAA,EACJ;AAGA,MAAI,CAAC,IAAI,kBAAkB;AACvB,YAAQ,KAAK,4EAA4E;AACzF;AAAA,EACJ;AAEA,MAAI;AACA,QAAI,iBAAiB,mBAAmB;AAAA,MACpC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,CAAC,OAAoB;AACzB,sBAAc,EAAE;AAAA,MACpB;AAAA,IAAA,CACH;AAED,wBAAoB;AACpB,YAAQ,IAAI,+CAA+C;AAAA,EAC/D,SAAS,GAAG;AACR,YAAQ,KAAK,6CAA6C,CAAC;AAAA,EAC/D;AACJ;AAKO,SAAS,cAAoB;AAEhC,aAAW,MAAM;AACb,oBAAA;AAAA,EACJ,GAAG,GAAG;AACV;"}
\ No newline at end of file
+{"version":3,"file":"sidebar.js","sources":["../../src/sidebar/sidebar.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Sidebar Entry Point\n * \n * Registers the MagnifyGlass sidebar tab with ComfyUI.\n */\n\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\nimport { renderSettingsPanel } from './SidebarSettings';\n\n/**\n * Magnify glass icon SVG\n */\nconst MAGNIFY_ICON = ``;\n\n/** Track if sidebar has been registered */\nlet sidebarRegistered = false;\n\n/**\n * Load sidebar CSS\n */\nfunction loadSidebarStyles(onLoaded?: () => void): void {\n const existingLink = document.getElementById('magnify-sidebar-styles') as HTMLLinkElement;\n\n if (existingLink) {\n // Already exists, call callback immediately\n if (onLoaded) onLoaded();\n return;\n }\n\n const link = document.createElement('link');\n link.id = 'magnify-sidebar-styles';\n link.rel = 'stylesheet';\n link.type = 'text/css';\n link.href = 'extensions/comfyui-magnifyglass/sidebar.css';\n\n if (onLoaded) {\n link.onload = onLoaded;\n link.onerror = onLoaded; // Still show content even if CSS fails\n }\n\n document.head.appendChild(link);\n}\n\n/**\n * Render the sidebar content\n */\nfunction renderSidebar(container: HTMLElement): void {\n // Clear existing content to prevent duplicates\n container.innerHTML = '';\n\n // Create main container (hidden initially to prevent flash of unstyled content)\n const sidebar = document.createElement('div');\n sidebar.className = 'magnify-sidebar';\n sidebar.style.visibility = 'hidden';\n sidebar.style.opacity = '0';\n\n // Header\n const header = document.createElement('div');\n header.className = 'magnify-sidebar-header';\n header.innerHTML = `${MAGNIFY_ICON}Magnify
`;\n sidebar.appendChild(header);\n\n // Content area\n const content = document.createElement('div');\n content.className = 'magnify-sidebar-content';\n\n // Render settings panel (contains all settings)\n renderSettingsPanel(content);\n\n sidebar.appendChild(content);\n container.appendChild(sidebar);\n\n // Load styles and show sidebar after loaded\n loadSidebarStyles(() => {\n // Small delay to ensure styles are applied\n requestAnimationFrame(() => {\n sidebar.style.visibility = 'visible';\n sidebar.style.opacity = '1';\n sidebar.style.transition = 'opacity 0.1s ease-in';\n });\n });\n}\n\n/**\n * Register the sidebar tab with ComfyUI\n */\nexport function registerSidebar(): void {\n // Prevent duplicate registration\n if (sidebarRegistered) {\n return;\n }\n\n // Wait for app to be ready\n if (!app.extensionManager) {\n console.warn('MagnifyGlass: extensionManager not available, sidebar registration skipped');\n return;\n }\n\n try {\n app.extensionManager.registerSidebarTab({\n id: 'magnifyglass',\n icon: 'pi pi-search',\n title: 'Magnify',\n tooltip: 'MagnifyGlass Settings',\n type: 'custom',\n render: (el: HTMLElement) => {\n renderSidebar(el);\n }\n });\n\n sidebarRegistered = true;\n console.log('MagnifyGlass: Sidebar registered successfully');\n } catch (e) {\n console.warn('MagnifyGlass: Failed to register sidebar:', e);\n }\n}\n\n/**\n * Initialize sidebar (called after app setup)\n */\nexport function initSidebar(): void {\n // Register on next tick to ensure app is ready\n setTimeout(() => {\n registerSidebar();\n }, 100);\n}\n"],"names":[],"mappings":";;AAaA,MAAM,eAAe;AAGrB,IAAI,oBAAoB;AAKxB,SAAS,kBAAkB,UAA6B;AACpD,QAAM,eAAe,SAAS,eAAe,wBAAwB;AAErE,MAAI,cAAc;AAEd,QAAI,SAAU,UAAA;AACd;AAAA,EACJ;AAEA,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,KAAK;AACV,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,OAAK,OAAO;AAEZ,MAAI,UAAU;AACV,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACnB;AAEA,WAAS,KAAK,YAAY,IAAI;AAClC;AAKA,SAAS,cAAc,WAA8B;AAEjD,YAAU,YAAY;AAGtB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,UAAQ,MAAM,aAAa;AAC3B,UAAQ,MAAM,UAAU;AAGxB,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,YAAY,GAAG,YAAY;AAClC,UAAQ,YAAY,MAAM;AAG1B,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AAGpB,sBAAoB,OAAO;AAE3B,UAAQ,YAAY,OAAO;AAC3B,YAAU,YAAY,OAAO;AAG7B,oBAAkB,MAAM;AAEpB,0BAAsB,MAAM;AACxB,cAAQ,MAAM,aAAa;AAC3B,cAAQ,MAAM,UAAU;AACxB,cAAQ,MAAM,aAAa;AAAA,IAC/B,CAAC;AAAA,EACL,CAAC;AACL;AAKO,SAAS,kBAAwB;AAEpC,MAAI,mBAAmB;AACnB;AAAA,EACJ;AAGA,MAAI,CAAC,IAAI,kBAAkB;AACvB,YAAQ,KAAK,4EAA4E;AACzF;AAAA,EACJ;AAEA,MAAI;AACA,QAAI,iBAAiB,mBAAmB;AAAA,MACpC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,CAAC,OAAoB;AACzB,sBAAc,EAAE;AAAA,MACpB;AAAA,IAAA,CACH;AAED,wBAAoB;AACpB,YAAQ,IAAI,+CAA+C;AAAA,EAC/D,SAAS,GAAG;AACR,YAAQ,KAAK,6CAA6C,CAAC;AAAA,EAC/D;AACJ;AAKO,SAAS,cAAoB;AAEhC,aAAW,MAAM;AACb,oBAAA;AAAA,EACJ,GAAG,GAAG;AACV;"}
\ No newline at end of file