Files
DemonGatanjieu-Anomalous_Mo…/web/modules/interface_settings.js
T
DemonGatanjieuandClaude Opus 5.5 a8b75d33a6 Faster model cards, video posters, and a settings page
Card thumbnails lived in ComfyUI's temp folder, which ComfyUI empties on
every start, so each first visit after a restart remade them all. They now
live in <user>/anomalous/cache/card_thumbnails (still capped at 256 MB).
Listing a model type queues the missing ones for one background thread;
the first screen of cards loads before the rest. A video cover gets a
first-frame poster (PyAV) and fetches nothing until it plays; hover-play
is now the default, as the setting already recommended.

The gear opens a settings page instead of a pop-up with a dialog inside:
look and language, model cards and memory with the thumbnail cache (size,
empty), folders, workflow fingerprints, opening mode and window, help.
Display preferences live behind owner.displayPrefs. Also removes a dead
overlay rule that shared the new page's id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:00:47 +08:00

176 lines
6.9 KiB
JavaScript

import { app } from '../../../scripts/app.js';
import { normalizeLocale, resolveLocale, translate } from './locales.js';
export const LANGUAGE_SETTING_ID = 'Anomalous.ModelBrowser.Language';
export const ABYSSAL_SCARLET_SETTING_ID = 'Anomalous.ModelBrowser.AbyssalScarletTheme';
let defaultLang = 'zh';
try {
let comfyDetected = false;
const aglLang = localStorage.getItem('Comfy.Settings.AIGODLIKE-COMFYUI-TRANSLATION.Language');
if (aglLang) {
defaultLang = aglLang.toLowerCase().includes('en') ? 'en' : 'zh';
comfyDetected = true;
} else {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (!key || (!key.toLowerCase().includes('lang') && !key.toLowerCase().includes('locale'))) continue;
const value = localStorage.getItem(key);
if (typeof value !== 'string') continue;
const normalizedValue = value.toLowerCase();
if (normalizedValue.includes('zh') || normalizedValue.includes('chinese')) {
defaultLang = 'zh';
comfyDetected = true;
break;
}
if (normalizedValue.includes('en') || normalizedValue.includes('english')) {
defaultLang = 'en';
comfyDetected = true;
break;
}
}
}
if (!comfyDetected && navigator.language && !navigator.language.toLowerCase().startsWith('zh')) {
defaultLang = 'en';
}
} catch (_) {
if (navigator.language && !navigator.language.toLowerCase().startsWith('zh')) {
defaultLang = 'en';
}
}
let currentLang = resolveLocale(localStorage.getItem('anomalous_lang') || defaultLang);
window.anomalous_browser_lang = currentLang;
export const t = (key, params) => translate(key, params, window.anomalous_browser_lang || currentLang);
export const getCurrentLanguage = () => currentLang;
function normalizeLanguagePreference(value) {
return value === 'zh' || value === 'en' ? value : 'auto';
}
function resolveComfyLanguage() {
try {
const settings = app.extensionManager?.setting;
const locale = settings?.get('Comfy.Locale')
|| app.ui?.settings?.getSettingValue?.('Comfy.Locale')
|| app.ui?.settings?.getSettingValue?.('Comfy.Locale.Language');
return normalizeLocale(locale) || defaultLang;
} catch (_) {
return defaultLang;
}
}
function getSettingTranslationPatches() {
const category = t('mainInterfaceCategory');
return {
[LANGUAGE_SETTING_ID]: {
name: t('mainLanguageSetting'),
category: ['Anomalous Model Browser', category, 'language'],
tooltip: t('mainLanguageTooltip'),
options: [
{ value: 'auto', text: t('mainLanguageAuto') },
{ value: 'zh', text: t('mainLanguageChinese') },
{ value: 'en', text: t('mainLanguageEnglish') }
]
},
[ABYSSAL_SCARLET_SETTING_ID]: {
name: t('mainAbyssalScarletThemeSetting'),
category: ['Anomalous Model Browser', category, 'theme'],
tooltip: t('mainAbyssalScarletThemeTooltip')
}
};
}
function refreshRegisteredSettings() {
const settingsApi = app.extensionManager?.setting;
const registry = settingsApi?.settings?.value || settingsApi?.settings;
if (!registry || typeof registry !== 'object') return;
for (const [id, patch] of Object.entries(getSettingTranslationPatches())) {
if (registry[id]) registry[id] = { ...registry[id], ...patch };
}
}
/** 'auto' | 'zh' | 'en': the ComfyUI setting's change handler, also used without the settings API. */
export function applyLanguagePreference(value) {
const preference = normalizeLanguagePreference(value);
if (preference === 'auto') localStorage.removeItem('anomalous_lang');
else localStorage.setItem('anomalous_lang', preference);
const nextLanguage = preference === 'auto' ? resolveComfyLanguage() : preference;
const changed = nextLanguage !== window.anomalous_browser_lang;
currentLang = nextLanguage;
window.anomalous_browser_lang = nextLanguage;
refreshRegisteredSettings();
if (changed) {
window.dispatchEvent(new CustomEvent('anomalous-language-change', {
detail: { language: nextLanguage, preference }
}));
}
}
if (!localStorage.getItem('anomalous_lang')) {
currentLang = resolveComfyLanguage();
window.anomalous_browser_lang = currentLang;
}
function showThemeNoticeToast(isEnabled) {
document.getElementById('anomalous-theme-toast')?.remove();
const toast = document.createElement('div');
toast.id = 'anomalous-theme-toast';
toast.className = 'anomalous-theme-toast' + (isEnabled ? ' is-abyssal' : '');
toast.textContent = isEnabled ? t('themeDomainActivated') : t('themeDomainDeactivated');
document.body.appendChild(toast);
requestAnimationFrame(() => toast.classList.add('is-show'));
setTimeout(() => {
toast.classList.remove('is-show');
setTimeout(() => toast.remove(), 400);
}, 2400);
}
export function setAbyssalScarletTheme(enabled, notify = false) {
const isEnabled = Boolean(enabled);
localStorage.setItem('anomalous_theme_abyssal_scarlet', isEnabled ? 'true' : 'false');
document.documentElement.classList.toggle('theme-abyssal-scarlet', isEnabled);
document.getElementById('anomalous-modal')?.classList.toggle('theme-abyssal-scarlet', isEnabled);
document.getElementById('anomalous-container')?.classList.toggle('theme-abyssal-scarlet', isEnabled);
try {
const settings = app.extensionManager?.setting;
if (settings && typeof settings.set === 'function' && settings.get(ABYSSAL_SCARLET_SETTING_ID) !== isEnabled) {
settings.set(ABYSSAL_SCARLET_SETTING_ID, isEnabled);
}
} catch (_) {}
if (notify) showThemeNoticeToast(isEnabled);
window.dispatchEvent(new CustomEvent('anomalous-theme-change', {
detail: { theme: isEnabled ? 'abyssal-scarlet' : 'default', enabled: isEnabled }
}));
}
window.setAbyssalScarletTheme = setAbyssalScarletTheme;
setAbyssalScarletTheme(localStorage.getItem('anomalous_theme_abyssal_scarlet') === 'true', false);
export function createInterfaceSettings() {
const translations = getSettingTranslationPatches();
return [
{
id: LANGUAGE_SETTING_ID,
...translations[LANGUAGE_SETTING_ID],
type: 'combo',
defaultValue: () => normalizeLanguagePreference(localStorage.getItem('anomalous_lang')),
onChange: applyLanguagePreference
},
{
id: ABYSSAL_SCARLET_SETTING_ID,
...translations[ABYSSAL_SCARLET_SETTING_ID],
type: 'boolean',
defaultValue: () => localStorage.getItem('anomalous_theme_abyssal_scarlet') === 'true',
onChange(value) {
setAbyssalScarletTheme(Boolean(value), true);
}
}
];
}