/* ===== Settings Page ===== */ window.NAISettings = { STORAGE_KEY: 'nai_settings', // Default values defaults: { // General theme: 'system', // 'light' | 'dark' | 'system' freeOnly: false, // Generate defaults genPrompt: '', genQuality: 'best quality, amazing quality, very aesthetic, absurdres', genNegative: 'lowres, {bad}, error, fewer, extra, missing, worst quality, jpeg artifacts, bad quality, watermark, unfinished, displeasing, chromatic aberration, signature, extra digits, artistic error, username, scan, [abstract]', genSampler: 'k_euler', genCfg: 5, genSteps: 28, genWidth: 1024, genHeight: 1024, genScheduler: 'karras', genSmea: false, genSmeaDyn: false, genVariety: false, genLegacy: false, genDecrisp: false, genCfgRescale: 0, // History clearHistoryOnRefresh: true, // Cloud saveToCloud: true, // Tag autocomplete tacEnabled: true, tacShowZh: true, tacMatchZh: true, tacMaxResults: 20, tacAutoCorrect: true, tacAddComma: true, tacChineseComma: false, tacUnderscoreToSpace: true, }, current: {}, load() { try { const stored = localStorage.getItem(this.STORAGE_KEY); const parsed = stored ? JSON.parse(stored) : {}; this.current = { ...this.defaults, ...parsed }; } catch (e) { this.current = { ...this.defaults }; } }, save() { try { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.current)); } catch (e) { console.error('Failed to save settings:', e); } }, get(key) { return this.current[key] ?? this.defaults[key]; }, set(key, value) { this.current[key] = value; this.save(); }, // ── Apply settings to UI ── applyAll() { this.applyTheme(); this.applyGenerateDefaults(); this.applyCloudCheckbox(); this.applyHistoryClear(); }, applyTheme() { const theme = this.get('theme'); let effective; if (theme === 'system') { effective = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } else { effective = theme; } document.documentElement.setAttribute('data-theme', effective); NAIState.theme = effective; }, applyGenerateDefaults() { const s = this.current; const setVal = (id, val) => { const el = document.getElementById(id); if (!el) return; el.value = val; el.dispatchEvent(new Event('input', { bubbles: true })); }; const setCheck = (id, val) => { const el = document.getElementById(id); if (el) el.checked = !!val; }; // Only apply defaults if prompt is empty (first load) const promptEl = $('#prompt-input'); if (promptEl && promptEl.value === '' && s.genPrompt) { promptEl.value = s.genPrompt; NAIUtils.autoResize(promptEl); } const qualEl = $('#quality-input'); if (qualEl && s.genQuality !== undefined) { // Always apply quality default on load if (qualEl.value === this.defaults.genQuality || qualEl.value === '') { qualEl.value = s.genQuality; NAIUtils.autoResize(qualEl); } } const negEl = $('#negative-input'); if (negEl && s.genNegative !== undefined) { if (negEl.value === this.defaults.genNegative || negEl.value === '') { negEl.value = s.genNegative; NAIUtils.autoResize(negEl); } } // Sampler const samplerEl = $('#sampler-select'); if (samplerEl && s.genSampler) { const opts = [...samplerEl.options].map(o => o.value); if (opts.includes(s.genSampler)) samplerEl.value = s.genSampler; } setVal('cfg-slider', s.genCfg); setVal('steps-slider', s.genSteps); setVal('width-slider', s.genWidth); setVal('height-slider', s.genHeight); // Advanced const schedEl = $('#adv-scheduler'); if (schedEl && s.genScheduler) { const opts = [...schedEl.options].map(o => o.value); if (opts.includes(s.genScheduler)) schedEl.value = s.genScheduler; } setCheck('adv-smea', s.genSmea); setCheck('adv-smea-dyn', s.genSmeaDyn); setCheck('adv-variety', s.genVariety); setCheck('adv-legacy', s.genLegacy); setCheck('adv-decrisp', s.genDecrisp); setVal('adv-cfg-rescale', s.genCfgRescale); }, applyCloudCheckbox() { const el = $('#save-to-cloud'); if (el) el.checked = this.get('saveToCloud'); }, applyHistoryClear() { if (this.get('clearHistoryOnRefresh')) { NAIIDB.clearAllHistory().catch(() => {}); } }, // ── Capture current generate params as defaults ── captureCurrentAsDefaults() { const s = this.current; s.genPrompt = $('#prompt-input')?.value || ''; s.genQuality = $('#quality-input')?.value || ''; s.genNegative = $('#negative-input')?.value || ''; s.genSampler = $('#sampler-select')?.value || 'k_euler'; s.genCfg = parseFloat($('#cfg-slider')?.value) || 5; s.genSteps = parseInt($('#steps-slider')?.value) || 28; s.genWidth = parseInt($('#width-slider')?.value) || 1024; s.genHeight = parseInt($('#height-slider')?.value) || 1024; s.genScheduler = $('#adv-scheduler')?.value || 'karras'; s.genSmea = $('#adv-smea')?.checked || false; s.genSmeaDyn = $('#adv-smea-dyn')?.checked || false; s.genVariety = $('#adv-variety')?.checked || false; s.genLegacy = $('#adv-legacy')?.checked || false; s.genDecrisp = $('#adv-decrisp')?.checked || false; s.genCfgRescale = parseFloat($('#adv-cfg-rescale')?.value) || 0; this.save(); }, // ── Render settings page ── renderPage() { const s = this.current; // Theme $('#setting-theme').value = s.theme; $('#setting-free-only').checked = s.freeOnly; // Generate $('#setting-gen-prompt').value = s.genPrompt; $('#setting-gen-quality').value = s.genQuality; $('#setting-gen-negative').value = s.genNegative; $('#setting-gen-sampler').value = s.genSampler; $('#setting-gen-cfg').value = s.genCfg; $('#setting-gen-cfg-val').textContent = parseFloat(s.genCfg).toFixed(1); $('#setting-gen-steps').value = s.genSteps; $('#setting-gen-steps-val').textContent = s.genSteps; $('#setting-gen-width').value = s.genWidth; $('#setting-gen-width-val').textContent = s.genWidth; $('#setting-gen-height').value = s.genHeight; $('#setting-gen-height-val').textContent = s.genHeight; $('#setting-gen-scheduler').value = s.genScheduler; $('#setting-gen-smea').checked = s.genSmea; $('#setting-gen-smea-dyn').checked = s.genSmeaDyn; $('#setting-gen-variety').checked = s.genVariety; $('#setting-gen-legacy').checked = s.genLegacy; $('#setting-gen-decrisp').checked = s.genDecrisp; $('#setting-gen-cfg-rescale').value = s.genCfgRescale; $('#setting-gen-cfg-rescale-val').textContent = parseFloat(s.genCfgRescale).toFixed(2); // History $('#setting-clear-history').checked = s.clearHistoryOnRefresh; // Cloud $('#setting-save-cloud').checked = s.saveToCloud; // TAC $('#setting-tac-enabled').checked = s.tacEnabled; $('#setting-tac-show-zh').checked = s.tacShowZh; $('#setting-tac-match-zh').checked = s.tacMatchZh; $('#setting-tac-max').value = s.tacMaxResults; $('#setting-tac-autocorrect').checked = s.tacAutoCorrect; $('#setting-tac-add-comma').checked = s.tacAddComma; $('#setting-tac-zh-comma').checked = s.tacChineseComma; $('#setting-tac-underscore').checked = s.tacUnderscoreToSpace; // Auto-resize textareas $$('#page-settings .form-textarea').forEach(t => NAIUtils.autoResize(t)); }, // ── Bind all settings inputs ── _bindInputs() { const bind = (id, key, type = 'value') => { const el = $(`#${id}`); if (!el) return; const event = el.type === 'checkbox' ? 'change' : 'input'; el.addEventListener(event, () => { let val; if (type === 'check') val = el.checked; else if (type === 'float') val = parseFloat(el.value); else if (type === 'int') val = parseInt(el.value); else val = el.value; this.set(key, val); this._onSettingChanged(key, val); }); }; // General bind('setting-theme', 'theme'); bind('setting-free-only', 'freeOnly', 'check'); // Generate bind('setting-gen-prompt', 'genPrompt'); bind('setting-gen-quality', 'genQuality'); bind('setting-gen-negative', 'genNegative'); bind('setting-gen-sampler', 'genSampler'); bind('setting-gen-cfg', 'genCfg', 'float'); bind('setting-gen-steps', 'genSteps', 'int'); bind('setting-gen-width', 'genWidth', 'int'); bind('setting-gen-height', 'genHeight', 'int'); bind('setting-gen-scheduler', 'genScheduler'); bind('setting-gen-smea', 'genSmea', 'check'); bind('setting-gen-smea-dyn', 'genSmeaDyn', 'check'); bind('setting-gen-variety', 'genVariety', 'check'); bind('setting-gen-legacy', 'genLegacy', 'check'); bind('setting-gen-decrisp', 'genDecrisp', 'check'); bind('setting-gen-cfg-rescale', 'genCfgRescale', 'float'); // History bind('setting-clear-history', 'clearHistoryOnRefresh', 'check'); // Cloud bind('setting-save-cloud', 'saveToCloud', 'check'); // TAC bind('setting-tac-enabled', 'tacEnabled', 'check'); bind('setting-tac-show-zh', 'tacShowZh', 'check'); bind('setting-tac-match-zh', 'tacMatchZh', 'check'); bind('setting-tac-max', 'tacMaxResults', 'int'); bind('setting-tac-autocorrect', 'tacAutoCorrect', 'check'); bind('setting-tac-add-comma', 'tacAddComma', 'check'); bind('setting-tac-zh-comma', 'tacChineseComma', 'check'); bind('setting-tac-underscore', 'tacUnderscoreToSpace', 'check'); // Slider value displays NAIUtils.bindSlider('setting-gen-cfg', 'setting-gen-cfg-val', v => v.toFixed(1)); NAIUtils.bindSlider('setting-gen-steps', 'setting-gen-steps-val'); NAIUtils.bindSlider('setting-gen-width', 'setting-gen-width-val'); NAIUtils.bindSlider('setting-gen-height', 'setting-gen-height-val'); NAIUtils.bindSlider('setting-gen-cfg-rescale', 'setting-gen-cfg-rescale-val', v => v.toFixed(2)); // Capture button $('#btn-setting-capture').addEventListener('click', () => { this.captureCurrentAsDefaults(); this.renderPage(); alert('已将当前参数设为默认值'); }); $('#btn-setting-reset').addEventListener('click', () => { if (!confirm('确定要重置所有设置吗?')) return; localStorage.removeItem(this.STORAGE_KEY); this.load(); this.applyAll(); this.renderPage(); }); $('#btn-tac-cache-refresh').addEventListener('click', () => { caches.delete('nai-tac-v2'); NAITagComplete.loaded = false; NAITagComplete.loadTags(); alert('标签自动补全缓存已刷新'); }); }, _onSettingChanged(key, value) { if (key === 'theme') this.applyTheme(); if (key === 'saveToCloud') this.applyCloudCheckbox(); if (key === 'freeOnly') this._updateFreeOnlyState(); }, _updateFreeOnlyState() { // This is checked by cost.js when updating buttons // Force a cost recalc if (window.NAICost) NAICost.updateCostDisplay(); }, init() { this.load(); this._bindInputs(); // Render when switching to settings page document.querySelector('.nav-item[data-page="settings"]')?.addEventListener('click', () => { this.renderPage(); }); // System theme listener window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { if (this.get('theme') === 'system') this.applyTheme(); }); // Apply settings after a short delay (let other modules init first) setTimeout(() => this.applyAll(), 100); }, };