From dcac1b7c8ae87afd0c6f765592b9be5068f0c6e9 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Wed, 24 Jun 2026 23:42:34 +0200 Subject: [PATCH] refactor: consolidate shared utilities and relocate view-specific settings logic - Moved formatting helpers (formatBytes, formatDuration, getDownloadEtaText, formatDownloadProgressMeta) and getLocalMatchDisplayInfo from resolve_download_methods.js and missing_browser_methods.js into render_format_methods.js for shared reuse. - Relocated settings-related methods (getStoredTokens, applyFrontendLoggingPreference, loadSettingsFromServer) and their log_system imports from download_target_methods.js into options_methods.js, where they are actually consumed by the options UI. - Simplified missing_browser_methods.js to use getLocalMatchDisplayInfo instead of inline local-match code. --- .../actions/resolve_download_methods.js | 54 ------- .../search/download_target_methods.js | 134 ------------------ web/resolver/utils/render_format_methods.js | 70 ++++++++- web/resolver/views/missing_browser_methods.js | 12 +- web/resolver/views/options_methods.js | 134 ++++++++++++++++++ 5 files changed, 205 insertions(+), 199 deletions(-) diff --git a/web/resolver/actions/resolve_download_methods.js b/web/resolver/actions/resolve_download_methods.js index e2bd7ab..db61328 100644 --- a/web/resolver/actions/resolve_download_methods.js +++ b/web/resolver/actions/resolve_download_methods.js @@ -2640,59 +2640,5 @@ export const resolveDownloadMethods = { this.updateQueuePanel?.(); this.showNotification('Download failed: ' + error.message, 'error'); } - }, - - /** - * Format bytes to human readable string - */ - formatBytes(bytes) { - const value = Number(bytes) || 0; - if (!Number.isFinite(value) || value <= 0) return '0 B'; - const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - const i = Math.min(sizes.length - 1, Math.floor(Math.log(value) / Math.log(k))); - return parseFloat((value / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; - }, - - formatDuration(seconds) { - const totalSeconds = Math.max(0, Math.ceil(Number(seconds) || 0)); - if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) return ''; - - if (totalSeconds < 60) return `${totalSeconds}s`; - - const hours = Math.floor(totalSeconds / 3600); - const minutes = Math.floor((totalSeconds % 3600) / 60); - const remainingSeconds = totalSeconds % 60; - - if (hours > 0) { - return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`; - } - - return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`; - }, - - getDownloadEtaText(progress = {}) { - const totalSize = Number(progress.total_size) || 0; - const downloaded = Number(progress.downloaded) || 0; - const speed = Number(progress.speed) || 0; - if (totalSize <= 0 || downloaded <= 0 || speed <= 0 || downloaded >= totalSize) return ''; - - const duration = this.formatDuration((totalSize - downloaded) / speed); - return duration ? `ETA ${duration}` : ''; - }, - - formatDownloadProgressMeta(progress = {}) { - const parts = []; - const speed = Number(progress.speed) || 0; - if (speed > 0) { - parts.push(`${this.formatBytes(speed)}/s`); - } - - const eta = this.getDownloadEtaText(progress); - if (eta) { - parts.push(eta); - } - - return parts.join(' | '); } }; diff --git a/web/resolver/search/download_target_methods.js b/web/resolver/search/download_target_methods.js index 6b79f58..f630354 100644 --- a/web/resolver/search/download_target_methods.js +++ b/web/resolver/search/download_target_methods.js @@ -1,8 +1,6 @@ import { app } from "../../../../../scripts/app.js"; import { api } from "../../../../../scripts/api.js"; import { $el } from "../../../../../scripts/ui.js"; -import { LOG_LEVEL as DEFAULT_FRONTEND_LOG_LEVEL } from "../../log_system/config.js"; -import { logger as frontendLogger } from "../../log_system/logger.js"; import { getSvgIcon } from "../../utils/icon_utils.js"; import { createFloatingTreePicker } from "../utils/tree_picker.js"; export const downloadTargetMethods = { @@ -2407,138 +2405,6 @@ export const downloadTargetMethods = { .catch(() => refreshCategoryTooltip()); }, - getStoredTokens() { - const civitaiCandidateLimitRaw = parseInt(localStorage.getItem('ModelResolver.civitaiCandidateLimit') || '5', 10); - const civitai_candidate_limit = Number.isFinite(civitaiCandidateLimitRaw) - ? Math.min(20, Math.max(1, civitaiCandidateLimitRaw)) - : 5; - const search_source_enabled = this.getSearchSourceEnabledMap(); - const storedFrontendLogsEnabled = localStorage.getItem('ModelResolver.frontendLogsEnabled'); - const storedBackendLogsEnabled = localStorage.getItem('ModelResolver.backendLogsEnabled'); - const storedFrontendLogLevel = localStorage.getItem('ModelResolver.frontendLogLevel'); - const storedBackendLogLevel = localStorage.getItem('ModelResolver.backendLogLevel'); - - return { - civitai_key: localStorage.getItem('ModelResolver.civitaiApiKey') || '', - civitai_session_token: localStorage.getItem('ModelResolver.civitaiSessionToken') || '', - hf_token: localStorage.getItem('ModelResolver.huggingFaceToken') || '', - brave_search_api_key: localStorage.getItem('ModelResolver.braveSearchApiKey') || '', - civitai_use_trpc_search: localStorage.getItem('ModelResolver.civitaiUseTrpcSearch') !== 'false', - civitai_use_html_fallback: localStorage.getItem('ModelResolver.civitaiUseHtmlFallback') !== 'false', - hf_use_api_search: localStorage.getItem('ModelResolver.hfUseApiSearch') !== 'false', - hf_use_comfy_org_fallback: localStorage.getItem('ModelResolver.hfUseComfyOrgFallback') !== 'false', - hf_use_brave_fallback: localStorage.getItem('ModelResolver.hfUseBraveFallback') !== 'false', - auto_fill_base_model: localStorage.getItem('ModelResolver.autoFillBaseModel') !== 'false', - auto_fill_subfolder: localStorage.getItem('ModelResolver.autoFillSubfolder') !== 'false', - auto_refresh_comfy_models_after_apply: localStorage.getItem('ModelResolver.autoRefreshComfyModelsAfterApply') !== 'false', - frontend_logs_enabled: storedFrontendLogsEnabled === null - ? frontendLogger.enabled !== false - : storedFrontendLogsEnabled !== 'false', - backend_logs_enabled: storedBackendLogsEnabled === null - ? true - : storedBackendLogsEnabled !== 'false', - frontend_log_level: storedFrontendLogLevel || DEFAULT_FRONTEND_LOG_LEVEL, - backend_log_level: storedBackendLogLevel || 'DEBUG', - civitai_candidate_limit, - search_source_enabled, - download_path_mode: this.getDownloadPathMode(), - download_path_templates: this.getDownloadPathTemplates(), - base_model_path_mappings: this.getBaseModelPathMappings(), - ...this.getDefaultRootSettings() - }; - }, - - applyFrontendLoggingPreference(enabled = true, levelName = 'DEBUG') { - frontendLogger.setEnabled(Boolean(enabled)); - frontendLogger.setGlobalAndModuleLevel(frontendLogger.normalizeLevel(levelName)); - }, - - /** - * Fetch settings saved on the server and sync them into localStorage. - * Call this once when the dialog initialises so every browser gets the - * same tokens without the user having to re-enter them. - */ - async loadSettingsFromServer() { - try { - const resp = await api.fetchApi('/model_resolver/settings'); - if (!resp.ok) { - const tokens = this.getStoredTokens(); - this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); - return; - } - const data = await resp.json(); - if (!data || typeof data !== 'object') { - const tokens = this.getStoredTokens(); - this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); - return; - } - - // Helper: write to localStorage only when the value from server is - // non-empty, so we don't overwrite a key the user already has locally. - const sync = (localKey, serverValue) => { - if (serverValue !== undefined && serverValue !== null && serverValue !== '') { - localStorage.setItem(localKey, serverValue); - } - }; - - sync('ModelResolver.civitaiApiKey', data.civitai_key); - sync('ModelResolver.civitaiSessionToken', data.civitai_session_token); - sync('ModelResolver.huggingFaceToken', data.hf_token); - sync('ModelResolver.braveSearchApiKey', data.brave_search_api_key); - - if (data.civitai_use_trpc_search !== undefined) - localStorage.setItem('ModelResolver.civitaiUseTrpcSearch', data.civitai_use_trpc_search ? 'true' : 'false'); - if (data.civitai_use_html_fallback !== undefined) - localStorage.setItem('ModelResolver.civitaiUseHtmlFallback', data.civitai_use_html_fallback ? 'true' : 'false'); - if (data.hf_use_api_search !== undefined) - localStorage.setItem('ModelResolver.hfUseApiSearch', data.hf_use_api_search ? 'true' : 'false'); - if (data.hf_use_comfy_org_fallback !== undefined) - localStorage.setItem('ModelResolver.hfUseComfyOrgFallback', data.hf_use_comfy_org_fallback ? 'true' : 'false'); - if (data.hf_use_brave_fallback !== undefined) - localStorage.setItem('ModelResolver.hfUseBraveFallback', data.hf_use_brave_fallback ? 'true' : 'false'); - if (data.auto_fill_base_model !== undefined) - localStorage.setItem('ModelResolver.autoFillBaseModel', data.auto_fill_base_model ? 'true' : 'false'); - if (data.auto_fill_subfolder !== undefined) - localStorage.setItem('ModelResolver.autoFillSubfolder', data.auto_fill_subfolder ? 'true' : 'false'); - if (data.auto_refresh_comfy_models_after_apply !== undefined) - localStorage.setItem('ModelResolver.autoRefreshComfyModelsAfterApply', data.auto_refresh_comfy_models_after_apply ? 'true' : 'false'); - if (data.download_path_mode !== undefined) - localStorage.setItem('ModelResolver.downloadPathMode', this.normalizeDownloadPathMode(data.download_path_mode)); - if (data.download_path_templates !== undefined) - localStorage.setItem('ModelResolver.downloadPathTemplates', JSON.stringify(data.download_path_templates || {})); - if (data.base_model_path_mappings !== undefined) - localStorage.setItem('ModelResolver.baseModelPathMappings', JSON.stringify(data.base_model_path_mappings || {})); - this.getDefaultRootCategoryDefinitions().forEach((item) => { - if (data[item.settingKey] !== undefined) { - localStorage.setItem(item.storageKey, String(data[item.settingKey] || '')); - } - }); - if (data.civitai_candidate_limit !== undefined) - localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${data.civitai_candidate_limit}`); - if (data.frontend_logs_enabled !== undefined) - localStorage.setItem('ModelResolver.frontendLogsEnabled', data.frontend_logs_enabled ? 'true' : 'false'); - if (data.backend_logs_enabled !== undefined) - localStorage.setItem('ModelResolver.backendLogsEnabled', data.backend_logs_enabled ? 'true' : 'false'); - if (data.frontend_log_level !== undefined) - localStorage.setItem('ModelResolver.frontendLogLevel', String(data.frontend_log_level || 'DEBUG').toUpperCase()); - if (data.backend_log_level !== undefined) - localStorage.setItem('ModelResolver.backendLogLevel', String(data.backend_log_level || 'DEBUG').toUpperCase()); - - // Source-enabled flags stored as a nested object - if (data.search_source_enabled && typeof data.search_source_enabled === 'object') { - Object.entries(data.search_source_enabled).forEach(([key, val]) => { - if (key) localStorage.setItem(key, val ? 'true' : 'false'); - }); - } - const tokens = this.getStoredTokens(); - this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); - } catch (err) { - console.warn('Model Resolver: could not load settings from server, using localStorage only.', err); - const tokens = this.getStoredTokens(); - this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); - } - }, - clearFrontendSearchCaches() { for (const state of this.searchResultCache.values()) { state.activeSearchRunId = null; diff --git a/web/resolver/utils/render_format_methods.js b/web/resolver/utils/render_format_methods.js index a19e1fb..7f2ff55 100644 --- a/web/resolver/utils/render_format_methods.js +++ b/web/resolver/utils/render_format_methods.js @@ -1,4 +1,4 @@ -import { app } from "../../../../../scripts/app.js"; +import { app } from "../../../../../scripts/app.js"; import { api } from "../../../../../scripts/api.js"; import { $el } from "../../../../../scripts/ui.js"; import { getSvgIcon } from "../../utils/icon_utils.js"; @@ -242,5 +242,73 @@ export const renderFormatMethods = { await new Promise(resolve => setTimeout(resolve, 250)); } + }, + + /** + * Get display information for the best local match of a missing model + * @param {object} missing - The missing model object + * @returns {object} Match display metadata + */ + getLocalMatchDisplayInfo(missing) { + const bestMatch = this.getBestLocalMatch?.(missing, 70) || null; + const confidence = bestMatch ? Number(bestMatch.confidence || 0) : 0; + const matchName = bestMatch?.model?.relative_path || bestMatch?.filename || bestMatch?.path || ''; + const matchDisplay = matchName || 'No local match'; + const matchClass = confidence === 100 ? 'exact' : (bestMatch ? 'partial' : 'none'); + return { bestMatch, confidence, matchDisplay, matchClass }; + }, + + /** + * Format bytes to human readable string + */ + formatBytes(bytes) { + const value = Number(bytes) || 0; + if (!Number.isFinite(value) || value <= 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.min(sizes.length - 1, Math.floor(Math.log(value) / Math.log(k))); + return parseFloat((value / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; + }, + + formatDuration(seconds) { + const totalSeconds = Math.max(0, Math.ceil(Number(seconds) || 0)); + if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) return ''; + + if (totalSeconds < 60) return `${totalSeconds}s`; + + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const remainingSeconds = totalSeconds % 60; + + if (hours > 0) { + return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`; + } + + return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`; + }, + + getDownloadEtaText(progress = {}) { + const totalSize = Number(progress.total_size) || 0; + const downloaded = Number(progress.downloaded) || 0; + const speed = Number(progress.speed) || 0; + if (totalSize <= 0 || downloaded <= 0 || speed <= 0 || downloaded >= totalSize) return ''; + + const duration = this.formatDuration((totalSize - downloaded) / speed); + return duration ? `ETA ${duration}` : ''; + }, + + formatDownloadProgressMeta(progress = {}) { + const parts = []; + const speed = Number(progress.speed) || 0; + if (speed > 0) { + parts.push(`${this.formatBytes(speed)}/s`); + } + + const eta = this.getDownloadEtaText(progress); + if (eta) { + parts.push(eta); + } + + return parts.join(' | '); } }; diff --git a/web/resolver/views/missing_browser_methods.js b/web/resolver/views/missing_browser_methods.js index 0b7bf63..a223e70 100644 --- a/web/resolver/views/missing_browser_methods.js +++ b/web/resolver/views/missing_browser_methods.js @@ -386,11 +386,7 @@ export const missingBrowserMethods = { const isBatchSelected = this.batchSelectedMissingKeys?.has(key); const isResolved = this.isMissingModelResolved(missing); const filename = this.getMissingFilename(missing); - const bestMatch = this.getBestLocalMatch(missing, 70); - const confidence = bestMatch ? Number(bestMatch.confidence || 0) : 0; - const matchName = bestMatch?.model?.relative_path || bestMatch?.filename || bestMatch?.path || ''; - const matchDisplay = matchName || 'No local match'; - const matchClass = confidence === 100 ? 'exact' : (bestMatch ? 'partial' : 'none'); + const { bestMatch, confidence, matchDisplay, matchClass } = this.getLocalMatchDisplayInfo(missing); const typeLabel = missing.category ? this.getCategoryDisplayName(missing.category) : 'unknown'; const typeColorClass = this.getModelTypeColorClass(missing.category || typeLabel); const nodeDisplay = this.getMissingNodeDisplay(missing); @@ -1609,11 +1605,7 @@ export const missingBrowserMethods = { .find(item => item.dataset.missingKey === missingKey); if (!row) return; - const bestMatch = this.getBestLocalMatch(missing, 70); - const confidence = bestMatch ? Number(bestMatch.confidence || 0) : 0; - const matchName = bestMatch?.model?.relative_path || bestMatch?.filename || bestMatch?.path || ''; - const matchDisplay = matchName || 'No local match'; - const matchClass = confidence === 100 ? 'exact' : (bestMatch ? 'partial' : 'none'); + const { bestMatch, confidence, matchDisplay, matchClass } = this.getLocalMatchDisplayInfo(missing); const bestEl = row.querySelector('.mr-missing-row-best'); if (bestEl) { diff --git a/web/resolver/views/options_methods.js b/web/resolver/views/options_methods.js index 7b84c5b..62b33b7 100644 --- a/web/resolver/views/options_methods.js +++ b/web/resolver/views/options_methods.js @@ -2,6 +2,8 @@ import { app } from "../../../../../scripts/app.js"; import { api } from "../../../../../scripts/api.js"; import { $el } from "../../../../../scripts/ui.js"; import { getSvgIcon } from "../../utils/icon_utils.js"; +import { LOG_LEVEL as DEFAULT_FRONTEND_LOG_LEVEL } from "../../log_system/config.js"; +import { logger as frontendLogger } from "../../log_system/logger.js"; export const optionsMethods = { displayOptions() { if (!this.contentElement) return; @@ -1693,5 +1695,137 @@ export const optionsMethods = { }); } + }, + + getStoredTokens() { + const civitaiCandidateLimitRaw = parseInt(localStorage.getItem('ModelResolver.civitaiCandidateLimit') || '5', 10); + const civitai_candidate_limit = Number.isFinite(civitaiCandidateLimitRaw) + ? Math.min(20, Math.max(1, civitaiCandidateLimitRaw)) + : 5; + const search_source_enabled = this.getSearchSourceEnabledMap(); + const storedFrontendLogsEnabled = localStorage.getItem('ModelResolver.frontendLogsEnabled'); + const storedBackendLogsEnabled = localStorage.getItem('ModelResolver.backendLogsEnabled'); + const storedFrontendLogLevel = localStorage.getItem('ModelResolver.frontendLogLevel'); + const storedBackendLogLevel = localStorage.getItem('ModelResolver.backendLogLevel'); + + return { + civitai_key: localStorage.getItem('ModelResolver.civitaiApiKey') || '', + civitai_session_token: localStorage.getItem('ModelResolver.civitaiSessionToken') || '', + hf_token: localStorage.getItem('ModelResolver.huggingFaceToken') || '', + brave_search_api_key: localStorage.getItem('ModelResolver.braveSearchApiKey') || '', + civitai_use_trpc_search: localStorage.getItem('ModelResolver.civitaiUseTrpcSearch') !== 'false', + civitai_use_html_fallback: localStorage.getItem('ModelResolver.civitaiUseHtmlFallback') !== 'false', + hf_use_api_search: localStorage.getItem('ModelResolver.hfUseApiSearch') !== 'false', + hf_use_comfy_org_fallback: localStorage.getItem('ModelResolver.hfUseComfyOrgFallback') !== 'false', + hf_use_brave_fallback: localStorage.getItem('ModelResolver.hfUseBraveFallback') !== 'false', + auto_fill_base_model: localStorage.getItem('ModelResolver.autoFillBaseModel') !== 'false', + auto_fill_subfolder: localStorage.getItem('ModelResolver.autoFillSubfolder') !== 'false', + auto_refresh_comfy_models_after_apply: localStorage.getItem('ModelResolver.autoRefreshComfyModelsAfterApply') !== 'false', + frontend_logs_enabled: storedFrontendLogsEnabled === null + ? frontendLogger.enabled !== false + : storedFrontendLogsEnabled !== 'false', + backend_logs_enabled: storedBackendLogsEnabled === null + ? true + : storedBackendLogsEnabled !== 'false', + frontend_log_level: storedFrontendLogLevel || DEFAULT_FRONTEND_LOG_LEVEL, + backend_log_level: storedBackendLogLevel || 'DEBUG', + civitai_candidate_limit, + search_source_enabled, + download_path_mode: this.getDownloadPathMode(), + download_path_templates: this.getDownloadPathTemplates(), + base_model_path_mappings: this.getBaseModelPathMappings(), + ...this.getDefaultRootSettings() + }; + }, + + applyFrontendLoggingPreference(enabled = true, levelName = 'DEBUG') { + frontendLogger.setEnabled(Boolean(enabled)); + frontendLogger.setGlobalAndModuleLevel(frontendLogger.normalizeLevel(levelName)); + }, + + /** + * Fetch settings saved on the server and sync them into localStorage. + * Call this once when the dialog initialises so every browser gets the + * same tokens without the user having to re-enter them. + */ + async loadSettingsFromServer() { + try { + const resp = await api.fetchApi('/model_resolver/settings'); + if (!resp.ok) { + const tokens = this.getStoredTokens(); + this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); + return; + } + const data = await resp.json(); + if (!data || typeof data !== 'object') { + const tokens = this.getStoredTokens(); + this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); + return; + } + + // Helper: write to localStorage only when the value from server is + // non-empty, so we don't overwrite a key the user already has locally. + const sync = (localKey, serverValue) => { + if (serverValue !== undefined && serverValue !== null && serverValue !== '') { + localStorage.setItem(localKey, serverValue); + } + }; + + sync('ModelResolver.civitaiApiKey', data.civitai_key); + sync('ModelResolver.civitaiSessionToken', data.civitai_session_token); + sync('ModelResolver.huggingFaceToken', data.hf_token); + sync('ModelResolver.braveSearchApiKey', data.brave_search_api_key); + + if (data.civitai_use_trpc_search !== undefined) + localStorage.setItem('ModelResolver.civitaiUseTrpcSearch', data.civitai_use_trpc_search ? 'true' : 'false'); + if (data.civitai_use_html_fallback !== undefined) + localStorage.setItem('ModelResolver.civitaiUseHtmlFallback', data.civitai_use_html_fallback ? 'true' : 'false'); + if (data.hf_use_api_search !== undefined) + localStorage.setItem('ModelResolver.hfUseApiSearch', data.hf_use_api_search ? 'true' : 'false'); + if (data.hf_use_comfy_org_fallback !== undefined) + localStorage.setItem('ModelResolver.hfUseComfyOrgFallback', data.hf_use_comfy_org_fallback ? 'true' : 'false'); + if (data.hf_use_brave_fallback !== undefined) + localStorage.setItem('ModelResolver.hfUseBraveFallback', data.hf_use_brave_fallback ? 'true' : 'false'); + if (data.auto_fill_base_model !== undefined) + localStorage.setItem('ModelResolver.autoFillBaseModel', data.auto_fill_base_model ? 'true' : 'false'); + if (data.auto_fill_subfolder !== undefined) + localStorage.setItem('ModelResolver.autoFillSubfolder', data.auto_fill_subfolder ? 'true' : 'false'); + if (data.auto_refresh_comfy_models_after_apply !== undefined) + localStorage.setItem('ModelResolver.autoRefreshComfyModelsAfterApply', data.auto_refresh_comfy_models_after_apply ? 'true' : 'false'); + if (data.download_path_mode !== undefined) + localStorage.setItem('ModelResolver.downloadPathMode', this.normalizeDownloadPathMode(data.download_path_mode)); + if (data.download_path_templates !== undefined) + localStorage.setItem('ModelResolver.downloadPathTemplates', JSON.stringify(data.download_path_templates || {})); + if (data.base_model_path_mappings !== undefined) + localStorage.setItem('ModelResolver.baseModelPathMappings', JSON.stringify(data.base_model_path_mappings || {})); + this.getDefaultRootCategoryDefinitions().forEach((item) => { + if (data[item.settingKey] !== undefined) { + localStorage.setItem(item.storageKey, String(data[item.settingKey] || '')); + } + }); + if (data.civitai_candidate_limit !== undefined) + localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${data.civitai_candidate_limit}`); + if (data.frontend_logs_enabled !== undefined) + localStorage.setItem('ModelResolver.frontendLogsEnabled', data.frontend_logs_enabled ? 'true' : 'false'); + if (data.backend_logs_enabled !== undefined) + localStorage.setItem('ModelResolver.backendLogsEnabled', data.backend_logs_enabled ? 'true' : 'false'); + if (data.frontend_log_level !== undefined) + localStorage.setItem('ModelResolver.frontendLogLevel', String(data.frontend_log_level || 'DEBUG').toUpperCase()); + if (data.backend_log_level !== undefined) + localStorage.setItem('ModelResolver.backendLogLevel', String(data.backend_log_level || 'DEBUG').toUpperCase()); + + // Source-enabled flags stored as a nested object + if (data.search_source_enabled && typeof data.search_source_enabled === 'object') { + Object.entries(data.search_source_enabled).forEach(([key, val]) => { + if (key) localStorage.setItem(key, val ? 'true' : 'false'); + }); + } + const tokens = this.getStoredTokens(); + this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); + } catch (err) { + console.warn('Model Resolver: could not load settings from server, using localStorage only.', err); + const tokens = this.getStoredTokens(); + this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level); + } } };