From c608b92f941e5c0300183769efcd5bdb8a5e29a8 Mon Sep 17 00:00:00 2001 From: AEmotionStudio Date: Sat, 3 Jan 2026 02:08:06 -0800 Subject: [PATCH] feat: default info panel to expanded state --- src/info-panel/StateManager.ts | 2 +- web/info-panel/StateManager.js | 2 +- web/info-panel/StateManager.js.map | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/info-panel/StateManager.ts b/src/info-panel/StateManager.ts index e9fecab..ab563b2 100644 --- a/src/info-panel/StateManager.ts +++ b/src/info-panel/StateManager.ts @@ -59,7 +59,7 @@ export class StateManager { // Panel visibility and positioning isPanelVisible: false, wasPanelVisibleBeforeHide: false, - isPanelMinimized: true, + isPanelMinimized: false, isPanelPinned: false, isPanelLocked: false, // New lock state isAutoPinned: false, // Track if panel was auto-pinned by glass hide diff --git a/web/info-panel/StateManager.js b/web/info-panel/StateManager.js index fb61a74..5ed2d6c 100644 --- a/web/info-panel/StateManager.js +++ b/web/info-panel/StateManager.js @@ -11,7 +11,7 @@ class StateManager { // Panel visibility and positioning isPanelVisible: false, wasPanelVisibleBeforeHide: false, - isPanelMinimized: true, + isPanelMinimized: false, isPanelPinned: false, isPanelLocked: false, // New lock state diff --git a/web/info-panel/StateManager.js.map b/web/info-panel/StateManager.js.map index ccfb2cd..89d1b74 100644 --- a/web/info-panel/StateManager.js.map +++ b/web/info-panel/StateManager.js.map @@ -1 +1 @@ -{"version":3,"file":"StateManager.js","sources":["../../src/info-panel/StateManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel State Manager (TypeScript)\n * \n * Centralized state management for the information panel.\n */\n\nimport type { GatheredInfo, SettingsChange } from '../types/comfyui';\nimport { getSettingValue } from '../shared/utils';\nimport { DEFAULT_PANEL_SETTINGS } from '../shared/settings';\n\nexport interface InfoPanelStateData {\n // Panel visibility and positioning\n isPanelVisible: boolean;\n wasPanelVisibleBeforeHide: boolean; // Persist state across glass toggles\n isPanelMinimized: boolean;\n isPanelPinned: boolean;\n isPanelLocked: boolean; // New lock state\n isAutoPinned: boolean; // Track if panel was auto-pinned by glass hide\n pinnedPosition: { x: number; y: number };\n lastPinnedPosition: { x: number; y: number } | null; // Remember last pinned location\n\n // Glass visibility\n isGlassPreviewVisible: boolean;\n\n // Section expansion\n expandedSections: Set;\n\n // Interaction states\n isPanelHovered: boolean;\n isHoveringNode: boolean;\n\n // Auto-collapse\n autoExpandTimer: ReturnType | null;\n lastNodeId: string | number | null;\n\n // Update scheduling\n updateScheduled: boolean;\n isInitialLoading: boolean;\n\n // Current data\n currentInfo: GatheredInfo | null;\n\n // Settings cache - using index signature for dynamic ComfyUI settings\n settings: { [key: string]: string | number | boolean };\n\n // Auto-detected theme\n currentTheme: string;\n}\n\n/**\n * State Manager class.\n * Handles all state-related operations and provides clean state transitions.\n */\nexport class StateManager {\n state: InfoPanelStateData;\n\n constructor() {\n this.state = {\n // Panel visibility and positioning\n isPanelVisible: false,\n wasPanelVisibleBeforeHide: false,\n isPanelMinimized: true,\n isPanelPinned: false,\n isPanelLocked: false, // New lock state\n isAutoPinned: false, // Track if panel was auto-pinned by glass hide\n pinnedPosition: { x: 0, y: 0 },\n lastPinnedPosition: null, // Remember last pinned location\n\n // Glass visibility\n isGlassPreviewVisible: true,\n\n // Section expansion\n expandedSections: new Set(['node']),\n\n // Interaction states\n isPanelHovered: false,\n isHoveringNode: false,\n\n // Auto-collapse\n autoExpandTimer: null,\n lastNodeId: null,\n\n // Update scheduling\n updateScheduled: false,\n isInitialLoading: false,\n\n // Current data\n currentInfo: null,\n\n // Settings cache\n settings: {},\n\n // Auto-detected theme\n currentTheme: 'dark' // Will be auto-detected\n };\n\n this.initThemeDetection();\n this.loadSettings();\n }\n\n initThemeDetection(): void {\n // Detect ComfyUI's current theme\n this.detectCurrentTheme();\n\n // Force an immediate check in case we missed the initial state\n setTimeout(() => {\n if (this.detectCurrentTheme()) {\n this.notifyThemeChange();\n }\n }, 1000);\n\n // Set up mutation observer to watch for theme changes\n this.setupThemeObserver();\n\n // Listen for system theme changes as fallback\n if (window.matchMedia) {\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');\n if (prefersDark.addListener) {\n prefersDark.addListener(() => this.detectCurrentTheme());\n } else if (prefersDark.addEventListener) {\n prefersDark.addEventListener('change', () => this.detectCurrentTheme());\n }\n }\n\n // Add click listener to theme toggle buttons for immediate detection\n this.setupThemeButtonListeners();\n }\n\n detectCurrentTheme(): boolean {\n let detectedTheme = 'dark'; // Default fallback\n\n // Method 1: Try to read from ComfyUI's color palette setting\n try {\n // ComfyUI stores theme in 'Comfy.ColorPalette' setting\n const colorPalette = getSettingValue('Comfy.ColorPalette', '');\n\n if (colorPalette) {\n // Extract theme name from various possible formats\n const paletteStr = String(colorPalette).toLowerCase();\n\n if (paletteStr.includes('solarized')) {\n detectedTheme = 'solarized';\n } else if (paletteStr.includes('arc')) {\n detectedTheme = 'arc';\n } else if (paletteStr.includes('nord')) {\n detectedTheme = 'nord';\n } else if (paletteStr.includes('github')) {\n detectedTheme = 'github';\n } else if (paletteStr.includes('light')) {\n detectedTheme = 'light';\n } else if (paletteStr.includes('dark')) {\n detectedTheme = 'dark';\n }\n }\n } catch (e) {\n // Fallback to brightness-based detection\n console.warn('ComfyUI Info Panel: Could not read theme from settings, using fallback detection');\n }\n\n // Method 2: Fallback - check body/html for theme classes or data attributes\n if (detectedTheme === 'dark') {\n const body = document.body;\n const html = document.documentElement;\n\n // Check for data-theme attribute\n const dataTheme = body.getAttribute('data-theme') || html.getAttribute('data-theme');\n if (dataTheme) {\n detectedTheme = dataTheme.toLowerCase();\n }\n\n // Check for theme classes\n const classes = (body.className + ' ' + html.className).toLowerCase();\n if (classes.includes('theme-solarized') || classes.includes('solarized')) {\n detectedTheme = 'solarized';\n } else if (classes.includes('theme-arc') || classes.includes('arc-theme')) {\n detectedTheme = 'arc';\n } else if (classes.includes('theme-nord') || classes.includes('nord-theme')) {\n detectedTheme = 'nord';\n } else if (classes.includes('theme-github') || classes.includes('github-theme')) {\n detectedTheme = 'github';\n } else if (classes.includes('theme-light') || classes.includes('light-theme')) {\n detectedTheme = 'light';\n }\n }\n\n // Method 3: Last resort - brightness detection for light vs dark\n if (detectedTheme === 'dark') {\n const bodyStyles = window.getComputedStyle(document.body);\n const bgColor = bodyStyles.backgroundColor;\n if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {\n const rgbMatch = bgColor.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (rgbMatch) {\n const [, r, g, b] = rgbMatch.map(Number);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n if (brightness > 180) {\n detectedTheme = 'light';\n }\n }\n }\n }\n\n // Update theme if it changed\n if (this.state.currentTheme !== detectedTheme) {\n this.state.currentTheme = detectedTheme;\n return true; // Theme changed\n }\n\n return false; // No change\n }\n\n setupThemeObserver(): void {\n // Watch for changes to body/html classes and attributes\n const observer = new MutationObserver((mutations) => {\n let shouldCheck = false;\n mutations.forEach((mutation) => {\n if (mutation.type === 'attributes' &&\n (mutation.attributeName === 'class' ||\n mutation.attributeName === 'data-theme' ||\n mutation.attributeName === 'style')) {\n shouldCheck = true;\n }\n });\n\n if (shouldCheck && this.detectCurrentTheme()) {\n // Theme changed, update UI immediately\n this.notifyThemeChange();\n }\n });\n\n // Observe changes to body, html, and vue app\n observer.observe(document.body, { attributes: true, attributeOldValue: true });\n observer.observe(document.documentElement, { attributes: true, attributeOldValue: true });\n\n const vueApp = document.querySelector('#vue-app');\n if (vueApp) {\n observer.observe(vueApp, { attributes: true, attributeOldValue: true });\n }\n\n // Also watch for changes to any element with theme-related classes\n const themeElements = document.querySelectorAll('[class*=\"theme\"], [class*=\"dark\"], [class*=\"light\"], [data-theme]');\n themeElements.forEach(el => {\n observer.observe(el, { attributes: true, attributeOldValue: true });\n });\n\n // Periodic fallback check every 2 seconds\n setInterval(() => {\n if (this.detectCurrentTheme()) {\n this.notifyThemeChange();\n }\n }, 2000);\n }\n\n setupThemeButtonListeners(): void {\n // Listen for clicks on theme toggle buttons\n document.addEventListener('click', (e) => {\n // Check if clicked element might be a theme toggle\n const target = e.target as HTMLElement;\n const isThemeButton = target.textContent?.toLowerCase().includes('theme') ||\n target.textContent?.toLowerCase().includes('light') ||\n target.textContent?.toLowerCase().includes('dark') ||\n target.title?.toLowerCase().includes('theme') ||\n (typeof target.className === 'string' && target.className.toLowerCase().includes('theme'));\n\n if (isThemeButton) {\n setTimeout(() => {\n if (this.detectCurrentTheme()) {\n this.notifyThemeChange();\n }\n }, 100);\n }\n });\n }\n\n notifyThemeChange(): void {\n // Update settings to reflect new theme\n this.state.settings[\"🔍MagnifyGlass.InfoPanelTheme\"] = this.state.currentTheme;\n\n // Notify UI components of theme change immediately\n if (window.infoPanelManager && window.infoPanelManager.uiManager) {\n window.infoPanelManager.uiManager.updateTheme(this.state.currentTheme);\n }\n }\n\n loadSettings(): void {\n Object.keys(DEFAULT_PANEL_SETTINGS).forEach(key => {\n // Skip the theme setting since we auto-detect it\n if (key !== \"🔍MagnifyGlass.InfoPanelTheme\") {\n this.state.settings[key] = getSettingValue(key, (DEFAULT_PANEL_SETTINGS as any)[key]);\n }\n });\n\n // Set theme to auto-detected value\n this.state.settings[\"🔍MagnifyGlass.InfoPanelTheme\"] = this.state.currentTheme;\n }\n\n updateSettings(): Record {\n const oldSettings = { ...this.state.settings };\n this.loadSettings();\n\n // Return what changed for reactive updates\n const changes: Record = {};\n Object.keys(this.state.settings).forEach(key => {\n if (oldSettings[key] !== this.state.settings[key]) {\n changes[key] = {\n old: oldSettings[key],\n new: this.state.settings[key]\n };\n }\n });\n\n return changes;\n }\n\n togglePanelVisibility(): boolean {\n this.state.isPanelVisible = !this.state.isPanelVisible;\n return this.state.isPanelVisible;\n }\n\n toggleGlassPreview(): boolean {\n this.state.isGlassPreviewVisible = !this.state.isGlassPreviewVisible;\n return this.state.isGlassPreviewVisible;\n }\n\n togglePinning(): boolean {\n const wasPinned = this.state.isPanelPinned;\n this.state.isPanelPinned = !this.state.isPanelPinned;\n\n // When unpinning, also unlock and clear auto-pin flag\n if (!this.state.isPanelPinned) {\n this.state.isPanelLocked = false;\n this.state.isAutoPinned = false; // Clear auto-pin flag when manually unpinning\n } else {\n // When manually pinning, also clear auto-pin flag (this is a user-initiated pin)\n this.state.isAutoPinned = false;\n }\n\n if (this.state.isPanelPinned && this.state.lastPinnedPosition) {\n // Restore to last pinned position\n this.state.pinnedPosition = { ...this.state.lastPinnedPosition };\n } else if (!this.state.isPanelPinned && wasPinned) {\n // Save current position when unpinning\n this.state.lastPinnedPosition = { ...this.state.pinnedPosition };\n }\n\n return this.state.isPanelPinned;\n }\n\n toggleLocking(): boolean {\n // Only allow locking when pinned\n if (!this.state.isPanelPinned) {\n return false;\n }\n\n this.state.isPanelLocked = !this.state.isPanelLocked;\n return this.state.isPanelLocked;\n }\n\n toggleMinimized(): boolean {\n this.state.isPanelMinimized = !this.state.isPanelMinimized;\n return this.state.isPanelMinimized;\n }\n\n toggleSection(sectionId: string): boolean {\n if (sectionId === 'node') return false; // Node section always expanded\n\n if (this.state.expandedSections.has(sectionId)) {\n this.state.expandedSections.delete(sectionId);\n } else {\n this.state.expandedSections.add(sectionId);\n }\n return true;\n }\n\n setPinnedPosition(x: number, y: number): void {\n this.state.pinnedPosition = { x, y };\n // Also save as last pinned position for memory\n this.state.lastPinnedPosition = { x, y };\n }\n\n setCurrentInfo(info: GatheredInfo): void {\n this.state.currentInfo = info;\n }\n\n scheduleAutoCollapse(): void {\n this.clearAutoExpandTimer();\n if (this.state.settings[\"🔍MagnifyGlass.InfoPanelAnimations\"]) {\n this.state.autoExpandTimer = setTimeout(() => {\n if (!this.state.isPanelHovered) {\n this.collapseNodeSections();\n }\n }, 1500);\n }\n }\n\n clearAutoExpandTimer(): void {\n if (this.state.autoExpandTimer) {\n clearTimeout(this.state.autoExpandTimer);\n this.state.autoExpandTimer = null;\n }\n }\n\n expandNodeSections(): void {\n this.state.expandedSections.add('hoveredNode');\n this.state.expandedSections.add('node');\n this.state.expandedSections.add('cursor');\n this.state.expandedSections.add('canvas');\n this.state.expandedSections.add('magnifier');\n this.clearAutoExpandTimer();\n }\n\n collapseNodeSections(): void {\n this.state.expandedSections.delete('hoveredNode');\n this.state.expandedSections.delete('node');\n this.state.expandedSections.delete('widget');\n }\n\n cleanup(): void {\n this.clearAutoExpandTimer();\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"StateManager.js","sources":["../../src/info-panel/StateManager.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel State Manager (TypeScript)\n * \n * Centralized state management for the information panel.\n */\n\nimport type { GatheredInfo, SettingsChange } from '../types/comfyui';\nimport { getSettingValue } from '../shared/utils';\nimport { DEFAULT_PANEL_SETTINGS } from '../shared/settings';\n\nexport interface InfoPanelStateData {\n // Panel visibility and positioning\n isPanelVisible: boolean;\n wasPanelVisibleBeforeHide: boolean; // Persist state across glass toggles\n isPanelMinimized: boolean;\n isPanelPinned: boolean;\n isPanelLocked: boolean; // New lock state\n isAutoPinned: boolean; // Track if panel was auto-pinned by glass hide\n pinnedPosition: { x: number; y: number };\n lastPinnedPosition: { x: number; y: number } | null; // Remember last pinned location\n\n // Glass visibility\n isGlassPreviewVisible: boolean;\n\n // Section expansion\n expandedSections: Set;\n\n // Interaction states\n isPanelHovered: boolean;\n isHoveringNode: boolean;\n\n // Auto-collapse\n autoExpandTimer: ReturnType | null;\n lastNodeId: string | number | null;\n\n // Update scheduling\n updateScheduled: boolean;\n isInitialLoading: boolean;\n\n // Current data\n currentInfo: GatheredInfo | null;\n\n // Settings cache - using index signature for dynamic ComfyUI settings\n settings: { [key: string]: string | number | boolean };\n\n // Auto-detected theme\n currentTheme: string;\n}\n\n/**\n * State Manager class.\n * Handles all state-related operations and provides clean state transitions.\n */\nexport class StateManager {\n state: InfoPanelStateData;\n\n constructor() {\n this.state = {\n // Panel visibility and positioning\n isPanelVisible: false,\n wasPanelVisibleBeforeHide: false,\n isPanelMinimized: false,\n isPanelPinned: false,\n isPanelLocked: false, // New lock state\n isAutoPinned: false, // Track if panel was auto-pinned by glass hide\n pinnedPosition: { x: 0, y: 0 },\n lastPinnedPosition: null, // Remember last pinned location\n\n // Glass visibility\n isGlassPreviewVisible: true,\n\n // Section expansion\n expandedSections: new Set(['node']),\n\n // Interaction states\n isPanelHovered: false,\n isHoveringNode: false,\n\n // Auto-collapse\n autoExpandTimer: null,\n lastNodeId: null,\n\n // Update scheduling\n updateScheduled: false,\n isInitialLoading: false,\n\n // Current data\n currentInfo: null,\n\n // Settings cache\n settings: {},\n\n // Auto-detected theme\n currentTheme: 'dark' // Will be auto-detected\n };\n\n this.initThemeDetection();\n this.loadSettings();\n }\n\n initThemeDetection(): void {\n // Detect ComfyUI's current theme\n this.detectCurrentTheme();\n\n // Force an immediate check in case we missed the initial state\n setTimeout(() => {\n if (this.detectCurrentTheme()) {\n this.notifyThemeChange();\n }\n }, 1000);\n\n // Set up mutation observer to watch for theme changes\n this.setupThemeObserver();\n\n // Listen for system theme changes as fallback\n if (window.matchMedia) {\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');\n if (prefersDark.addListener) {\n prefersDark.addListener(() => this.detectCurrentTheme());\n } else if (prefersDark.addEventListener) {\n prefersDark.addEventListener('change', () => this.detectCurrentTheme());\n }\n }\n\n // Add click listener to theme toggle buttons for immediate detection\n this.setupThemeButtonListeners();\n }\n\n detectCurrentTheme(): boolean {\n let detectedTheme = 'dark'; // Default fallback\n\n // Method 1: Try to read from ComfyUI's color palette setting\n try {\n // ComfyUI stores theme in 'Comfy.ColorPalette' setting\n const colorPalette = getSettingValue('Comfy.ColorPalette', '');\n\n if (colorPalette) {\n // Extract theme name from various possible formats\n const paletteStr = String(colorPalette).toLowerCase();\n\n if (paletteStr.includes('solarized')) {\n detectedTheme = 'solarized';\n } else if (paletteStr.includes('arc')) {\n detectedTheme = 'arc';\n } else if (paletteStr.includes('nord')) {\n detectedTheme = 'nord';\n } else if (paletteStr.includes('github')) {\n detectedTheme = 'github';\n } else if (paletteStr.includes('light')) {\n detectedTheme = 'light';\n } else if (paletteStr.includes('dark')) {\n detectedTheme = 'dark';\n }\n }\n } catch (e) {\n // Fallback to brightness-based detection\n console.warn('ComfyUI Info Panel: Could not read theme from settings, using fallback detection');\n }\n\n // Method 2: Fallback - check body/html for theme classes or data attributes\n if (detectedTheme === 'dark') {\n const body = document.body;\n const html = document.documentElement;\n\n // Check for data-theme attribute\n const dataTheme = body.getAttribute('data-theme') || html.getAttribute('data-theme');\n if (dataTheme) {\n detectedTheme = dataTheme.toLowerCase();\n }\n\n // Check for theme classes\n const classes = (body.className + ' ' + html.className).toLowerCase();\n if (classes.includes('theme-solarized') || classes.includes('solarized')) {\n detectedTheme = 'solarized';\n } else if (classes.includes('theme-arc') || classes.includes('arc-theme')) {\n detectedTheme = 'arc';\n } else if (classes.includes('theme-nord') || classes.includes('nord-theme')) {\n detectedTheme = 'nord';\n } else if (classes.includes('theme-github') || classes.includes('github-theme')) {\n detectedTheme = 'github';\n } else if (classes.includes('theme-light') || classes.includes('light-theme')) {\n detectedTheme = 'light';\n }\n }\n\n // Method 3: Last resort - brightness detection for light vs dark\n if (detectedTheme === 'dark') {\n const bodyStyles = window.getComputedStyle(document.body);\n const bgColor = bodyStyles.backgroundColor;\n if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {\n const rgbMatch = bgColor.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (rgbMatch) {\n const [, r, g, b] = rgbMatch.map(Number);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n if (brightness > 180) {\n detectedTheme = 'light';\n }\n }\n }\n }\n\n // Update theme if it changed\n if (this.state.currentTheme !== detectedTheme) {\n this.state.currentTheme = detectedTheme;\n return true; // Theme changed\n }\n\n return false; // No change\n }\n\n setupThemeObserver(): void {\n // Watch for changes to body/html classes and attributes\n const observer = new MutationObserver((mutations) => {\n let shouldCheck = false;\n mutations.forEach((mutation) => {\n if (mutation.type === 'attributes' &&\n (mutation.attributeName === 'class' ||\n mutation.attributeName === 'data-theme' ||\n mutation.attributeName === 'style')) {\n shouldCheck = true;\n }\n });\n\n if (shouldCheck && this.detectCurrentTheme()) {\n // Theme changed, update UI immediately\n this.notifyThemeChange();\n }\n });\n\n // Observe changes to body, html, and vue app\n observer.observe(document.body, { attributes: true, attributeOldValue: true });\n observer.observe(document.documentElement, { attributes: true, attributeOldValue: true });\n\n const vueApp = document.querySelector('#vue-app');\n if (vueApp) {\n observer.observe(vueApp, { attributes: true, attributeOldValue: true });\n }\n\n // Also watch for changes to any element with theme-related classes\n const themeElements = document.querySelectorAll('[class*=\"theme\"], [class*=\"dark\"], [class*=\"light\"], [data-theme]');\n themeElements.forEach(el => {\n observer.observe(el, { attributes: true, attributeOldValue: true });\n });\n\n // Periodic fallback check every 2 seconds\n setInterval(() => {\n if (this.detectCurrentTheme()) {\n this.notifyThemeChange();\n }\n }, 2000);\n }\n\n setupThemeButtonListeners(): void {\n // Listen for clicks on theme toggle buttons\n document.addEventListener('click', (e) => {\n // Check if clicked element might be a theme toggle\n const target = e.target as HTMLElement;\n const isThemeButton = target.textContent?.toLowerCase().includes('theme') ||\n target.textContent?.toLowerCase().includes('light') ||\n target.textContent?.toLowerCase().includes('dark') ||\n target.title?.toLowerCase().includes('theme') ||\n (typeof target.className === 'string' && target.className.toLowerCase().includes('theme'));\n\n if (isThemeButton) {\n setTimeout(() => {\n if (this.detectCurrentTheme()) {\n this.notifyThemeChange();\n }\n }, 100);\n }\n });\n }\n\n notifyThemeChange(): void {\n // Update settings to reflect new theme\n this.state.settings[\"🔍MagnifyGlass.InfoPanelTheme\"] = this.state.currentTheme;\n\n // Notify UI components of theme change immediately\n if (window.infoPanelManager && window.infoPanelManager.uiManager) {\n window.infoPanelManager.uiManager.updateTheme(this.state.currentTheme);\n }\n }\n\n loadSettings(): void {\n Object.keys(DEFAULT_PANEL_SETTINGS).forEach(key => {\n // Skip the theme setting since we auto-detect it\n if (key !== \"🔍MagnifyGlass.InfoPanelTheme\") {\n this.state.settings[key] = getSettingValue(key, (DEFAULT_PANEL_SETTINGS as any)[key]);\n }\n });\n\n // Set theme to auto-detected value\n this.state.settings[\"🔍MagnifyGlass.InfoPanelTheme\"] = this.state.currentTheme;\n }\n\n updateSettings(): Record {\n const oldSettings = { ...this.state.settings };\n this.loadSettings();\n\n // Return what changed for reactive updates\n const changes: Record = {};\n Object.keys(this.state.settings).forEach(key => {\n if (oldSettings[key] !== this.state.settings[key]) {\n changes[key] = {\n old: oldSettings[key],\n new: this.state.settings[key]\n };\n }\n });\n\n return changes;\n }\n\n togglePanelVisibility(): boolean {\n this.state.isPanelVisible = !this.state.isPanelVisible;\n return this.state.isPanelVisible;\n }\n\n toggleGlassPreview(): boolean {\n this.state.isGlassPreviewVisible = !this.state.isGlassPreviewVisible;\n return this.state.isGlassPreviewVisible;\n }\n\n togglePinning(): boolean {\n const wasPinned = this.state.isPanelPinned;\n this.state.isPanelPinned = !this.state.isPanelPinned;\n\n // When unpinning, also unlock and clear auto-pin flag\n if (!this.state.isPanelPinned) {\n this.state.isPanelLocked = false;\n this.state.isAutoPinned = false; // Clear auto-pin flag when manually unpinning\n } else {\n // When manually pinning, also clear auto-pin flag (this is a user-initiated pin)\n this.state.isAutoPinned = false;\n }\n\n if (this.state.isPanelPinned && this.state.lastPinnedPosition) {\n // Restore to last pinned position\n this.state.pinnedPosition = { ...this.state.lastPinnedPosition };\n } else if (!this.state.isPanelPinned && wasPinned) {\n // Save current position when unpinning\n this.state.lastPinnedPosition = { ...this.state.pinnedPosition };\n }\n\n return this.state.isPanelPinned;\n }\n\n toggleLocking(): boolean {\n // Only allow locking when pinned\n if (!this.state.isPanelPinned) {\n return false;\n }\n\n this.state.isPanelLocked = !this.state.isPanelLocked;\n return this.state.isPanelLocked;\n }\n\n toggleMinimized(): boolean {\n this.state.isPanelMinimized = !this.state.isPanelMinimized;\n return this.state.isPanelMinimized;\n }\n\n toggleSection(sectionId: string): boolean {\n if (sectionId === 'node') return false; // Node section always expanded\n\n if (this.state.expandedSections.has(sectionId)) {\n this.state.expandedSections.delete(sectionId);\n } else {\n this.state.expandedSections.add(sectionId);\n }\n return true;\n }\n\n setPinnedPosition(x: number, y: number): void {\n this.state.pinnedPosition = { x, y };\n // Also save as last pinned position for memory\n this.state.lastPinnedPosition = { x, y };\n }\n\n setCurrentInfo(info: GatheredInfo): void {\n this.state.currentInfo = info;\n }\n\n scheduleAutoCollapse(): void {\n this.clearAutoExpandTimer();\n if (this.state.settings[\"🔍MagnifyGlass.InfoPanelAnimations\"]) {\n this.state.autoExpandTimer = setTimeout(() => {\n if (!this.state.isPanelHovered) {\n this.collapseNodeSections();\n }\n }, 1500);\n }\n }\n\n clearAutoExpandTimer(): void {\n if (this.state.autoExpandTimer) {\n clearTimeout(this.state.autoExpandTimer);\n this.state.autoExpandTimer = null;\n }\n }\n\n expandNodeSections(): void {\n this.state.expandedSections.add('hoveredNode');\n this.state.expandedSections.add('node');\n this.state.expandedSections.add('cursor');\n this.state.expandedSections.add('canvas');\n this.state.expandedSections.add('magnifier');\n this.clearAutoExpandTimer();\n }\n\n collapseNodeSections(): void {\n this.state.expandedSections.delete('hoveredNode');\n this.state.expandedSections.delete('node');\n this.state.expandedSections.delete('widget');\n }\n\n cleanup(): void {\n this.clearAutoExpandTimer();\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file