Update version to 1.4.1 and enhance README with recent improvements. Adjust magnifying glass settings for better UI integration and smart input detection. Refine info panel features including theme detection and customizable colors.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# ComfyUI-MagnifyGlass
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -14,6 +14,28 @@ ComfyUI-MagnifyGlass adds an intuitive magnifying glass to your ComfyUI canvas,
|
||||
|
||||

|
||||
|
||||
## 🔥 Recent Improvements
|
||||
|
||||
**Latest Session Updates:**
|
||||
|
||||
- **🎯 Smart Input Detection**: Hotkeys now intelligently detect when you're typing in input fields (similar to ComfyUI's native 'F' key behavior) and won't activate, eliminating the need for Alt key requirements
|
||||
- **🔓 Enhanced Panel Controls**:
|
||||
- Pin button now properly toggles between unlocked (🔓) and locked (🔒) states
|
||||
- 'U' key unlocks panel to mouse location without requiring Alt
|
||||
- 'G' key intelligently respects manual pin/lock decisions and only auto-unlocks when appropriate
|
||||
- **🎨 Visual Improvements**:
|
||||
- Updated default border colors to gray (#6b7280) for better UI integration
|
||||
- Added customizable info panel text and accent colors with smart defaults for light/dark modes
|
||||
- Automatic theme synchronization with main ComfyUI frontend for dark and light modes
|
||||
- **⚙️ Enhanced Settings**:
|
||||
- Percentage-based controls for opacity (10-100%) and zoom factor (100-1000%) for finer control
|
||||
- Fixed color picker persistence issues
|
||||
- **🔄 Improved Reset Functionality**: Reset key ('O') now properly resets both magnify glass and inspector panel to default top-right positions
|
||||
- **📱 UI Polish**:
|
||||
- Inspector panel always appears above other UI elements with proper z-index layering
|
||||
- Fixed blank settings dropdowns and improved tooltip descriptions
|
||||
- Updated button tooltips to reflect current hotkey behavior
|
||||
|
||||
## 🚀 Latest Updates
|
||||
|
||||
**6/12/25 (v1.3.0)**
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-magnifyglass"
|
||||
description = "ComfyUI-MagnifyGlass: A powerful & customizable magnifying glass for ComfyUI. Zoom into canvas details with smooth controls, configurable activation, custom styles (shape, size, border) & WebGL performance."
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
+138
-26
@@ -4,7 +4,7 @@ import { app } from "../../../scripts/app.js";
|
||||
* ComfyUI Magnifying Glass
|
||||
*
|
||||
* This script adds a magnifying glass feature to ComfyUI.
|
||||
* Hold Alt+X to activate the magnifying glass and see a zoomed view of the canvas.
|
||||
* Press X to activate the magnifying glass and see a zoomed view of the canvas.
|
||||
*/
|
||||
|
||||
app.registerExtension({
|
||||
@@ -12,13 +12,13 @@ app.registerExtension({
|
||||
async setup() {
|
||||
// Default settings configuration
|
||||
const DEFAULT_SETTINGS = {
|
||||
"🔍MagnifyGlass.ZoomFactor": 3,
|
||||
"🔍MagnifyGlass.ZoomFactor": 300,
|
||||
"🔍MagnifyGlass.GlassSize": 300,
|
||||
"🔍MagnifyGlass.BorderColor": "#ffffff",
|
||||
"🔍MagnifyGlass.BorderWidth": 2,
|
||||
"🔍MagnifyGlass.BorderColor": "#6b7280",
|
||||
"🔍MagnifyGlass.BorderWidth": 1,
|
||||
"🔍MagnifyGlass.ActivationKey": "x",
|
||||
"🔍MagnifyGlass.AltRequired": false,
|
||||
"🔍MagnifyGlass.FollowCursor": true,
|
||||
"🔍MagnifyGlass.FollowCursor": false,
|
||||
"🔍MagnifyGlass.DebugMode": false,
|
||||
"🔍MagnifyGlass.OffsetStep": 5,
|
||||
"🔍MagnifyGlass.GlassPosition": "Top-Right",
|
||||
@@ -26,7 +26,7 @@ app.registerExtension({
|
||||
"🔍MagnifyGlass.GlassShape": "Rounded Square",
|
||||
"🔍MagnifyGlass.BorderEnabled": true,
|
||||
"🔍MagnifyGlass.TextureFiltering": "Linear",
|
||||
"🔍MagnifyGlass.AlwaysActiveMode": false,
|
||||
"🔍MagnifyGlass.AlwaysActiveMode": true,
|
||||
"🔍MagnifyGlass.ToggleFollowCursorKey": "h",
|
||||
};
|
||||
|
||||
@@ -94,7 +94,7 @@ app.registerExtension({
|
||||
// Attach event handlers
|
||||
this.eventHandler.attachListeners();
|
||||
|
||||
this.debugger.log(`Initialized (WebGL). Hold ${this.config.altRequired ? 'Alt+' : ''}${this.config.activationKey.toUpperCase()} to activate. Arrow keys to adjust offset, R to reset.`);
|
||||
this.debugger.log(`Initialized (WebGL) with Smart Input Detection. Press ${this.config.altRequired ? 'Alt+' : ''}${this.config.activationKey.toUpperCase()} to activate. Hotkeys disabled while typing in inputs.`);
|
||||
}
|
||||
|
||||
findLiteGraphCanvas() {
|
||||
@@ -345,7 +345,7 @@ app.registerExtension({
|
||||
|
||||
// Method to update config from settings (can be called from callbacks)
|
||||
updateConfigFromSettings() {
|
||||
this.config.zoomFactor = getSettingValue("🔍MagnifyGlass.ZoomFactor", DEFAULT_SETTINGS["🔍MagnifyGlass.ZoomFactor"]);
|
||||
this.config.zoomFactor = getSettingValue("🔍MagnifyGlass.ZoomFactor", DEFAULT_SETTINGS["🔍MagnifyGlass.ZoomFactor"]) / 100;
|
||||
this.config.glassSize = getSettingValue("🔍MagnifyGlass.GlassSize", DEFAULT_SETTINGS["🔍MagnifyGlass.GlassSize"]);
|
||||
this.config.borderColor = getSettingValue("🔍MagnifyGlass.BorderColor", DEFAULT_SETTINGS["🔍MagnifyGlass.BorderColor"]);
|
||||
this.config.borderWidth = getSettingValue("🔍MagnifyGlass.BorderWidth", DEFAULT_SETTINGS["🔍MagnifyGlass.BorderWidth"]);
|
||||
@@ -415,7 +415,40 @@ app.registerExtension({
|
||||
if (this.state.active) {
|
||||
this.updateMagnifiedView(); // Update if active
|
||||
}
|
||||
this.debugger.log("Offsets reset to 0, 0");
|
||||
|
||||
// Reset magnify glass position to top-right corner
|
||||
if (this.ui.glassDiv) {
|
||||
const glassSize = this.config.glassSize;
|
||||
const padding = 20; // Same padding as initial positioning
|
||||
this.ui.glassDiv.style.left = `${window.innerWidth - glassSize - padding}px`;
|
||||
this.ui.glassDiv.style.top = `${padding}px`;
|
||||
// Reset the activation flag so next time it uses default positioning
|
||||
this.state.wasActivatedBefore = false;
|
||||
}
|
||||
|
||||
// Reset inspector panel position to default
|
||||
if (window.comfyUIMagnifyGlassExtensions && window.comfyUIMagnifyGlassExtensions.length > 0) {
|
||||
window.comfyUIMagnifyGlassExtensions.forEach(extension => {
|
||||
if (extension && extension.stateManager) {
|
||||
// Reset panel to default position
|
||||
extension.stateManager.state.isPanelPinned = false;
|
||||
extension.stateManager.state.isPanelLocked = false;
|
||||
extension.stateManager.state.isAutoPinned = false;
|
||||
extension.stateManager.state.pinnedPosition = { x: 0, y: 0 };
|
||||
extension.stateManager.state.lastPinnedPosition = null;
|
||||
|
||||
// Update panel position and controls
|
||||
if (extension.positionManager) {
|
||||
extension.positionManager.positionPanel();
|
||||
}
|
||||
if (extension.uiManager && extension.uiManager.updateControlStates) {
|
||||
extension.uiManager.updateControlStates();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.debugger.log("Reset: Magnify glass and inspector panel positions restored to top-right defaults");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +456,7 @@ app.registerExtension({
|
||||
class ConfigManager {
|
||||
constructor() {
|
||||
// Initialize properties that will be set by reading settings
|
||||
this.zoomFactor = DEFAULT_SETTINGS["🔍MagnifyGlass.ZoomFactor"];
|
||||
this.zoomFactor = DEFAULT_SETTINGS["🔍MagnifyGlass.ZoomFactor"] / 100;
|
||||
this.glassSize = DEFAULT_SETTINGS["🔍MagnifyGlass.GlassSize"];
|
||||
this.borderColor = DEFAULT_SETTINGS["🔍MagnifyGlass.BorderColor"];
|
||||
this.borderWidth = DEFAULT_SETTINGS["🔍MagnifyGlass.BorderWidth"];
|
||||
@@ -447,7 +480,7 @@ app.registerExtension({
|
||||
|
||||
// Call this after settings are registered and potentially read by MagnifyGlass
|
||||
loadSettings() {
|
||||
this.zoomFactor = getSettingValue("🔍MagnifyGlass.ZoomFactor", this.zoomFactor);
|
||||
this.zoomFactor = getSettingValue("🔍MagnifyGlass.ZoomFactor", this.zoomFactor * 100) / 100;
|
||||
this.glassSize = getSettingValue("🔍MagnifyGlass.GlassSize", this.glassSize);
|
||||
this.borderColor = getSettingValue("🔍MagnifyGlass.BorderColor", this.borderColor);
|
||||
this.borderWidth = getSettingValue("🔍MagnifyGlass.BorderWidth", this.borderWidth);
|
||||
@@ -543,7 +576,7 @@ app.registerExtension({
|
||||
border: ${this.config.borderEnabled ? `${this.config.borderWidth}px solid ${this.config.borderColor}` : 'none'};
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
z-index: 98999;
|
||||
display: none;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
@@ -589,7 +622,7 @@ app.registerExtension({
|
||||
right: 10px;
|
||||
background: rgba(0,0,0,0.7);
|
||||
border: 1px solid #fff;
|
||||
z-index: 10000;
|
||||
z-index: 99000;
|
||||
pointer-events: none;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
@@ -931,10 +964,43 @@ app.registerExtension({
|
||||
document.addEventListener("mousemove", this.handleMouseMove.bind(this));
|
||||
}
|
||||
|
||||
// Helper function to check if user is typing in an input field
|
||||
isUserTyping() {
|
||||
const activeElement = document.activeElement;
|
||||
if (!activeElement) return false;
|
||||
|
||||
// Check if the active element is an input, textarea, or contenteditable
|
||||
const tagName = activeElement.tagName.toLowerCase();
|
||||
if (tagName === 'input' || tagName === 'textarea') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for contenteditable elements
|
||||
if (activeElement.contentEditable === 'true') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it's inside a form or has input-like classes
|
||||
if (activeElement.closest('form') ||
|
||||
activeElement.classList.contains('cm-editor') || // CodeMirror editor
|
||||
activeElement.classList.contains('monaco-editor') || // Monaco editor
|
||||
activeElement.closest('.cm-editor') ||
|
||||
activeElement.closest('.monaco-editor')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
const config = this.magnifyGlass.config;
|
||||
const state = this.magnifyGlass.state;
|
||||
|
||||
// Don't handle hotkeys if user is typing in an input field (Smart Input Detection)
|
||||
if (this.isUserTyping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Magnifier activation
|
||||
if (e.key.toLowerCase() === config.activationKey &&
|
||||
(!config.altRequired || e.altKey)) {
|
||||
@@ -982,10 +1048,43 @@ app.registerExtension({
|
||||
e.preventDefault();
|
||||
} else if (e.key.toLowerCase() === config.resetKey.toLowerCase() &&
|
||||
(!config.altRequired || e.altKey)) { // Use configured reset key & check Alt
|
||||
// Reset offsets to zero
|
||||
// Reset magnify glass offsets to zero
|
||||
config.offsetX = 0;
|
||||
config.offsetY = 0;
|
||||
offsetChanged = true;
|
||||
|
||||
// Reset magnify glass position to top-right corner
|
||||
if (this.magnifyGlass.ui.glassDiv) {
|
||||
const glassSize = config.glassSize;
|
||||
const padding = 20; // Same padding as initial positioning
|
||||
this.magnifyGlass.ui.glassDiv.style.left = `${window.innerWidth - glassSize - padding}px`;
|
||||
this.magnifyGlass.ui.glassDiv.style.top = `${padding}px`;
|
||||
// Reset the activation flag so next time it uses default positioning
|
||||
this.magnifyGlass.state.wasActivatedBefore = false;
|
||||
}
|
||||
|
||||
// Reset inspector panel position to default
|
||||
if (window.comfyUIMagnifyGlassExtensions && window.comfyUIMagnifyGlassExtensions.length > 0) {
|
||||
window.comfyUIMagnifyGlassExtensions.forEach(extension => {
|
||||
if (extension && extension.stateManager) {
|
||||
// Reset panel to default position
|
||||
extension.stateManager.state.isPanelPinned = false;
|
||||
extension.stateManager.state.isPanelLocked = false;
|
||||
extension.stateManager.state.pinnedPosition = { x: 0, y: 0 };
|
||||
extension.stateManager.state.lastPinnedPosition = null;
|
||||
|
||||
// Update panel position and controls
|
||||
if (extension.positionManager) {
|
||||
extension.positionManager.positionPanel();
|
||||
}
|
||||
if (extension.uiManager && extension.uiManager.updateControlStates) {
|
||||
extension.uiManager.updateControlStates();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.magnifyGlass.debugger.log("Reset: Magnify glass and inspector panel positions restored to top-right defaults");
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
@@ -1276,16 +1375,17 @@ app.registerExtension({
|
||||
// Add settings to the ComfyUI settings dialog
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.ZoomFactor",
|
||||
name: "🔍 Magnify Glass: Zoom Factor",
|
||||
name: "🔍 Magnify Glass: Zoom Factor (%)",
|
||||
type: "slider",
|
||||
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.ZoomFactor"],
|
||||
min: 1.0,
|
||||
max: 10.0,
|
||||
step: 0.1,
|
||||
tooltip: "Magnification level (e.g., 2.5 means 2.5x zoom).",
|
||||
min: 100,
|
||||
max: 1000,
|
||||
step: 25,
|
||||
tooltip: "Magnification level as a percentage (e.g., 300 = 3x zoom, 150 = 1.5x zoom).",
|
||||
onChange: (value) => {
|
||||
if (magnifyGlass && magnifyGlass.config) {
|
||||
magnifyGlass.config.zoomFactor = parseFloat(value);
|
||||
// Convert percentage to decimal (e.g., 300 -> 3.0)
|
||||
magnifyGlass.config.zoomFactor = value / 100;
|
||||
if (magnifyGlass.state.active) {
|
||||
magnifyGlass.updateMagnifiedView();
|
||||
}
|
||||
@@ -1320,11 +1420,11 @@ app.registerExtension({
|
||||
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.BorderWidth"],
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
tooltip: "Width of the border around the magnifying glass.",
|
||||
step: 0.1,
|
||||
tooltip: "Width of the border around the magnifying glass (supports decimal values like 0.5, 1.5, etc.).",
|
||||
onChange: (value) => {
|
||||
if (magnifyGlass && magnifyGlass.config) {
|
||||
magnifyGlass.config.borderWidth = parseInt(value, 10);
|
||||
magnifyGlass.config.borderWidth = parseFloat(value);
|
||||
magnifyGlass.applyUiChanges(); // Update border style
|
||||
}
|
||||
}
|
||||
@@ -1338,7 +1438,19 @@ app.registerExtension({
|
||||
tooltip: "Color of the border around the magnifying glass.",
|
||||
onChange: (value) => {
|
||||
if (magnifyGlass && magnifyGlass.config) {
|
||||
magnifyGlass.config.borderColor = value;
|
||||
// Ensure color value includes # symbol for valid CSS
|
||||
const normalizedColor = value && !value.startsWith('#') ? `#${value}` : value;
|
||||
magnifyGlass.config.borderColor = normalizedColor;
|
||||
|
||||
// Only save back if we actually normalized the color (to prevent recursion)
|
||||
if (value !== normalizedColor) {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.BorderColor", normalizedColor);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save normalized border color:', e);
|
||||
}
|
||||
}
|
||||
|
||||
magnifyGlass.applyUiChanges(); // Update border style
|
||||
}
|
||||
}
|
||||
@@ -1365,7 +1477,7 @@ app.registerExtension({
|
||||
type: "combo",
|
||||
options: [ { value: true, text: "Yes" }, { value: false, text: "No" } ],
|
||||
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.AltRequired"],
|
||||
tooltip: "If Yes, Alt (Windows/Linux) or Option (Mac) must be held for activation and for reset.",
|
||||
tooltip: "If Yes, Alt (Windows/Linux) or Option (Mac) must be held for activation and for reset. Not needed when 'Smart Input Detection' prevents hotkeys during typing.",
|
||||
onChange: (value) => {
|
||||
if (magnifyGlass && magnifyGlass.config) {
|
||||
magnifyGlass.config.altRequired = value;
|
||||
@@ -1410,7 +1522,7 @@ app.registerExtension({
|
||||
type: "combo",
|
||||
options: ["r", "o", "p", "k", "l"], // Example keys, can be expanded
|
||||
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.ResetKey"],
|
||||
tooltip: "The key (case-insensitive) to press to reset the view offset while the magnifier is active. Works with Alt/Option if 'Require Alt for Reset' is Yes.",
|
||||
tooltip: "The key (case-insensitive) to press to reset the magnify glass offset and inspector panel position to their default locations while the magnifier is active. Works with Alt/Option if 'Require Alt for Reset' is Yes.",
|
||||
onChange: (value) => {
|
||||
if (magnifyGlass && magnifyGlass.config) {
|
||||
magnifyGlass.config.resetKey = value.toLowerCase();
|
||||
|
||||
+431
-57
@@ -42,18 +42,19 @@ app.registerExtension({
|
||||
// Default settings configuration
|
||||
const DEFAULT_SETTINGS = {
|
||||
"🔍MagnifyGlass.InfoPanelEnabled": true,
|
||||
"🔍MagnifyGlass.InfoPanelPosition": "Left",
|
||||
"🔍MagnifyGlass.InfoPanelPosition": "Bottom",
|
||||
"🔍MagnifyGlass.InfoPanelWidth": 320,
|
||||
"🔍MagnifyGlass.InfoPanelOpacity": 0.95,
|
||||
"🔍MagnifyGlass.InfoPanelOpacity": 100,
|
||||
"🔍MagnifyGlass.InfoPanelMaxHeight": 500,
|
||||
"🔍MagnifyGlass.InfoPanelTheme": "Dark",
|
||||
"🔍MagnifyGlass.InfoPanelAnimations": true,
|
||||
"🔍MagnifyGlass.InfoPanelAnimations": false,
|
||||
"🔍MagnifyGlass.ShowInspectorTab": false,
|
||||
"🔍MagnifyGlass.ToggleHotkey": "i",
|
||||
"🔍MagnifyGlass.GlassPreviewToggleHotkey": "g",
|
||||
"🔍MagnifyGlass.PinPanelHotkey": "p",
|
||||
"🔍MagnifyGlass.PinPanelHotkey": "u",
|
||||
"🔍MagnifyGlass.ShowHoveringControls": true,
|
||||
"🔍MagnifyGlass.ControlsPosition": "bottom-centered",
|
||||
"🔍MagnifyGlass.ControlsPosition": "bottom",
|
||||
"🔍MagnifyGlass.InfoPanelTextColor": "#6b7280",
|
||||
"🔍MagnifyGlass.InfoPanelAccentColor": "#3b82f6",
|
||||
};
|
||||
|
||||
const getSettingValue = (key, defaultValue) => {
|
||||
@@ -78,6 +79,7 @@ app.registerExtension({
|
||||
isPanelMinimized: false,
|
||||
isPanelPinned: false,
|
||||
isPanelLocked: false, // New lock state
|
||||
isAutoPinned: false, // Track if panel was auto-pinned by glass hide
|
||||
pinnedPosition: { x: 0, y: 0 },
|
||||
lastPinnedPosition: null, // Remember last pinned location
|
||||
|
||||
@@ -103,16 +105,265 @@ app.registerExtension({
|
||||
currentInfo: {},
|
||||
|
||||
// Settings cache
|
||||
settings: {}
|
||||
settings: {},
|
||||
|
||||
// Auto-detected theme
|
||||
currentTheme: 'dark' // Will be auto-detected
|
||||
};
|
||||
|
||||
this.initThemeDetection();
|
||||
this.loadSettings();
|
||||
}
|
||||
|
||||
initThemeDetection() {
|
||||
// Immediate forced detection
|
||||
console.log('ComfyUI Magnify Glass: Starting theme detection...');
|
||||
|
||||
// Detect ComfyUI's current theme
|
||||
this.detectCurrentTheme();
|
||||
|
||||
// Force an immediate check in case we missed the initial state
|
||||
setTimeout(() => {
|
||||
console.log('ComfyUI Magnify Glass: Delayed theme check...');
|
||||
if (this.detectCurrentTheme()) {
|
||||
this.notifyThemeChange();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Set up mutation observer to watch for theme changes
|
||||
this.setupThemeObserver();
|
||||
|
||||
// Listen for system theme changes as fallback
|
||||
if (window.matchMedia) {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
prefersDark.addListener(() => this.detectCurrentTheme());
|
||||
}
|
||||
|
||||
// Add click listener to theme toggle buttons for immediate detection
|
||||
this.setupThemeButtonListeners();
|
||||
}
|
||||
|
||||
detectCurrentTheme() {
|
||||
let detectedTheme = 'dark'; // Default fallback
|
||||
|
||||
const body = document.body;
|
||||
const html = document.documentElement;
|
||||
const vueApp = document.querySelector('#vue-app');
|
||||
const sidebar = document.querySelector('.comfy-menu, .sidebar, .menu, [class*="sidebar"], [class*="menu"]');
|
||||
|
||||
// Get all possible theme-related elements
|
||||
const allElements = [body, html, vueApp, sidebar].filter(Boolean);
|
||||
|
||||
// Debug: Log current classes and attributes with more detail
|
||||
const debugInfo = {
|
||||
bodyClasses: Array.from(body.classList),
|
||||
htmlClasses: Array.from(html.classList),
|
||||
bodyDataTheme: body.getAttribute('data-theme'),
|
||||
htmlDataTheme: html.getAttribute('data-theme'),
|
||||
vueAppClasses: vueApp ? Array.from(vueApp.classList) : null,
|
||||
sidebarClasses: sidebar ? Array.from(sidebar.classList) : null,
|
||||
bodyBgColor: window.getComputedStyle(body).backgroundColor,
|
||||
htmlBgColor: window.getComputedStyle(html).backgroundColor,
|
||||
allComputedVars: {}
|
||||
};
|
||||
|
||||
// Check CSS variables
|
||||
const rootStyles = window.getComputedStyle(html);
|
||||
const bodyStyles = window.getComputedStyle(body);
|
||||
|
||||
// Get all CSS custom properties
|
||||
for (let i = 0; i < rootStyles.length; i++) {
|
||||
const prop = rootStyles[i];
|
||||
if (prop.startsWith('--')) {
|
||||
debugInfo.allComputedVars[prop] = rootStyles.getPropertyValue(prop).trim();
|
||||
}
|
||||
}
|
||||
|
||||
console.log('ComfyUI Magnify Glass: Comprehensive theme detection debug:', debugInfo);
|
||||
|
||||
// Method 1: Look for specific light theme indicators in CSS variables
|
||||
const primarySurface = rootStyles.getPropertyValue('--p-primary-color') ||
|
||||
rootStyles.getPropertyValue('--primary-color') ||
|
||||
bodyStyles.getPropertyValue('--p-primary-color') ||
|
||||
bodyStyles.getPropertyValue('--primary-color');
|
||||
|
||||
const surfaceGround = rootStyles.getPropertyValue('--p-surface-ground') ||
|
||||
rootStyles.getPropertyValue('--surface-ground') ||
|
||||
bodyStyles.getPropertyValue('--p-surface-ground') ||
|
||||
bodyStyles.getPropertyValue('--surface-ground');
|
||||
|
||||
const textColor = rootStyles.getPropertyValue('--p-text-color') ||
|
||||
rootStyles.getPropertyValue('--text-color') ||
|
||||
bodyStyles.getPropertyValue('--p-text-color') ||
|
||||
bodyStyles.getPropertyValue('--text-color');
|
||||
|
||||
console.log('ComfyUI Magnify Glass: CSS Variables:', { primarySurface, surfaceGround, textColor });
|
||||
|
||||
// Method 2: Analyze background colors of multiple elements
|
||||
const backgrounds = allElements.map(el => {
|
||||
const styles = window.getComputedStyle(el);
|
||||
return {
|
||||
element: el.tagName || el.className,
|
||||
backgroundColor: styles.backgroundColor,
|
||||
color: styles.color
|
||||
};
|
||||
});
|
||||
|
||||
console.log('ComfyUI Magnify Glass: Background analysis:', backgrounds);
|
||||
|
||||
// Method 3: Check for light backgrounds
|
||||
for (const bg of backgrounds) {
|
||||
if (bg.backgroundColor && bg.backgroundColor !== 'rgba(0, 0, 0, 0)' && bg.backgroundColor !== 'transparent') {
|
||||
const rgbMatch = bg.backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
||||
if (rgbMatch) {
|
||||
const [, r, g, b] = rgbMatch.map(Number);
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
console.log(`ComfyUI Magnify Glass: Element ${bg.element} brightness: ${brightness}`);
|
||||
if (brightness > 180) { // Higher threshold for light detection
|
||||
detectedTheme = 'light';
|
||||
console.log(`ComfyUI Magnify Glass: Light theme detected via background analysis of ${bg.element}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method 4: Check for theme toggle buttons state (they might indicate current theme)
|
||||
const themeButtons = document.querySelectorAll('[class*="theme"], button[title*="theme"], button[title*="Theme"], .p-button[title*="light"], .p-button[title*="Light"]');
|
||||
themeButtons.forEach(btn => {
|
||||
console.log('ComfyUI Magnify Glass: Found theme button:', {
|
||||
classes: Array.from(btn.classList),
|
||||
text: btn.textContent,
|
||||
title: btn.title,
|
||||
active: btn.classList.contains('active') || btn.classList.contains('selected')
|
||||
});
|
||||
});
|
||||
|
||||
// Method 5: Force light theme detection if we see light backgrounds
|
||||
if (detectedTheme === 'dark') {
|
||||
// Look for any element with very light background
|
||||
const lightElements = document.querySelectorAll('*');
|
||||
for (let i = 0; i < Math.min(lightElements.length, 50); i++) { // Check first 50 elements
|
||||
const el = lightElements[i];
|
||||
const styles = window.getComputedStyle(el);
|
||||
const bgColor = styles.backgroundColor;
|
||||
|
||||
if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
|
||||
const rgbMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
||||
if (rgbMatch) {
|
||||
const [, r, g, b] = rgbMatch.map(Number);
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
if (brightness > 200) { // Very light elements
|
||||
detectedTheme = 'light';
|
||||
console.log(`ComfyUI Magnify Glass: Light theme forced via element ${el.tagName}.${el.className} with brightness ${brightness}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update theme if it changed
|
||||
if (this.state.currentTheme !== detectedTheme) {
|
||||
console.log(`ComfyUI Magnify Glass: Theme changed from ${this.state.currentTheme} to ${detectedTheme}`);
|
||||
this.state.currentTheme = detectedTheme;
|
||||
return true; // Theme changed
|
||||
}
|
||||
|
||||
console.log(`ComfyUI Magnify Glass: Current theme remains: ${detectedTheme}`);
|
||||
return false; // No change
|
||||
}
|
||||
|
||||
setupThemeObserver() {
|
||||
// Watch for changes to body/html classes and attributes
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
let shouldCheck = false;
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'attributes' &&
|
||||
(mutation.attributeName === 'class' ||
|
||||
mutation.attributeName === 'data-theme' ||
|
||||
mutation.attributeName === 'style')) {
|
||||
shouldCheck = true;
|
||||
console.log('ComfyUI Magnify Glass: DOM change detected:', {
|
||||
target: mutation.target.tagName,
|
||||
attribute: mutation.attributeName,
|
||||
oldValue: mutation.oldValue,
|
||||
newValue: mutation.target.getAttribute(mutation.attributeName)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldCheck && this.detectCurrentTheme()) {
|
||||
// Theme changed, update UI immediately
|
||||
this.notifyThemeChange();
|
||||
}
|
||||
});
|
||||
|
||||
// Observe changes to body, html, and vue app
|
||||
observer.observe(document.body, { attributes: true, attributeOldValue: true });
|
||||
observer.observe(document.documentElement, { attributes: true, attributeOldValue: true });
|
||||
|
||||
const vueApp = document.querySelector('#vue-app');
|
||||
if (vueApp) {
|
||||
observer.observe(vueApp, { attributes: true, attributeOldValue: true });
|
||||
}
|
||||
|
||||
// Also watch for changes to any element with theme-related classes
|
||||
const themeElements = document.querySelectorAll('[class*="theme"], [class*="dark"], [class*="light"], [data-theme]');
|
||||
themeElements.forEach(el => {
|
||||
observer.observe(el, { attributes: true, attributeOldValue: true });
|
||||
});
|
||||
|
||||
// Periodic fallback check every 2 seconds
|
||||
setInterval(() => {
|
||||
if (this.detectCurrentTheme()) {
|
||||
this.notifyThemeChange();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
setupThemeButtonListeners() {
|
||||
// Listen for clicks on theme toggle buttons
|
||||
document.addEventListener('click', (e) => {
|
||||
// Check if clicked element might be a theme toggle
|
||||
const target = e.target;
|
||||
const isThemeButton = target.textContent?.toLowerCase().includes('theme') ||
|
||||
target.textContent?.toLowerCase().includes('light') ||
|
||||
target.textContent?.toLowerCase().includes('dark') ||
|
||||
target.title?.toLowerCase().includes('theme') ||
|
||||
target.className?.toLowerCase().includes('theme');
|
||||
|
||||
if (isThemeButton) {
|
||||
console.log('ComfyUI Magnify Glass: Theme button clicked, triggering detection');
|
||||
setTimeout(() => {
|
||||
if (this.detectCurrentTheme()) {
|
||||
this.notifyThemeChange();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
notifyThemeChange() {
|
||||
// Update settings to reflect new theme
|
||||
this.state.settings["🔍MagnifyGlass.InfoPanelTheme"] = this.state.currentTheme;
|
||||
|
||||
// Notify UI components of theme change immediately
|
||||
if (window.infoPanelManager && window.infoPanelManager.uiManager) {
|
||||
window.infoPanelManager.uiManager.updateTheme(this.state.currentTheme);
|
||||
}
|
||||
}
|
||||
|
||||
loadSettings() {
|
||||
Object.keys(DEFAULT_SETTINGS).forEach(key => {
|
||||
this.state.settings[key] = getSettingValue(key, DEFAULT_SETTINGS[key]);
|
||||
// Skip the theme setting since we auto-detect it
|
||||
if (key !== "🔍MagnifyGlass.InfoPanelTheme") {
|
||||
this.state.settings[key] = getSettingValue(key, DEFAULT_SETTINGS[key]);
|
||||
}
|
||||
});
|
||||
|
||||
// Set theme to auto-detected value
|
||||
this.state.settings["🔍MagnifyGlass.InfoPanelTheme"] = this.state.currentTheme;
|
||||
}
|
||||
|
||||
updateSettings() {
|
||||
@@ -149,9 +400,10 @@ app.registerExtension({
|
||||
const wasPinned = this.state.isPanelPinned;
|
||||
this.state.isPanelPinned = !this.state.isPanelPinned;
|
||||
|
||||
// When unpinning, also unlock
|
||||
// When unpinning, also unlock and clear auto-pin flag
|
||||
if (!this.state.isPanelPinned) {
|
||||
this.state.isPanelLocked = false;
|
||||
this.state.isAutoPinned = false; // Clear auto-pin flag when manually unpinning
|
||||
}
|
||||
|
||||
if (this.state.isPanelPinned && this.state.lastPinnedPosition) {
|
||||
@@ -266,7 +518,7 @@ app.registerExtension({
|
||||
// Main panel container
|
||||
this.elements.panel = document.createElement("div");
|
||||
this.elements.panel.id = "comfyui-magnify-info-panel-pro-v2";
|
||||
this.elements.panel.className = `magnify-info-panel theme-${this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelTheme"].toLowerCase()}`;
|
||||
this.elements.panel.className = `magnify-info-panel theme-${this.stateManager.state.currentTheme}`;
|
||||
|
||||
// Header
|
||||
this.elements.header = document.createElement("div");
|
||||
@@ -304,10 +556,10 @@ app.registerExtension({
|
||||
this.elements.controls = document.createElement("div");
|
||||
this.elements.controls.className = "floating-controls vertical-layout"; // Default to vertical
|
||||
this.elements.controls.innerHTML = `
|
||||
<button class="control-btn pin-btn" title="Pin Panel (Alt+P)" data-action="pin">🔓</button>
|
||||
<button class="control-btn pin-btn" title="Unlock Panel to Mouse Location (U)" data-action="pin">🔓</button>
|
||||
<button class="control-btn lock-btn" title="Lock Panel Position" data-action="lock">📌</button>
|
||||
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (Alt+I)" data-action="toggle-panel">👁️</button>
|
||||
<button class="control-btn glass-btn" title="Toggle Glass Preview (Alt+G)" data-action="toggle-glass">🔍</button>
|
||||
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (I)" data-action="toggle-panel">👁️</button>
|
||||
<button class="control-btn glass-btn" title="Toggle Glass Preview (G)" data-action="toggle-glass">🔍</button>
|
||||
`;
|
||||
|
||||
// Insert before the panel in the document body, not as a child
|
||||
@@ -329,6 +581,7 @@ app.registerExtension({
|
||||
if (pinBtn) {
|
||||
pinBtn.classList.toggle('active', this.stateManager.state.isPanelPinned);
|
||||
pinBtn.title = this.stateManager.state.isPanelPinned ? "Lock Panel" : "Unlock Panel";
|
||||
pinBtn.textContent = this.stateManager.state.isPanelPinned ? "🔒" : "🔓";
|
||||
}
|
||||
|
||||
if (lockBtn) {
|
||||
@@ -367,11 +620,33 @@ app.registerExtension({
|
||||
applyStyles() {
|
||||
const settings = this.stateManager.state.settings;
|
||||
|
||||
// Apply custom text color via CSS variable if provided
|
||||
const textColor = settings["🔍MagnifyGlass.InfoPanelTextColor"];
|
||||
if (textColor) {
|
||||
// Ensure color has # symbol
|
||||
const normalizedTextColor = textColor.startsWith('#') ? textColor : `#${textColor}`;
|
||||
this.elements.panel.style.setProperty('--info-panel-text-color', normalizedTextColor);
|
||||
}
|
||||
|
||||
// Apply custom accent color via CSS variable if provided
|
||||
const accentColor = settings["🔍MagnifyGlass.InfoPanelAccentColor"];
|
||||
if (accentColor) {
|
||||
// Ensure color has # symbol
|
||||
const normalizedAccentColor = accentColor.startsWith('#') ? accentColor : `#${accentColor}`;
|
||||
this.elements.panel.style.setProperty('--info-panel-accent-color', normalizedAccentColor);
|
||||
}
|
||||
|
||||
// Apply opacity setting if panel is visible (convert percentage to decimal)
|
||||
if (this.stateManager.state.isPanelVisible) {
|
||||
const opacityPercent = settings["🔍MagnifyGlass.InfoPanelOpacity"];
|
||||
this.elements.panel.style.opacity = opacityPercent / 100;
|
||||
}
|
||||
|
||||
this.elements.panel.style.cssText = `
|
||||
position: absolute;
|
||||
width: ${settings["🔍MagnifyGlass.InfoPanelWidth"]}px;
|
||||
max-height: ${settings["🔍MagnifyGlass.InfoPanelMaxHeight"]}px;
|
||||
z-index: 10001;
|
||||
z-index: 99999;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
@@ -379,6 +654,8 @@ app.registerExtension({
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
${textColor ? `--info-panel-text-color: ${textColor.startsWith('#') ? textColor : `#${textColor}`};` : ''}
|
||||
${accentColor ? `--info-panel-accent-color: ${accentColor.startsWith('#') ? accentColor : `#${accentColor}`};` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -388,6 +665,9 @@ app.registerExtension({
|
||||
if (this.elements.controls) {
|
||||
this.elements.controls.style.display = "flex";
|
||||
}
|
||||
// Apply user's opacity setting when showing (convert percentage to decimal)
|
||||
const opacityPercent = this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelOpacity"];
|
||||
this.elements.panel.style.opacity = opacityPercent / 100;
|
||||
// Trigger reflow
|
||||
this.elements.panel.offsetHeight;
|
||||
this.elements.panel.classList.add('visible');
|
||||
@@ -424,7 +704,10 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
updateTheme(newTheme) {
|
||||
this.elements.panel.className = this.elements.panel.className.replace(/theme-\w+/, `theme-${newTheme.toLowerCase()}`);
|
||||
if (this.elements.panel) {
|
||||
this.elements.panel.className = this.elements.panel.className.replace(/theme-\w+/, `theme-${newTheme.toLowerCase()}`);
|
||||
console.log(`ComfyUI Magnify Glass: Panel theme updated to ${newTheme}`);
|
||||
}
|
||||
}
|
||||
|
||||
displayInfo(info) {
|
||||
@@ -571,15 +854,16 @@ app.registerExtension({
|
||||
updateHeaderSubtitle(info) {
|
||||
const subtitleElement = this.elements.header.querySelector('.header-subtitle');
|
||||
if (subtitleElement) {
|
||||
const accentColor = this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelAccentColor"];
|
||||
if (info.hoveredNode) {
|
||||
subtitleElement.textContent = `Analyzing: ${info.hoveredNode.title}`;
|
||||
subtitleElement.style.color = '#74b9ff';
|
||||
subtitleElement.style.color = accentColor;
|
||||
} else if (info.media) {
|
||||
subtitleElement.textContent = `Media: ${info.media.tagName}`;
|
||||
subtitleElement.style.color = '#a0d468';
|
||||
subtitleElement.style.color = accentColor;
|
||||
} else if (info.connection) {
|
||||
subtitleElement.textContent = `Connection: ${info.connection.type}`;
|
||||
subtitleElement.style.color = '#fdcb6e';
|
||||
subtitleElement.style.color = accentColor;
|
||||
} else {
|
||||
subtitleElement.textContent = 'Real-time analysis';
|
||||
subtitleElement.style.color = '';
|
||||
@@ -763,18 +1047,17 @@ app.registerExtension({
|
||||
|
||||
.magnify-info-panel.theme-dark {
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.95), rgba(10, 10, 15, 0.95));
|
||||
color: #e0e0e0;
|
||||
color: var(--info-panel-text-color, #e0e0e0);
|
||||
border: 1px solid rgba(100, 100, 120, 0.3);
|
||||
}
|
||||
|
||||
.magnify-info-panel.theme-light {
|
||||
background: linear-gradient(135deg, rgba(250, 250, 255, 0.95), rgba(240, 240, 250, 0.95));
|
||||
color: #2a2a2a;
|
||||
color: var(--info-panel-text-color, #2a2a2a);
|
||||
border: 1px solid rgba(200, 200, 220, 0.4);
|
||||
}
|
||||
|
||||
.magnify-info-panel.visible {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
@@ -803,7 +1086,7 @@ app.registerExtension({
|
||||
cursor: grabbing;
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(255, 215, 0, 0.7);
|
||||
transform: scale(1.02) !important;
|
||||
z-index: 10010 !important;
|
||||
z-index: 100001 !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@@ -839,7 +1122,7 @@ app.registerExtension({
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
z-index: 10003;
|
||||
z-index: 100000;
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
@@ -1008,7 +1291,7 @@ app.registerExtension({
|
||||
|
||||
.section-header.expanded {
|
||||
background: rgba(116, 185, 255, 0.1);
|
||||
border-left: 2px solid #74b9ff;
|
||||
border-left: 2px solid var(--info-panel-accent-color, #74b9ff);
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] {
|
||||
@@ -1030,7 +1313,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-title {
|
||||
color: #74b9ff;
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -1064,7 +1347,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] .section-title {
|
||||
color: #fdcb6e;
|
||||
color: var(--info-panel-accent-color, #fdcb6e);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -1098,12 +1381,12 @@ app.registerExtension({
|
||||
|
||||
.section-header:hover .expand-icon {
|
||||
opacity: 1;
|
||||
color: #74b9ff;
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
}
|
||||
|
||||
.section-header.expanded .expand-icon {
|
||||
transform: rotate(90deg);
|
||||
color: #74b9ff;
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
@@ -1141,7 +1424,7 @@ app.registerExtension({
|
||||
.info-value {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
color: #a0d468;
|
||||
color: var(--info-panel-accent-color, #a0d468);
|
||||
background: rgba(160, 212, 104, 0.1);
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
@@ -1442,29 +1725,64 @@ app.registerExtension({
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to check if user is typing in an input field
|
||||
isUserTyping() {
|
||||
const activeElement = document.activeElement;
|
||||
if (!activeElement) return false;
|
||||
|
||||
// Check if the active element is an input, textarea, or contenteditable
|
||||
const tagName = activeElement.tagName.toLowerCase();
|
||||
if (tagName === 'input' || tagName === 'textarea') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for contenteditable elements
|
||||
if (activeElement.contentEditable === 'true') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it's inside a form or has input-like classes
|
||||
if (activeElement.closest('form') ||
|
||||
activeElement.classList.contains('cm-editor') || // CodeMirror editor
|
||||
activeElement.classList.contains('monaco-editor') || // Monaco editor
|
||||
activeElement.closest('.cm-editor') ||
|
||||
activeElement.closest('.monaco-editor')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setupHotkeyEvents() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Don't handle hotkeys if user is typing in an input field (Smart Input Detection)
|
||||
if (this.isUserTyping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle hotkeys when magnifying glass is active
|
||||
if (!magnifyGlass.state.active) return;
|
||||
|
||||
const settings = this.stateManager.state.settings;
|
||||
|
||||
// Toggle info panel
|
||||
if (e.key.toLowerCase() === settings["🔍MagnifyGlass.ToggleHotkey"].toLowerCase() && !e.repeat) {
|
||||
if (e.key.toLowerCase() === settings["🔍MagnifyGlass.ToggleHotkey"].toLowerCase() &&
|
||||
(!magnifyGlass.config.altRequired || e.altKey) && !e.repeat) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.handleControlAction('toggle-panel');
|
||||
}
|
||||
|
||||
// Toggle glass preview
|
||||
if (e.key.toLowerCase() === settings["🔍MagnifyGlass.GlassPreviewToggleHotkey"].toLowerCase() && !e.repeat) {
|
||||
if (e.key.toLowerCase() === settings["🔍MagnifyGlass.GlassPreviewToggleHotkey"].toLowerCase() &&
|
||||
(!magnifyGlass.config.altRequired || e.altKey) && !e.repeat) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.handleControlAction('toggle-glass');
|
||||
}
|
||||
|
||||
// Pin at mouse location with Alt + configured key
|
||||
if (e.key.toLowerCase() === settings["🔍MagnifyGlass.PinPanelHotkey"].toLowerCase() && e.altKey && !e.repeat) {
|
||||
// Unlock panel from magnify glass and move to current mouse location (no Alt required due to Smart Input Detection)
|
||||
if (e.key.toLowerCase() === settings["🔍MagnifyGlass.PinPanelHotkey"].toLowerCase() && !e.repeat) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.handleControlAction('pin-at-mouse');
|
||||
@@ -1754,6 +2072,14 @@ app.registerExtension({
|
||||
if (!isVisible && !this.stateManager.state.isPanelPinned) {
|
||||
this.captureCurrentPanelPosition(); // Capture current position before pinning
|
||||
this.stateManager.togglePinning();
|
||||
this.stateManager.state.isAutoPinned = true; // Mark as auto-pinned
|
||||
this.uiManager.updatePinnedState();
|
||||
}
|
||||
|
||||
// When the glass preview is shown again, only auto-unlock if NOT manually locked with 📌 button
|
||||
if (isVisible && this.stateManager.state.isPanelPinned && !this.stateManager.state.isPanelLocked) {
|
||||
this.stateManager.togglePinning(); // Unpin the panel
|
||||
this.stateManager.state.isAutoPinned = false; // Clear auto-pin flag
|
||||
this.uiManager.updatePinnedState();
|
||||
}
|
||||
|
||||
@@ -2170,10 +2496,8 @@ app.registerExtension({
|
||||
updateSettings() {
|
||||
const changes = this.stateManager.updateSettings();
|
||||
|
||||
// React to setting changes
|
||||
if (changes["🔍MagnifyGlass.InfoPanelTheme"]) {
|
||||
this.uiManager.updateTheme(changes["🔍MagnifyGlass.InfoPanelTheme"].new);
|
||||
}
|
||||
// React to setting changes (theme is now auto-detected)
|
||||
// Theme changes are handled automatically by the observer
|
||||
|
||||
if (changes["🔍MagnifyGlass.ControlsPosition"]) {
|
||||
// Update controls layout and position when position setting changes
|
||||
@@ -2199,12 +2523,15 @@ app.registerExtension({
|
||||
// Create the main manager instance
|
||||
const infoPanelManager = new ProfessionalInfoPanelManager(magnifyGlass);
|
||||
|
||||
// Store reference globally for cleanup
|
||||
// Store reference globally for cleanup and theme updates
|
||||
if (!window.comfyUIMagnifyGlassExtensions) {
|
||||
window.comfyUIMagnifyGlassExtensions = [];
|
||||
}
|
||||
window.comfyUIMagnifyGlassExtensions.push(infoPanelManager);
|
||||
|
||||
// Store main manager reference for theme updates
|
||||
window.infoPanelManager = infoPanelManager;
|
||||
|
||||
// Register settings with ComfyUI
|
||||
Object.keys(DEFAULT_SETTINGS).forEach(settingKey => {
|
||||
const settingConfig = getSettingConfig(settingKey, DEFAULT_SETTINGS[settingKey]);
|
||||
@@ -2256,13 +2583,20 @@ app.registerExtension({
|
||||
},
|
||||
"🔍MagnifyGlass.InfoPanelOpacity": {
|
||||
id: key,
|
||||
name: "📋 Magnify Glass: Info Panel Opacity",
|
||||
name: "📋 Magnify Glass: Info Panel Opacity (%)",
|
||||
type: "slider",
|
||||
defaultValue,
|
||||
min: 0.7,
|
||||
max: 1.0,
|
||||
step: 0.05,
|
||||
tooltip: "Opacity of the information panel background."
|
||||
min: 10,
|
||||
max: 100,
|
||||
step: 5,
|
||||
tooltip: "Opacity of the information panel background as a percentage (10 = very transparent, 100 = fully opaque).",
|
||||
onChange: (value) => {
|
||||
// Convert percentage to decimal and apply opacity immediately
|
||||
const opacity = value / 100;
|
||||
if (window.infoPanelManager && window.infoPanelManager.uiManager && window.infoPanelManager.uiManager.elements.panel) {
|
||||
window.infoPanelManager.uiManager.elements.panel.style.opacity = opacity;
|
||||
}
|
||||
}
|
||||
},
|
||||
"🔍MagnifyGlass.InfoPanelMaxHeight": {
|
||||
id: key,
|
||||
@@ -2274,17 +2608,7 @@ app.registerExtension({
|
||||
step: 25,
|
||||
tooltip: "Maximum height of the information panel in pixels."
|
||||
},
|
||||
"🔍MagnifyGlass.InfoPanelTheme": {
|
||||
id: key,
|
||||
name: "🎨 Magnify Glass: Info Panel Theme",
|
||||
type: "combo",
|
||||
options: [
|
||||
{ value: "Dark", text: "Dark" },
|
||||
{ value: "Light", text: "Light" }
|
||||
],
|
||||
defaultValue,
|
||||
tooltip: "Color theme for the information panel."
|
||||
},
|
||||
|
||||
"🔍MagnifyGlass.InfoPanelAnimations": {
|
||||
id: key,
|
||||
name: "🎬 Magnify Glass: Info Panel Animations",
|
||||
@@ -2325,11 +2649,11 @@ app.registerExtension({
|
||||
},
|
||||
"🔍MagnifyGlass.PinPanelHotkey": {
|
||||
id: key,
|
||||
name: "📌 Magnify Glass: Pin Panel Hotkey",
|
||||
name: "🔓 Magnify Glass: Unlock Panel to Mouse Location",
|
||||
type: "combo",
|
||||
options: ["p", "f", "x", "z", "h"],
|
||||
options: ["u", "p", "f", "x", "z"],
|
||||
defaultValue,
|
||||
tooltip: "Key to use with Alt to pin the info panel at mouse location (e.g., Alt+P)."
|
||||
tooltip: "Key to unlock the info panel from the magnify glass and move it to your current mouse position (e.g., U). Panel remains movable - use the pin button to lock it in place. Alt not required due to Smart Input Detection."
|
||||
},
|
||||
"🔍MagnifyGlass.ShowHoveringControls": {
|
||||
id: key,
|
||||
@@ -2358,6 +2682,56 @@ app.registerExtension({
|
||||
],
|
||||
defaultValue,
|
||||
tooltip: "Position of the floating control buttons relative to the info panel."
|
||||
},
|
||||
"🔍MagnifyGlass.InfoPanelTextColor": {
|
||||
id: key,
|
||||
name: "🎨 Magnify Glass: Info Panel Text Color",
|
||||
type: "color",
|
||||
defaultValue,
|
||||
tooltip: "Color of the text in the info panel. This affects all text content including titles, values, and labels.",
|
||||
onChange: (value) => {
|
||||
// Ensure color value includes # symbol for valid CSS and persistence
|
||||
const normalizedColor = value && !value.startsWith('#') ? `#${value}` : value;
|
||||
|
||||
// Only save back if we actually normalized the color (to prevent recursion)
|
||||
if (value !== normalizedColor) {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelTextColor", normalizedColor);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save normalized text color:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the color immediately
|
||||
if (window.infoPanelManager && window.infoPanelManager.uiManager) {
|
||||
window.infoPanelManager.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
},
|
||||
"🔍MagnifyGlass.InfoPanelAccentColor": {
|
||||
id: key,
|
||||
name: "🎨 Magnify Glass: Info Panel Accent Color",
|
||||
type: "color",
|
||||
defaultValue,
|
||||
tooltip: "Color used for highlighted elements like values, section titles, and active states in the info panel.",
|
||||
onChange: (value) => {
|
||||
// Ensure color value includes # symbol for valid CSS and persistence
|
||||
const normalizedColor = value && !value.startsWith('#') ? `#${value}` : value;
|
||||
|
||||
// Only save back if we actually normalized the color (to prevent recursion)
|
||||
if (value !== normalizedColor) {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelAccentColor", normalizedColor);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save normalized accent color:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the color immediately
|
||||
if (window.infoPanelManager && window.infoPanelManager.uiManager) {
|
||||
window.infoPanelManager.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user