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>
135 lines
6.0 KiB
JavaScript
135 lines
6.0 KiB
JavaScript
/**
|
|
* The browser's display preferences (view mode, interface scale, background atmosphere,
|
|
* window layout) and how they apply to the window; the rail's gear button, which opens
|
|
* the settings page (ui_settings_page.js); and redrawing the browser when the language
|
|
* changes. The settings page changes preferences only through `owner.displayPrefs`.
|
|
*/
|
|
|
|
import { app } from "../../../scripts/app.js";
|
|
import { translate } from './locales.js';
|
|
import { openSettingsPage, isSettingsPageOpen, leaveSettingsPage } from './ui_settings_page.js';
|
|
|
|
const t = (key, params) => translate(key, params);
|
|
const VIEW_MODES = ['compact', 'standard', 'aesthetic'];
|
|
|
|
export function createSettingsHub(owner, {
|
|
container,
|
|
savedScale,
|
|
savedBgOpacity,
|
|
updateLangClass,
|
|
dockBtn,
|
|
updateNoticeBtn,
|
|
icons,
|
|
}) {
|
|
const prefs = {
|
|
viewMode: VIEW_MODES.includes(localStorage.getItem('anomalous_view_mode')) ? localStorage.getItem('anomalous_view_mode') : 'compact',
|
|
scale: parseFloat(savedScale) || 1,
|
|
bgOpacity: Number.isFinite(parseFloat(savedBgOpacity)) ? parseFloat(savedBgOpacity) : 0.2,
|
|
};
|
|
|
|
const applyViewMode = (mode) => {
|
|
prefs.viewMode = VIEW_MODES.includes(mode) ? mode : 'compact';
|
|
container.classList.remove(...VIEW_MODES.map(name => `view-mode-${name}`));
|
|
container.classList.add(`view-mode-${prefs.viewMode}`);
|
|
};
|
|
applyViewMode(prefs.viewMode);
|
|
|
|
/** What the settings page reads and changes; each change is kept for the next session. */
|
|
owner.displayPrefs = {
|
|
get: () => ({ ...prefs }),
|
|
setViewMode(mode) {
|
|
applyViewMode(mode);
|
|
localStorage.setItem('anomalous_view_mode', prefs.viewMode);
|
|
},
|
|
setScale(value) {
|
|
prefs.scale = Math.max(0.5, Math.min(1.5, Math.round(value * 10) / 10));
|
|
container.style.setProperty('--anomalous-scale', prefs.scale);
|
|
localStorage.setItem('anomalous_ui_scale', prefs.scale);
|
|
},
|
|
setBgOpacity(value) {
|
|
prefs.bgOpacity = Math.max(0, Math.min(1, Math.round(value * 100) / 100));
|
|
container.style.setProperty('--anomalous-bg-opacity', prefs.bgOpacity);
|
|
localStorage.setItem('anomalous_bg_opacity', prefs.bgOpacity);
|
|
},
|
|
/** Window position, size, docking, scale, atmosphere and view mode back to their defaults. */
|
|
resetLayout() {
|
|
for (const key of ['anomalous_pos_x', 'anomalous_pos_y', 'anomalous_width', 'anomalous_height', 'anomalous_docked',
|
|
'anomalous_ui_scale', 'anomalous_bg_opacity', 'anomalous_view_mode']) {
|
|
localStorage.removeItem(key);
|
|
}
|
|
applyViewMode('compact');
|
|
Object.assign(container.style, { left: '5%', top: '5%', width: '90%', height: '90%' });
|
|
prefs.scale = 1;
|
|
prefs.bgOpacity = 0.2;
|
|
container.style.setProperty('--anomalous-scale', '1');
|
|
container.style.setProperty('--anomalous-bg-opacity', '0.2');
|
|
container.classList.remove('anomalous-docked');
|
|
},
|
|
};
|
|
|
|
const settingsBtn = document.createElement('button');
|
|
settingsBtn.id = 'anomalous-global-settings-btn';
|
|
settingsBtn.className = 'anomalous-tooltip-target';
|
|
settingsBtn.innerHTML = icons.SETTINGS;
|
|
settingsBtn.setAttribute('aria-label', t('sidebarSettings'));
|
|
settingsBtn.setAttribute('data-tooltip', t('sidebarSettings'));
|
|
settingsBtn.setAttribute('data-tooltip-pos', 'right');
|
|
// The gear opens the settings page; on it, the gear goes back to where you were.
|
|
settingsBtn.onclick = (event) => {
|
|
event.stopPropagation();
|
|
if (isSettingsPageOpen(owner)) leaveSettingsPage(owner);
|
|
else openSettingsPage(owner);
|
|
};
|
|
|
|
const refreshLanguageUi = () => {
|
|
updateLangClass();
|
|
owner.renderRailTools?.();
|
|
settingsBtn.setAttribute('aria-label', t('sidebarSettings'));
|
|
settingsBtn.setAttribute('data-tooltip', t('sidebarSettings'));
|
|
owner.refreshShellLanguage?.();
|
|
if (dockBtn) {
|
|
dockBtn.removeAttribute('title');
|
|
dockBtn.setAttribute('aria-label', t('dockTitle'));
|
|
dockBtn.setAttribute('data-tooltip', t('dockTitle'));
|
|
dockBtn.setAttribute('data-tooltip-pos', 'bottom');
|
|
}
|
|
if (updateNoticeBtn) {
|
|
updateNoticeBtn.setAttribute('data-tooltip', t('updateGuideNoticeTooltip'));
|
|
updateNoticeBtn.setAttribute('aria-label', t('updateGuideNoticeTooltip'));
|
|
}
|
|
|
|
// Pages drawn in the old language are drawn again.
|
|
if (owner.doctorPanel && owner.doctorPanel.style.display !== 'none') owner.openDoctorPage();
|
|
if (owner.assistantPanel && owner.assistantPanelInitialized) {
|
|
const selectedNode = Object.values(app.canvas?.selected_nodes || {})[0] || null;
|
|
owner.assistantPanelInitialized = false;
|
|
owner.initAssistantPanel();
|
|
owner.diagnoseNode(selectedNode);
|
|
}
|
|
if (owner.notebookNotesTab) owner.notebookNotesTab.textContent = t('promptNotes');
|
|
if (owner.notebookRecipesTab) owner.notebookRecipesTab.textContent = t('recipeTitle');
|
|
document.querySelectorAll('[data-anomalous-i18n-key]').forEach((element) => {
|
|
const key = element.dataset.anomalousI18nKey;
|
|
if (key) element.textContent = t(key);
|
|
});
|
|
document.getElementById('anomalous-import-overlay')?.remove();
|
|
if (isSettingsPageOpen(owner)) openSettingsPage(owner, { keepReturn: true });
|
|
|
|
owner.renderSidebar();
|
|
owner.loadModels();
|
|
if (owner.detailPanel.style.display !== 'none' && owner.currentDetailModel) {
|
|
owner.showDetail(owner.currentDetailModel);
|
|
}
|
|
if (owner.nbEditor && owner.nbEditor.innerHTML !== '') {
|
|
owner.renderNotebookEditor();
|
|
owner.refreshNotebooks();
|
|
}
|
|
};
|
|
window.addEventListener('anomalous-language-change', refreshLanguageUi);
|
|
|
|
return {
|
|
button: settingsBtn,
|
|
refreshLanguage: refreshLanguageUi,
|
|
};
|
|
}
|