mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-21 03:01:27 -03:00
fix(autocomplete): sync active filters via server-side store (#1091)
The LoRA Manager page kept its active filters in localStorage, which the
ComfyUI-side autocomplete read directly. When the two run in different
browsers, origins, or the ComfyUI Desktop Electron shell, localStorage is
not shared and the active-filters search silently did nothing.
The manager page now mirrors its filter state to a server-side in-memory
store (PUT /api/lm/{prefix}/active-filters), pushed on every change via a
storage-listener hook and once on page load. The autocomplete widget sends
only use_active_filters=true, and the relative-paths endpoint injects the
stored filters into the search, with explicit query params taking
precedence.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Mirrors the manager page's active filter state to the backend's in-memory
|
||||
* store, so the ComfyUI-side autocomplete can apply it even when the manager
|
||||
* page and ComfyUI run in different browsers/origins (localStorage is not
|
||||
* shared there).
|
||||
*/
|
||||
|
||||
import { getStorageItem, setActiveFiltersListener } from './storageHelpers.js';
|
||||
import { debounce } from './debounce.js';
|
||||
|
||||
const SYNC_DEBOUNCE_MS = 300;
|
||||
|
||||
const debouncedPushByPage = {};
|
||||
|
||||
function buildActiveFiltersPayload(pageType) {
|
||||
const activeFolder = getStorageItem(`${pageType}_activeFolder`);
|
||||
const recursiveSearch = getStorageItem(`${pageType}_recursiveSearch`, true);
|
||||
const filters = getStorageItem(`${pageType}_filters`);
|
||||
|
||||
return {
|
||||
// null stays null; legacy "null" string is normalized to null
|
||||
activeFolder: activeFolder && activeFolder !== 'null' ? activeFolder : null,
|
||||
recursiveSearch: recursiveSearch !== false,
|
||||
filters: filters && typeof filters === 'object' ? filters : null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function pushActiveFilters(pageType) {
|
||||
try {
|
||||
const response = await fetch(`/api/lm/${pageType}/active-filters`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(buildActiveFiltersPayload(pageType)),
|
||||
});
|
||||
if (!response.ok) {
|
||||
console.warn(`[Lora Manager] Failed to sync active filters for ${pageType}: HTTP ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[Lora Manager] Failed to sync active filters for ${pageType}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
export function syncActiveFilters(pageType) {
|
||||
if (!debouncedPushByPage[pageType]) {
|
||||
debouncedPushByPage[pageType] = debounce(() => {
|
||||
pushActiveFilters(pageType);
|
||||
}, SYNC_DEBOUNCE_MS);
|
||||
}
|
||||
debouncedPushByPage[pageType]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the storage listener and push the current (restored) state once.
|
||||
* The initial push covers server restarts, where the backend store is empty
|
||||
* until the manager page re-publishes its localStorage-restored filters.
|
||||
* @param {string} pageType - 'loras' | 'checkpoints' | 'embeddings'
|
||||
*/
|
||||
export function initActiveFiltersSync(pageType) {
|
||||
setActiveFiltersListener((changedPageType) => syncActiveFilters(changedPageType));
|
||||
pushActiveFilters(pageType);
|
||||
}
|
||||
@@ -6,6 +6,31 @@
|
||||
// Namespace prefix for all localStorage keys
|
||||
const STORAGE_PREFIX = 'lora_manager_';
|
||||
|
||||
// Matches keys that carry the manager page's active filter state
|
||||
// (e.g. 'loras_activeFolder', 'checkpoints_filters').
|
||||
const ACTIVE_FILTER_KEY_PATTERN = /^(loras|checkpoints|embeddings)_(activeFolder|recursiveSearch|filters)$/;
|
||||
|
||||
let activeFiltersListener = null;
|
||||
|
||||
/**
|
||||
* Register a listener invoked with the page type whenever one of the
|
||||
* active-filter storage keys changes. Used to mirror filter state to the
|
||||
* backend so the ComfyUI-side autocomplete can pick it up across
|
||||
* browsers/origins where localStorage is not shared.
|
||||
* @param {function(string): void} listener
|
||||
*/
|
||||
export function setActiveFiltersListener(listener) {
|
||||
activeFiltersListener = listener;
|
||||
}
|
||||
|
||||
function notifyActiveFiltersChanged(key) {
|
||||
if (!activeFiltersListener) return;
|
||||
const match = ACTIVE_FILTER_KEY_PATTERN.exec(key);
|
||||
if (match) {
|
||||
activeFiltersListener(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from localStorage with namespace support and fallback to legacy keys
|
||||
* @param {string} key - The key without prefix
|
||||
@@ -51,13 +76,15 @@ export function getStorageItem(key, defaultValue = null) {
|
||||
*/
|
||||
export function setStorageItem(key, value) {
|
||||
const prefixedKey = STORAGE_PREFIX + key;
|
||||
|
||||
|
||||
// Convert objects and arrays to JSON strings
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
localStorage.setItem(prefixedKey, JSON.stringify(value));
|
||||
} else {
|
||||
localStorage.setItem(prefixedKey, value);
|
||||
}
|
||||
|
||||
notifyActiveFiltersChanged(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +94,8 @@ export function setStorageItem(key, value) {
|
||||
export function removeStorageItem(key) {
|
||||
localStorage.removeItem(STORAGE_PREFIX + key);
|
||||
localStorage.removeItem(key); // Also remove legacy key
|
||||
|
||||
notifyActiveFiltersChanged(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user