Files
DemonGatanjieuandClaude Opus 5.5 17a2c2f03d Full window for the browser; more room on Voice-over
A full-window button beside Dock (and a double-click on the title bar)
makes the window fill the ComfyUI page, remembered; it is neither dragged
nor resized then, and docking turns it off. Voice-over: one row per line
card, a divider between the lines and the generating side (dragged and
remembered, ui_split_bar.js; about half each by default), "Into your own
workflow" folded, who reads and their voices on one row, the how-to line
only until there are lines. On a wide side the modes' scale and numbers
are shown open unless the fold was closed before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:20:17 +08:00

138 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,
maxBtn,
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 (maxBtn) {
maxBtn.setAttribute('aria-label', t('maximizeTitle'));
maxBtn.setAttribute('data-tooltip', t('maximizeTitle'));
}
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);
}
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,
};
}