From f4eb916914a2cf4e31c06f5495905125fc6501bb Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Tue, 21 Oct 2025 17:42:32 +0800 Subject: [PATCH] feat: standardize LoRA Manager frontend with CSS classes and improved styles - Replace inline styles with CSS classes for better maintainability - Update class names to use consistent 'lm-' prefix across components - Add comprehensive CSS stylesheet with tooltip system and responsive layouts - Improve accessibility with proper focus states and keyboard navigation - Implement hover and active state transitions for enhanced UX - Refactor expand button to use CSS classes instead of inline styles - Update test files to reflect new class naming convention --- .../lorasWidgetEvents.interactions.test.js | 10 +- web/comfyui/lm_styles.css | 449 ++++++++++++++++++ web/comfyui/lm_styles_loader.js | 37 ++ web/comfyui/loras_widget.js | 273 ++--------- web/comfyui/loras_widget_components.js | 239 +--------- web/comfyui/loras_widget_events.js | 95 ++-- web/comfyui/loras_widget_utils.js | 2 +- web/comfyui/preview_tooltip.js | 48 +- 8 files changed, 584 insertions(+), 569 deletions(-) create mode 100644 web/comfyui/lm_styles.css create mode 100644 web/comfyui/lm_styles_loader.js diff --git a/tests/frontend/components/lorasWidgetEvents.interactions.test.js b/tests/frontend/components/lorasWidgetEvents.interactions.test.js index 44d62448..099f60c8 100644 --- a/tests/frontend/components/lorasWidgetEvents.interactions.test.js +++ b/tests/frontend/components/lorasWidgetEvents.interactions.test.js @@ -28,14 +28,14 @@ vi.mock(COMPONENTS_MODULE, () => ({ describe('LoRA widget drag interactions', () => { beforeEach(() => { document.body.innerHTML = ''; - const dragStyle = document.getElementById('comfy-lora-drag-style'); + const dragStyle = document.getElementById('lm-lora-shared-styles'); if (dragStyle) { dragStyle.remove(); } }); afterEach(() => { - document.body.classList.remove('comfy-lora-dragging'); + document.body.classList.remove('lm-lora-strength-dragging'); }); it('adjusts a single LoRA strength while syncing collapsed clip strength', async () => { @@ -84,7 +84,7 @@ describe('LoRA widget drag interactions', () => { const previewSpy = { hide: vi.fn() }; const dragEl = document.createElement('div'); - dragEl.className = 'comfy-lora-entry'; + dragEl.className = 'lm-lora-entry'; document.body.append(dragEl); const widget = { @@ -95,7 +95,7 @@ describe('LoRA widget drag interactions', () => { module.initDrag(dragEl, 'Test', widget, false, previewSpy, renderSpy); dragEl.dispatchEvent(new MouseEvent('mousedown', { clientX: 50, bubbles: true })); - expect(document.body.classList.contains('comfy-lora-dragging')).toBe(true); + expect(document.body.classList.contains('lm-lora-strength-dragging')).toBe(true); document.dispatchEvent(new MouseEvent('mousemove', { clientX: 70, bubbles: true })); expect(renderSpy).toHaveBeenCalledWith(widget.value, widget); @@ -103,6 +103,6 @@ describe('LoRA widget drag interactions', () => { expect(widget.value[0].strength).not.toBe(0.5); document.dispatchEvent(new MouseEvent('mouseup')); - expect(document.body.classList.contains('comfy-lora-dragging')).toBe(false); + expect(document.body.classList.contains('lm-lora-strength-dragging')).toBe(false); }); }); diff --git a/web/comfyui/lm_styles.css b/web/comfyui/lm_styles.css new file mode 100644 index 00000000..119b3fdb --- /dev/null +++ b/web/comfyui/lm_styles.css @@ -0,0 +1,449 @@ +/* Shared styling for the LoRA Manager frontend widgets */ +.lm-tooltip { + position: fixed; + z-index: 9999; + background: rgba(0, 0, 0, 0.85); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + display: none; + overflow: hidden; + max-width: 300px; + pointer-events: none; + transition: opacity 0.15s ease; +} + +.lm-tooltip__media-container { + position: relative; + max-width: 300px; + max-height: 300px; +} + +.lm-tooltip__media { + max-width: 100%; + max-height: 300px; + object-fit: contain; + display: block; +} + +.lm-tooltip__label { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 8px; + color: white; + font-size: 13px; + font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif; + background: linear-gradient(transparent, rgba(0, 0, 0, 0.8)); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); +} + +.lm-loras-container { + display: flex; + flex-direction: column; + gap: 5px; + padding: 6px; + background-color: rgba(40, 44, 52, 0.6); + border-radius: 6px; + width: 100%; + box-sizing: border-box; + overflow: auto; + outline: none; +} + +.lm-loras-container:focus { + outline: none; +} + +.lm-lora-empty-state { + text-align: center; + padding: 20px 0; + color: rgba(226, 232, 240, 0.8); + font-style: italic; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + width: 100%; +} + +.lm-loras-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 8px; + border-bottom: 1px solid rgba(226, 232, 240, 0.2); + margin-bottom: 5px; + position: relative; + cursor: ew-resize; +} + +.lm-toggle-container { + display: flex; + align-items: center; +} + +.lm-toggle-label, +.lm-strength-label { + color: rgba(226, 232, 240, 0.8); + font-size: 13px; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +.lm-toggle-label { + margin-left: 8px; +} + +.lm-strength-label { + margin-right: 8px; + display: flex; + align-items: center; +} + +.lm-drag-hint { + margin-left: 5px; + font-size: 11px; + opacity: 0.6; + transition: opacity 0.2s ease; +} + +.lm-loras-header:hover .lm-drag-hint { + opacity: 1; +} + +.lm-lora-entry { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px; + border-radius: 6px; + transition: all 0.2s ease; + margin-bottom: 4px; + border: 1px solid transparent; + background-color: rgba(45, 55, 72, 0.7); +} + +.lm-lora-entry[data-active="false"] { + background-color: rgba(35, 40, 50, 0.5); +} + +.lm-lora-entry:hover { + background-color: rgba(50, 60, 80, 0.8); +} + +.lm-lora-entry[data-active="false"]:hover { + background-color: rgba(40, 45, 55, 0.6); +} + +.lm-lora-entry[data-selected="true"] { + background-color: rgba(66, 153, 225, 0.3) !important; + border: 1px solid rgba(66, 153, 225, 0.6) !important; + box-shadow: 0 0 0 1px rgba(66, 153, 225, 0.3) !important; +} + +.lm-lora-name { + margin-left: 4px; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(226, 232, 240, 0.9); + font-size: 13px; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +.lm-lora-entry[data-active="false"] .lm-lora-name { + color: rgba(226, 232, 240, 0.6); +} + +.lm-lora-strength-control { + display: flex; + align-items: center; + gap: 8px; +} + +.lm-lora-strength-input { + width: 50px; + min-width: 50px; + padding: 2px 4px; + border-radius: 4px; + border: 1px solid rgba(226, 232, 240, 0.2); + background: rgba(26, 32, 44, 0.9); + color: rgba(226, 232, 240, 0.9); + text-align: center; + font-size: 13px; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.lm-lora-strength-input:focus { + outline: none; + border-color: rgba(66, 153, 225, 0.9); + box-shadow: 0 0 0 2px rgba(66, 153, 225, 0.3); +} + +.lm-lora-strength-buttons { + display: flex; + flex-direction: column; + gap: 2px; +} + +.lm-lora-entry[data-active="false"] .lm-lora-strength-input { + color: rgba(226, 232, 240, 0.6); +} + +.lm-lora-arrow { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + user-select: none; + font-size: 12px; + color: rgba(226, 232, 240, 0.8); + transition: all 0.2s ease; +} + +.lm-lora-arrow:hover { + color: white; + transform: scale(1.2); +} + +.lm-lora-expand-button { + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + user-select: none; + font-size: 10px; + color: rgba(226, 232, 240, 0.7); + background-color: rgba(45, 55, 72, 0.3); + border: 1px solid rgba(226, 232, 240, 0.2); + border-radius: 3px; + transition: all 0.2s ease; + flex-shrink: 0; + padding: 0; + line-height: 1; + box-sizing: border-box; +} + +.lm-lora-expand-button:hover { + background-color: rgba(66, 153, 225, 0.2); + border-color: rgba(66, 153, 225, 0.4); + color: rgba(226, 232, 240, 0.9); + transform: scale(1.05); +} + +.lm-lora-expand-button:active { + transform: scale(0.95); + background-color: rgba(66, 153, 225, 0.3); +} + +.lm-lora-expand-button:focus { + outline: none; +} + +.lm-lora-expand-button:focus-visible { + box-shadow: 0 0 0 2px rgba(66, 153, 225, 0.5); +} + +.lm-lora-toggle { + width: 18px; + height: 18px; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s ease; + background-color: rgba(45, 55, 72, 0.7); + border: 1px solid rgba(226, 232, 240, 0.2); +} + +.lm-lora-toggle--active { + background-color: rgba(66, 153, 225, 0.9); + border-color: rgba(66, 153, 225, 0.9); +} + +.lm-lora-toggle:hover { + transform: scale(1.05); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); +} + +.lm-lora-drag-handle { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + cursor: grab; + user-select: none; + font-size: 14px; + color: rgba(226, 232, 240, 0.6); + transition: all 0.2s ease; + margin-right: 8px; + flex-shrink: 0; +} + +.lm-lora-drag-handle:hover { + color: rgba(226, 232, 240, 0.9); + transform: scale(1.1); +} + +.lm-lora-drag-handle:active { + cursor: grabbing; +} + +body.lm-lora-strength-dragging, +body.lm-lora-strength-dragging * { + cursor: ew-resize !important; + user-select: none !important; +} + +body.lm-lora-reordering, +body.lm-lora-reordering * { + cursor: grabbing !important; + user-select: none !important; +} + +.lm-lora-entry--dragging { + opacity: 0.5; + transform: scale(0.98); +} + +.lm-lora-drop-indicator { + position: absolute; + left: 0; + right: 0; + height: 3px; + background-color: rgba(66, 153, 225, 0.9); + border-radius: 2px; + opacity: 0; + transition: opacity 0.2s ease; + box-shadow: 0 0 6px rgba(66, 153, 225, 0.8); + z-index: 10; + pointer-events: none; +} + +.lm-lora-strength-wrapper { + display: flex; + align-items: center; + gap: 6px; +} + +.lm-lora-entry-left { + display: flex; + align-items: center; + flex: 1; + min-width: 0; + gap: 6px; +} + +.lm-lora-clip-entry { + margin-left: 10px; + margin-bottom: 4px; + padding: 6px; + padding-left: 20px; + border-radius: 6px; + background-color: rgba(65, 70, 90, 0.6); + border: 1px solid rgba(66, 153, 225, 0.2); + border-left: 2px solid rgba(72, 118, 255, 0.6); + display: flex; + justify-content: space-between; + align-items: center; + transition: all 0.2s ease; + margin-top: -2px; +} + +.lm-lora-clip-entry[data-active="false"] { + background-color: rgba(50, 55, 65, 0.5); + border-color: rgba(66, 153, 225, 0.1); + border-left-color: rgba(66, 153, 225, 0.3); +} + +.lm-lora-clip-entry:hover { + background-color: rgba(70, 75, 95, 0.7); +} + +.lm-lora-clip-entry[data-active="false"]:hover { + background-color: rgba(55, 60, 70, 0.6); +} + +.lm-lora-clip-entry .lm-lora-name { + font-size: 13px; + color: rgba(200, 215, 240, 0.9); + margin-left: 0; +} + +.lm-lora-clip-entry[data-active="false"] .lm-lora-name { + color: rgba(200, 215, 240, 0.6); +} + +.lm-lora-clip-entry .lm-lora-strength-input { + color: rgba(200, 215, 240, 0.9); +} + +.lm-lora-clip-entry[data-active="false"] .lm-lora-strength-input { + color: rgba(200, 215, 240, 0.6); +} + +.lm-lora-badge { + padding: 2px 6px; + border-radius: 12px; + background: rgba(66, 153, 225, 0.2); + color: rgba(226, 232, 240, 0.85); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.lm-lora-context-menu { + position: fixed; + background: rgba(26, 32, 44, 0.95); + border-radius: 8px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.35); + padding: 8px 0; + z-index: 10000; + min-width: 180px; + border: 1px solid rgba(66, 153, 225, 0.3); + backdrop-filter: blur(6px); +} + +.lm-lora-menu-item { + padding: 6px 20px; + cursor: pointer; + color: rgba(226, 232, 240, 0.9); + font-size: 13px; + user-select: none; + display: flex; + align-items: center; + gap: 8px; +} + +.lm-lora-menu-item:hover { + background-color: rgba(66, 153, 225, 0.2); +} + +.lm-lora-menu-item-icon { + width: 14px; + height: 14px; + display: flex; + align-items: center; + justify-content: center; +} + +.lm-lora-context-menu hr { + border: none; + border-top: 1px solid rgba(255, 255, 255, 0.05); + margin: 6px 0; +} diff --git a/web/comfyui/lm_styles_loader.js b/web/comfyui/lm_styles_loader.js new file mode 100644 index 00000000..a330edef --- /dev/null +++ b/web/comfyui/lm_styles_loader.js @@ -0,0 +1,37 @@ +const STYLE_ID = "lm-lora-shared-styles"; +let stylePromise = null; + +function injectStyles(cssText) { + let styleEl = document.getElementById(STYLE_ID); + if (!styleEl) { + styleEl = document.createElement("style"); + styleEl.id = STYLE_ID; + document.head.appendChild(styleEl); + } + styleEl.textContent = cssText; +} + +async function loadCssText() { + const cssUrl = new URL("./lm_styles.css", import.meta.url); + const response = await fetch(cssUrl); + if (!response.ok) { + throw new Error(`Failed to load ${cssUrl}`); + } + return await response.text(); +} + +export function ensureLmStyles() { + if (!stylePromise) { + stylePromise = loadCssText() + .then((cssText) => { + injectStyles(cssText); + return true; + }) + .catch((error) => { + console.warn("Failed to load LoRA Manager styles", error); + stylePromise = null; + return false; + }); + } + return stylePromise; +} diff --git a/web/comfyui/loras_widget.js b/web/comfyui/loras_widget.js index 58b3434c..2868b390 100644 --- a/web/comfyui/loras_widget.js +++ b/web/comfyui/loras_widget.js @@ -13,28 +13,19 @@ import { import { initDrag, createContextMenu, initHeaderDrag, initReorderDrag, handleKeyboardNavigation } from "./loras_widget_events.js"; import { forwardMiddleMouseToCanvas } from "./utils.js"; import { PreviewTooltip } from "./preview_tooltip.js"; +import { ensureLmStyles } from "./lm_styles_loader.js"; export function addLorasWidget(node, name, opts, callback) { + ensureLmStyles(); + // Create container for loras const container = document.createElement("div"); - container.className = "comfy-loras-container"; + container.className = "lm-loras-container"; forwardMiddleMouseToCanvas(container); // Set initial height using CSS variables approach const defaultHeight = 200; - - Object.assign(container.style, { - display: "flex", - flexDirection: "column", - gap: "5px", - padding: "6px", - backgroundColor: "rgba(40, 44, 52, 0.6)", - borderRadius: "6px", - width: "100%", - boxSizing: "border-box", - overflow: "auto" - }); // Initialize default value const defaultValue = opts?.defaultVal || []; @@ -50,7 +41,7 @@ export function addLorasWidget(node, name, opts, callback) { const selectLora = (loraName) => { selectedLora = loraName; // Update visual feedback for all entries - container.querySelectorAll('.comfy-lora-entry').forEach(entry => { + container.querySelectorAll('.lm-lora-entry').forEach(entry => { const entryLoraName = entry.dataset.loraName; updateEntrySelection(entry, entryLoraName === selectedLora); }); @@ -65,7 +56,6 @@ export function addLorasWidget(node, name, opts, callback) { // Make container focusable for keyboard events container.tabIndex = 0; - container.style.outline = 'none'; // Function to render loras from data const renderLoras = (value, widget) => { @@ -125,17 +115,7 @@ export function addLorasWidget(node, name, opts, callback) { // Show message when no loras are added const emptyMessage = document.createElement("div"); emptyMessage.textContent = "No LoRAs added"; - Object.assign(emptyMessage.style, { - textAlign: "center", - padding: "20px 0", - color: "rgba(226, 232, 240, 0.8)", - fontStyle: "italic", - userSelect: "none", - WebkitUserSelect: "none", - MozUserSelect: "none", - msUserSelect: "none", - width: "100%" - }); + emptyMessage.className = "lm-lora-empty-state"; container.appendChild(emptyMessage); // Set fixed height for empty state @@ -145,16 +125,7 @@ export function addLorasWidget(node, name, opts, callback) { // Create header const header = document.createElement("div"); - header.className = "comfy-loras-header"; - Object.assign(header.style, { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - padding: "4px 8px", - borderBottom: "1px solid rgba(226, 232, 240, 0.2)", - marginBottom: "5px", - position: "relative" // Added for positioning the drag hint - }); + header.className = "lm-loras-header"; // Add toggle all control const allActive = lorasData.every(lora => lora.active); @@ -170,58 +141,23 @@ export function addLorasWidget(node, name, opts, callback) { // Add label to toggle all const toggleLabel = document.createElement("div"); toggleLabel.textContent = "Toggle All"; - Object.assign(toggleLabel.style, { - color: "rgba(226, 232, 240, 0.8)", - fontSize: "13px", - marginLeft: "8px", - userSelect: "none", - WebkitUserSelect: "none", - MozUserSelect: "none", - msUserSelect: "none", - }); + toggleLabel.className = "lm-toggle-label"; const toggleContainer = document.createElement("div"); - Object.assign(toggleContainer.style, { - display: "flex", - alignItems: "center", - }); + toggleContainer.className = "lm-toggle-container"; toggleContainer.appendChild(toggleAll); toggleContainer.appendChild(toggleLabel); // Strength label with drag hint const strengthLabel = document.createElement("div"); strengthLabel.textContent = "Strength"; - Object.assign(strengthLabel.style, { - color: "rgba(226, 232, 240, 0.8)", - fontSize: "13px", - marginRight: "8px", - userSelect: "none", - WebkitUserSelect: "none", - MozUserSelect: "none", - msUserSelect: "none", - display: "flex", - alignItems: "center" - }); - + strengthLabel.className = "lm-strength-label"; + // Add drag hint icon next to strength label const dragHint = document.createElement("span"); dragHint.innerHTML = "↔"; // Simple left-right arrow as drag indicator - Object.assign(dragHint.style, { - marginLeft: "5px", - fontSize: "11px", - opacity: "0.6", - transition: "opacity 0.2s ease" - }); + dragHint.className = "lm-drag-hint"; strengthLabel.appendChild(dragHint); - - // Add hover effect to improve discoverability - header.addEventListener("mouseenter", () => { - dragHint.style.opacity = "1"; - }); - - header.addEventListener("mouseleave", () => { - dragHint.style.opacity = "0.6"; - }); header.appendChild(toggleContainer); header.appendChild(strengthLabel); @@ -243,30 +179,20 @@ export function addLorasWidget(node, name, opts, callback) { // Create the main LoRA entry const loraEl = document.createElement("div"); - loraEl.className = "comfy-lora-entry"; - Object.assign(loraEl.style, { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - padding: "6px", - borderRadius: "6px", - backgroundColor: active ? "rgba(45, 55, 72, 0.7)" : "rgba(35, 40, 50, 0.5)", - transition: "all 0.2s ease", - marginBottom: "4px", - }); + loraEl.className = "lm-lora-entry"; // Store lora name and active state in dataset for selection loraEl.dataset.loraName = name; - loraEl.dataset.active = active; + loraEl.dataset.active = active ? "true" : "false"; // Add click handler for selection loraEl.addEventListener('click', (e) => { // Skip if clicking on interactive elements - if (e.target.closest('.comfy-lora-toggle') || + if (e.target.closest('.lm-lora-toggle') || e.target.closest('input') || - e.target.closest('.comfy-lora-arrow') || - e.target.closest('.comfy-lora-drag-handle') || - e.target.closest('.comfy-lora-expand-button')) { + e.target.closest('.lm-lora-arrow') || + e.target.closest('.lm-lora-drag-handle') || + e.target.closest('.lm-lora-expand-button')) { return; } @@ -322,19 +248,7 @@ export function addLorasWidget(node, name, opts, callback) { // Create name display const nameEl = document.createElement("div"); nameEl.textContent = name; - Object.assign(nameEl.style, { - marginLeft: "4px", - flex: "1", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - color: active ? "rgba(226, 232, 240, 0.9)" : "rgba(226, 232, 240, 0.6)", - fontSize: "13px", - userSelect: "none", - WebkitUserSelect: "none", - MozUserSelect: "none", - msUserSelect: "none", - }); + nameEl.className = "lm-lora-name"; // Move preview tooltip events to nameEl instead of loraEl let previewTimer; // Timer for delayed preview @@ -355,15 +269,6 @@ export function addLorasWidget(node, name, opts, callback) { // Initialize drag functionality for strength adjustment initDrag(loraEl, name, widget, false, previewTooltip, renderLoras); - // Remove the preview tooltip events from loraEl - loraEl.onmouseenter = () => { - loraEl.style.backgroundColor = active ? "rgba(50, 60, 80, 0.8)" : "rgba(40, 45, 55, 0.6)"; - }; - - loraEl.onmouseleave = () => { - loraEl.style.backgroundColor = active ? "rgba(45, 55, 72, 0.7)" : "rgba(35, 40, 50, 0.5)"; - }; - // Add context menu event loraEl.addEventListener('contextmenu', (e) => { e.preventDefault(); @@ -373,11 +278,7 @@ export function addLorasWidget(node, name, opts, callback) { // Create strength control const strengthControl = document.createElement("div"); - Object.assign(strengthControl.style, { - display: "flex", - alignItems: "center", - gap: "8px", - }); + strengthControl.className = "lm-lora-strength-control"; // Left arrow const leftArrow = createArrowButton("left", () => { @@ -397,51 +298,21 @@ export function addLorasWidget(node, name, opts, callback) { // Strength display const strengthEl = document.createElement("input"); - strengthEl.classList.add("comfy-lora-strength-input"); + strengthEl.classList.add("lm-lora-strength-input"); strengthEl.type = "text"; strengthEl.value = typeof strength === 'number' ? strength.toFixed(2) : Number(strength).toFixed(2); strengthEl.addEventListener('pointerdown', () => { pendingFocusTarget = { name, type: "strength" }; }); - Object.assign(strengthEl.style, { - minWidth: "50px", - width: "50px", - textAlign: "center", - color: active ? "rgba(226, 232, 240, 0.9)" : "rgba(226, 232, 240, 0.6)", - fontSize: "13px", - background: "none", - border: "1px solid transparent", - padding: "2px 4px", - borderRadius: "3px", - outline: "none", - }); - - // Add hover effect - strengthEl.addEventListener('mouseenter', () => { - strengthEl.style.border = "1px solid rgba(226, 232, 240, 0.2)"; - }); - - strengthEl.addEventListener('mouseleave', () => { - if (document.activeElement !== strengthEl) { - strengthEl.style.border = "1px solid transparent"; - } - }); // Handle focus strengthEl.addEventListener('focus', () => { pendingFocusTarget = null; - strengthEl.style.border = "1px solid rgba(66, 153, 225, 0.6)"; - strengthEl.style.background = "rgba(0, 0, 0, 0.2)"; // Auto-select all content strengthEl.select(); selectLora(name); }); - strengthEl.addEventListener('blur', () => { - strengthEl.style.border = "1px solid transparent"; - strengthEl.style.background = "none"; - }); - // Handle input changes const commitStrengthValue = () => { let parsedValue = parseFloat(strengthEl.value); @@ -503,12 +374,7 @@ export function addLorasWidget(node, name, opts, callback) { // Assemble entry const leftSection = document.createElement("div"); - Object.assign(leftSection.style, { - display: "flex", - alignItems: "center", - flex: "1", - minWidth: "0", // Allow shrinking - }); + leftSection.className = "lm-lora-entry-left"; leftSection.appendChild(dragHandle); // Add drag handle first leftSection.appendChild(toggle); @@ -524,49 +390,20 @@ export function addLorasWidget(node, name, opts, callback) { if (isExpanded) { totalVisibleEntries++; const clipEl = document.createElement("div"); - clipEl.className = "comfy-lora-clip-entry"; - Object.assign(clipEl.style, { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - padding: "6px", - paddingLeft: "20px", // Indent to align with parent name - borderRadius: "6px", - backgroundColor: active ? "rgba(65, 70, 90, 0.6)" : "rgba(50, 55, 65, 0.5)", - borderLeft: "2px solid rgba(72, 118, 255, 0.6)", - transition: "all 0.2s ease", - marginBottom: "4px", - marginLeft: "10px", - marginTop: "-2px" - }); + clipEl.className = "lm-lora-clip-entry"; // Store the same lora name in clip entry dataset clipEl.dataset.loraName = name; - clipEl.dataset.active = active; + clipEl.dataset.active = active ? "true" : "false"; // Create clip name display const clipNameEl = document.createElement("div"); clipNameEl.textContent = "[clip] " + name; - Object.assign(clipNameEl.style, { - flex: "1", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - color: active ? "rgba(200, 215, 240, 0.9)" : "rgba(200, 215, 240, 0.6)", - fontSize: "13px", - userSelect: "none", - WebkitUserSelect: "none", - MozUserSelect: "none", - msUserSelect: "none", - }); + clipNameEl.className = "lm-lora-name"; // Create clip strength control const clipStrengthControl = document.createElement("div"); - Object.assign(clipStrengthControl.style, { - display: "flex", - alignItems: "center", - gap: "8px", - }); + clipStrengthControl.className = "lm-lora-strength-control"; // Left arrow for clip const clipLeftArrow = createArrowButton("left", () => { @@ -584,51 +421,21 @@ export function addLorasWidget(node, name, opts, callback) { // Clip strength display const clipStrengthEl = document.createElement("input"); - clipStrengthEl.classList.add("comfy-lora-strength-input", "comfy-lora-clip-strength-input"); + clipStrengthEl.classList.add("lm-lora-strength-input", "lm-lora-clip-strength-input"); clipStrengthEl.type = "text"; clipStrengthEl.value = typeof clipStrength === 'number' ? clipStrength.toFixed(2) : Number(clipStrength).toFixed(2); clipStrengthEl.addEventListener('pointerdown', () => { pendingFocusTarget = { name, type: "clip" }; }); - Object.assign(clipStrengthEl.style, { - minWidth: "50px", - width: "50px", - textAlign: "center", - color: active ? "rgba(200, 215, 240, 0.9)" : "rgba(200, 215, 240, 0.6)", - fontSize: "13px", - background: "none", - border: "1px solid transparent", - padding: "2px 4px", - borderRadius: "3px", - outline: "none", - }); - - // Add hover effect - clipStrengthEl.addEventListener('mouseenter', () => { - clipStrengthEl.style.border = "1px solid rgba(226, 232, 240, 0.2)"; - }); - - clipStrengthEl.addEventListener('mouseleave', () => { - if (document.activeElement !== clipStrengthEl) { - clipStrengthEl.style.border = "1px solid transparent"; - } - }); // Handle focus clipStrengthEl.addEventListener('focus', () => { pendingFocusTarget = null; - clipStrengthEl.style.border = "1px solid rgba(72, 118, 255, 0.6)"; - clipStrengthEl.style.background = "rgba(0, 0, 0, 0.2)"; // Auto-select all content clipStrengthEl.select(); selectLora(name); }); - clipStrengthEl.addEventListener('blur', () => { - clipStrengthEl.style.border = "1px solid transparent"; - clipStrengthEl.style.background = "none"; - }); - // Handle input changes const clipFocusEntry = createFocusEntry(name, "clip"); @@ -688,27 +495,13 @@ export function addLorasWidget(node, name, opts, callback) { // Assemble clip entry const clipLeftSection = document.createElement("div"); - Object.assign(clipLeftSection.style, { - display: "flex", - alignItems: "center", - flex: "1", - minWidth: "0", // Allow shrinking - }); - + clipLeftSection.className = "lm-lora-entry-left"; + clipLeftSection.appendChild(clipNameEl); - + clipEl.appendChild(clipLeftSection); clipEl.appendChild(clipStrengthControl); - // Hover effects for clip entry - clipEl.onmouseenter = () => { - clipEl.style.backgroundColor = active ? "rgba(70, 75, 95, 0.7)" : "rgba(55, 60, 70, 0.6)"; - }; - - clipEl.onmouseleave = () => { - clipEl.style.backgroundColor = active ? "rgba(65, 70, 90, 0.6)" : "rgba(50, 55, 65, 0.5)"; - }; - // Add drag functionality to clip entry initDrag(clipEl, name, widget, true, previewTooltip, renderLoras); @@ -722,7 +515,7 @@ export function addLorasWidget(node, name, opts, callback) { // After all LoRA elements are created, apply selection state as the last step // This ensures the selection state is not overwritten - container.querySelectorAll('.comfy-lora-entry').forEach(entry => { + container.querySelectorAll('.lm-lora-entry').forEach(entry => { const entryLoraName = entry.dataset.loraName; updateEntrySelection(entry, entryLoraName === selectedLora); }); @@ -733,9 +526,9 @@ export function addLorasWidget(node, name, opts, callback) { let selector = ""; if (focusTarget.type === "strength") { - selector = `.comfy-lora-entry[data-lora-name="${safeName}"] .comfy-lora-strength-input`; + selector = `.lm-lora-entry[data-lora-name="${safeName}"] .lm-lora-strength-input`; } else if (focusTarget.type === "clip") { - selector = `.comfy-lora-clip-entry[data-lora-name="${safeName}"] .comfy-lora-clip-strength-input`; + selector = `.lm-lora-clip-entry[data-lora-name="${safeName}"] .lm-lora-clip-strength-input`; } if (selector) { diff --git a/web/comfyui/loras_widget_components.js b/web/comfyui/loras_widget_components.js index 8c14526a..ffed8297 100644 --- a/web/comfyui/loras_widget_components.js +++ b/web/comfyui/loras_widget_components.js @@ -1,215 +1,80 @@ // Function to create toggle element export function createToggle(active, onChange) { const toggle = document.createElement("div"); - toggle.className = "comfy-lora-toggle"; - + toggle.className = "lm-lora-toggle"; + updateToggleStyle(toggle, active); - + toggle.addEventListener("click", (e) => { e.stopPropagation(); onChange(!active); }); - + return toggle; } // Helper function to update toggle style export function updateToggleStyle(toggleEl, active) { - Object.assign(toggleEl.style, { - width: "18px", - height: "18px", - borderRadius: "4px", - cursor: "pointer", - transition: "all 0.2s ease", - backgroundColor: active ? "rgba(66, 153, 225, 0.9)" : "rgba(45, 55, 72, 0.7)", - border: `1px solid ${active ? "rgba(66, 153, 225, 0.9)" : "rgba(226, 232, 240, 0.2)"}`, - }); - - // Add hover effect - toggleEl.onmouseenter = () => { - toggleEl.style.transform = "scale(1.05)"; - toggleEl.style.boxShadow = "0 2px 4px rgba(0,0,0,0.15)"; - }; - - toggleEl.onmouseleave = () => { - toggleEl.style.transform = "scale(1)"; - toggleEl.style.boxShadow = "none"; - }; + toggleEl.classList.toggle("lm-lora-toggle--active", active); } // Create arrow button for strength adjustment export function createArrowButton(direction, onClick) { const button = document.createElement("div"); - button.className = `comfy-lora-arrow comfy-lora-arrow-${direction}`; - - Object.assign(button.style, { - width: "16px", - height: "16px", - display: "flex", - alignItems: "center", - justifyContent: "center", - cursor: "pointer", - userSelect: "none", - fontSize: "12px", - color: "rgba(226, 232, 240, 0.8)", - transition: "all 0.2s ease", - }); - + button.className = `lm-lora-arrow lm-lora-arrow-${direction}`; button.textContent = direction === "left" ? "◀" : "▶"; - + button.addEventListener("click", (e) => { e.stopPropagation(); onClick(); }); - - // Add hover effect - button.onmouseenter = () => { - button.style.color = "white"; - button.style.transform = "scale(1.2)"; - }; - - button.onmouseleave = () => { - button.style.color = "rgba(226, 232, 240, 0.8)"; - button.style.transform = "scale(1)"; - }; - + return button; } // Function to create drag handle export function createDragHandle() { const handle = document.createElement("div"); - handle.className = "comfy-lora-drag-handle"; + handle.className = "lm-lora-drag-handle"; handle.innerHTML = "≡"; handle.title = "Drag to reorder LoRA"; - - Object.assign(handle.style, { - width: "16px", - height: "16px", - display: "flex", - alignItems: "center", - justifyContent: "center", - cursor: "grab", - userSelect: "none", - fontSize: "14px", - color: "rgba(226, 232, 240, 0.6)", - transition: "all 0.2s ease", - marginRight: "8px", - flexShrink: "0" - }); - - // Add hover effect - handle.onmouseenter = () => { - handle.style.color = "rgba(226, 232, 240, 0.9)"; - handle.style.transform = "scale(1.1)"; - }; - - handle.onmouseleave = () => { - handle.style.color = "rgba(226, 232, 240, 0.6)"; - handle.style.transform = "scale(1)"; - }; - - // Change cursor when dragging - handle.onmousedown = () => { - handle.style.cursor = "grabbing"; - }; - - handle.onmouseup = () => { - handle.style.cursor = "grab"; - }; - return handle; } // Function to create drop indicator export function createDropIndicator() { const indicator = document.createElement("div"); - indicator.className = "comfy-lora-drop-indicator"; - - Object.assign(indicator.style, { - position: "absolute", - left: "0", - right: "0", - height: "3px", - backgroundColor: "rgba(66, 153, 225, 0.9)", - borderRadius: "2px", - opacity: "0", - transition: "opacity 0.2s ease", - boxShadow: "0 0 6px rgba(66, 153, 225, 0.8)", - zIndex: "10", - pointerEvents: "none" - }); - + indicator.className = "lm-lora-drop-indicator"; return indicator; } // Function to update entry selection state export function updateEntrySelection(entryEl, isSelected) { - // Remove any conflicting styles first - entryEl.style.removeProperty('border'); - entryEl.style.removeProperty('box-shadow'); - - const baseColor = entryEl.dataset.active === 'true' ? - "rgba(45, 55, 72, 0.7)" : "rgba(35, 40, 50, 0.5)"; - const selectedColor = entryEl.dataset.active === 'true' ? - "rgba(66, 153, 225, 0.3)" : "rgba(66, 153, 225, 0.2)"; - - // Update data attribute to track selection state - entryEl.dataset.selected = isSelected ? 'true' : 'false'; - - if (isSelected) { - entryEl.style.setProperty('backgroundColor', selectedColor, 'important'); - entryEl.style.setProperty('border', "1px solid rgba(66, 153, 225, 0.6)", 'important'); - entryEl.style.setProperty('box-shadow', "0 0 0 1px rgba(66, 153, 225, 0.3)", 'important'); - } else { - entryEl.style.backgroundColor = baseColor; - entryEl.style.border = "1px solid transparent"; - entryEl.style.boxShadow = "none"; + entryEl.dataset.selected = isSelected ? "true" : "false"; + if (!isSelected) { + entryEl.style.removeProperty("background-color"); + entryEl.style.removeProperty("border"); + entryEl.style.removeProperty("box-shadow"); } } // Function to create menu item export function createMenuItem(text, icon, onClick) { - const menuItem = document.createElement('div'); - Object.assign(menuItem.style, { - padding: '6px 20px', - cursor: 'pointer', - color: 'rgba(226, 232, 240, 0.9)', - fontSize: '13px', - userSelect: 'none', - display: 'flex', - alignItems: 'center', - gap: '8px', - }); + const menuItem = document.createElement("div"); + menuItem.className = "lm-lora-menu-item"; - // Create icon element - const iconEl = document.createElement('div'); + const iconEl = document.createElement("div"); + iconEl.className = "lm-lora-menu-item-icon"; iconEl.innerHTML = icon; - Object.assign(iconEl.style, { - width: '14px', - height: '14px', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }); - // Create text element - const textEl = document.createElement('span'); + const textEl = document.createElement("span"); textEl.textContent = text; menuItem.appendChild(iconEl); menuItem.appendChild(textEl); - menuItem.addEventListener('mouseenter', () => { - menuItem.style.backgroundColor = 'rgba(66, 153, 225, 0.2)'; - }); - - menuItem.addEventListener('mouseleave', () => { - menuItem.style.backgroundColor = 'transparent'; - }); - if (onClick) { - menuItem.addEventListener('click', onClick); + menuItem.addEventListener("click", onClick); } return menuItem; @@ -218,73 +83,19 @@ export function createMenuItem(text, icon, onClick) { // Function to create expand/collapse button export function createExpandButton(isExpanded, onClick) { const button = document.createElement("button"); - button.className = "comfy-lora-expand-button"; + button.className = "lm-lora-expand-button"; button.type = "button"; button.tabIndex = -1; - - Object.assign(button.style, { - width: "20px", - height: "20px", - display: "flex", - alignItems: "center", - justifyContent: "center", - cursor: "pointer", - userSelect: "none", - fontSize: "10px", - color: "rgba(226, 232, 240, 0.7)", - backgroundColor: "rgba(45, 55, 72, 0.3)", - border: "1px solid rgba(226, 232, 240, 0.2)", - borderRadius: "3px", - transition: "all 0.2s ease", - marginLeft: "6px", - marginRight: "4px", - flexShrink: "0", - outline: "none" - }); - + // Set icon based on expanded state updateExpandButtonState(button, isExpanded); - + button.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); onClick(!isExpanded); }); - - // Add hover effects - button.addEventListener("mouseenter", () => { - button.style.backgroundColor = "rgba(66, 153, 225, 0.2)"; - button.style.borderColor = "rgba(66, 153, 225, 0.4)"; - button.style.color = "rgba(226, 232, 240, 0.9)"; - button.style.transform = "scale(1.05)"; - }); - - button.addEventListener("mouseleave", () => { - button.style.backgroundColor = "rgba(45, 55, 72, 0.3)"; - button.style.borderColor = "rgba(226, 232, 240, 0.2)"; - button.style.color = "rgba(226, 232, 240, 0.7)"; - button.style.transform = "scale(1)"; - }); - - // Add active (pressed) state - button.addEventListener("mousedown", () => { - button.style.transform = "scale(0.95)"; - button.style.backgroundColor = "rgba(66, 153, 225, 0.3)"; - }); - - button.addEventListener("mouseup", () => { - button.style.transform = "scale(1.05)"; // Return to hover state - }); - - // Add focus state for keyboard accessibility - button.addEventListener("focus", () => { - button.style.boxShadow = "0 0 0 2px rgba(66, 153, 225, 0.5)"; - }); - - button.addEventListener("blur", () => { - button.style.boxShadow = "none"; - }); - + return button; } diff --git a/web/comfyui/loras_widget_events.js b/web/comfyui/loras_widget_events.js index 85564891..06d4675b 100644 --- a/web/comfyui/loras_widget_events.js +++ b/web/comfyui/loras_widget_events.js @@ -102,27 +102,14 @@ export function initDrag(dragEl, name, widget, isClipStrength = false, previewTo let initialX = 0; let initialStrength = 0; - // Create a style element for drag cursor override if it doesn't exist - if (!document.getElementById('comfy-lora-drag-style')) { - const styleEl = document.createElement('style'); - styleEl.id = 'comfy-lora-drag-style'; - styleEl.textContent = ` - body.comfy-lora-dragging, - body.comfy-lora-dragging * { - cursor: ew-resize !important; - } - `; - document.head.appendChild(styleEl); - } - // Create a drag handler dragEl.addEventListener('mousedown', (e) => { // Skip if clicking on toggle or strength control areas - if (e.target.closest('.comfy-lora-toggle') || - e.target.closest('input') || - e.target.closest('.comfy-lora-arrow') || - e.target.closest('.comfy-lora-drag-handle') || - e.target.closest('.comfy-lora-expand-button')) { + if (e.target.closest('.lm-lora-toggle') || + e.target.closest('input') || + e.target.closest('.lm-lora-arrow') || + e.target.closest('.lm-lora-drag-handle') || + e.target.closest('.lm-lora-expand-button')) { return; } @@ -137,7 +124,7 @@ export function initDrag(dragEl, name, widget, isClipStrength = false, previewTo isDragging = true; // Add class to body to enforce cursor style globally - document.body.classList.add('comfy-lora-dragging'); + document.body.classList.add('lm-lora-strength-dragging'); // Prevent text selection during drag e.preventDefault(); @@ -166,7 +153,7 @@ export function initDrag(dragEl, name, widget, isClipStrength = false, previewTo if (isDragging) { isDragging = false; // Remove the class to restore normal cursor behavior - document.body.classList.remove('comfy-lora-dragging'); + document.body.classList.remove('lm-lora-strength-dragging'); } }); } @@ -178,12 +165,10 @@ export function initHeaderDrag(headerEl, widget, renderFunction) { let initialStrengths = []; // Add cursor style to indicate draggable - headerEl.style.cursor = 'ew-resize'; - // Create a drag handler headerEl.addEventListener('mousedown', (e) => { // Skip if clicking on toggle or other interactive elements - if (e.target.closest('.comfy-lora-toggle') || + if (e.target.closest('.lm-lora-toggle') || e.target.closest('input')) { return; } @@ -201,7 +186,7 @@ export function initHeaderDrag(headerEl, widget, renderFunction) { isDragging = true; // Add class to body to enforce cursor style globally - document.body.classList.add('comfy-lora-dragging'); + document.body.classList.add('lm-lora-strength-dragging'); // Prevent text selection during drag e.preventDefault(); @@ -225,7 +210,7 @@ export function initHeaderDrag(headerEl, widget, renderFunction) { if (isDragging) { isDragging = false; // Remove the class to restore normal cursor behavior - document.body.classList.remove('comfy-lora-dragging'); + document.body.classList.remove('lm-lora-strength-dragging'); } }); } @@ -243,14 +228,12 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { e.stopPropagation(); isDragging = true; - draggedElement = dragHandle.closest('.comfy-lora-entry'); + draggedElement = dragHandle.closest('.lm-lora-entry'); container = draggedElement.parentElement; // Add dragging class and visual feedback - draggedElement.classList.add('comfy-lora-dragging'); - draggedElement.style.opacity = '0.5'; - draggedElement.style.transform = 'scale(0.98)'; - + draggedElement.classList.add('lm-lora-entry--dragging'); + // Create single drop indicator with absolute positioning dropIndicator = createDropIndicator(); @@ -263,7 +246,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { container._originalPosition = originalPosition; // Add global cursor style - document.body.style.cursor = 'grabbing'; + document.body.classList.add('lm-lora-reordering'); // Store workflow scale for accurate positioning scale = app.canvas.ds.scale; @@ -273,7 +256,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { if (!isDragging || !draggedElement || !dropIndicator) return; const targetIndex = getDropTargetIndex(container, e.clientY); - const entries = container.querySelectorAll('.comfy-lora-entry, .comfy-lora-clip-entry'); + const entries = container.querySelectorAll('.lm-lora-entry, .lm-lora-clip-entry'); if (targetIndex === 0) { // Show at top @@ -307,7 +290,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { document.addEventListener('mouseup', (e) => { // Always reset cursor regardless of isDragging state - document.body.style.cursor = ''; + document.body.classList.remove('lm-lora-reordering'); if (!isDragging || !draggedElement) return; @@ -319,7 +302,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { if (currentIndex !== -1 && currentIndex !== targetIndex) { // Calculate actual target index (excluding clip entries from count) - const loraEntries = container.querySelectorAll('.comfy-lora-entry'); + const loraEntries = container.querySelectorAll('.lm-lora-entry'); let actualTargetIndex = targetIndex; // Adjust target index if it's beyond the number of actual LoRA entries @@ -347,9 +330,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { // Cleanup isDragging = false; if (draggedElement) { - draggedElement.classList.remove('comfy-lora-dragging'); - draggedElement.style.opacity = ''; - draggedElement.style.transform = ''; + draggedElement.classList.remove('lm-lora-entry--dragging'); draggedElement = null; } @@ -366,7 +347,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) { // Also reset cursor when mouse leaves the document document.addEventListener('mouseleave', () => { - document.body.style.cursor = ''; + document.body.classList.remove('lm-lora-reordering'); }); } @@ -461,25 +442,15 @@ export function createContextMenu(x, y, loraName, widget, previewTooltip, render } // Remove existing context menu if any - const existingMenu = document.querySelector('.comfy-lora-context-menu'); + const existingMenu = document.querySelector('.lm-lora-context-menu'); if (existingMenu) { existingMenu.remove(); } const menu = document.createElement('div'); - menu.className = 'comfy-lora-context-menu'; - Object.assign(menu.style, { - position: 'fixed', - left: `${x}px`, - top: `${y}px`, - backgroundColor: 'rgba(30, 30, 30, 0.95)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '4px', - padding: '4px 0', - zIndex: 1000, - boxShadow: '0 2px 10px rgba(0,0,0,0.2)', - minWidth: '180px', - }); + menu.className = 'lm-lora-context-menu'; + menu.style.left = `${x}px`; + menu.style.top = `${y}px`; // View on Civitai option with globe icon const viewOnCivitaiOption = createMenuItem( @@ -713,25 +684,13 @@ export function createContextMenu(x, y, loraName, widget, previewTooltip, render ); // Add separator - const separator1 = document.createElement('div'); - Object.assign(separator1.style, { - margin: '4px 0', - borderTop: '1px solid rgba(255, 255, 255, 0.1)', - }); - + const separator1 = document.createElement('hr'); + // Add second separator - const separator2 = document.createElement('div'); - Object.assign(separator2.style, { - margin: '4px 0', - borderTop: '1px solid rgba(255, 255, 255, 0.1)', - }); + const separator2 = document.createElement('hr'); // Add separator for order options - const orderSeparator = document.createElement('div'); - Object.assign(orderSeparator.style, { - margin: '4px 0', - borderTop: '1px solid rgba(255, 255, 255, 0.1)', - }); + const orderSeparator = document.createElement('hr'); menu.appendChild(viewOnCivitaiOption); menu.appendChild(deleteOption); diff --git a/web/comfyui/loras_widget_utils.js b/web/comfyui/loras_widget_utils.js index 85913c72..91484965 100644 --- a/web/comfyui/loras_widget_utils.js +++ b/web/comfyui/loras_widget_utils.js @@ -219,7 +219,7 @@ export function moveLoraByDirection(loras, loraName, direction) { * @returns {number} - Target index for dropping */ export function getDropTargetIndex(container, clientY) { - const entries = container.querySelectorAll('.comfy-lora-entry'); + const entries = container.querySelectorAll('.lm-lora-entry'); let targetIndex = entries.length; for (let i = 0; i < entries.length; i++) { diff --git a/web/comfyui/preview_tooltip.js b/web/comfyui/preview_tooltip.js index 042bd45f..02c1376a 100644 --- a/web/comfyui/preview_tooltip.js +++ b/web/comfyui/preview_tooltip.js @@ -1,4 +1,5 @@ import { api } from "../../scripts/api.js"; +import { ensureLmStyles } from "./lm_styles_loader.js"; /** * Lightweight preview tooltip that can display images or videos for different model types. @@ -21,18 +22,10 @@ export class PreviewTooltip { ? displayNameFormatter : (name) => name; + ensureLmStyles(); + this.element = document.createElement("div"); - Object.assign(this.element.style, { - position: "fixed", - zIndex: 9999, - background: "rgba(0, 0, 0, 0.85)", - borderRadius: "6px", - boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)", - display: "none", - overflow: "hidden", - maxWidth: "300px", - pointerEvents: "none", - }); + this.element.className = "lm-tooltip"; document.body.appendChild(this.element); this.hideTimeout = null; this.isFromAutocomplete = false; @@ -119,23 +112,13 @@ export class PreviewTooltip { } const mediaContainer = document.createElement("div"); - Object.assign(mediaContainer.style, { - position: "relative", - maxWidth: "300px", - maxHeight: "300px", - }); + mediaContainer.className = "lm-tooltip__media-container"; const isVideo = previewUrl.endsWith(".mp4"); const mediaElement = isVideo ? document.createElement("video") : document.createElement("img"); - - Object.assign(mediaElement.style, { - maxWidth: "300px", - maxHeight: "300px", - objectFit: "contain", - display: "block", - }); + mediaElement.classList.add("lm-tooltip__media"); if (isVideo) { mediaElement.autoplay = true; @@ -146,24 +129,7 @@ export class PreviewTooltip { const nameLabel = document.createElement("div"); nameLabel.textContent = displayName; - Object.assign(nameLabel.style, { - position: "absolute", - bottom: "0", - left: "0", - right: "0", - padding: "8px", - color: "white", - fontSize: "13px", - fontFamily: - "'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif", - background: "linear-gradient(transparent, rgba(0, 0, 0, 0.8))", - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - textAlign: "center", - backdropFilter: "blur(4px)", - WebkitBackdropFilter: "blur(4px)", - }); + nameLabel.className = "lm-tooltip__label"; mediaContainer.appendChild(mediaElement); mediaContainer.appendChild(nameLabel);