Files
nj/docs/opendesign/js/theme-switcher.js
iven b320641d9c
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
fix(app): 全链路验证修复 — 编译错误/CORS/迁移/启动脚本
前端修复:
- calendar_page: 移除不存在的 JournalEntry.content getter
- responsive_scaffold: 移除不存在的 notchThickness 参数
- splash_page: SingleTickerProvider → TickerProvider (多 AnimationController)
- profile_page: UserRoleType.name → .code (修复运行时崩溃)
- 导入缺失的 user.dart

后端修复:
- class_service: generate_class_code 取 UUID 后6位(随机部分)避免碰撞
- diary_role_seed: 移除不存在的 id 列,使用复合主键 ON CONFLICT

基础设施:
- config/default.toml: CORS 改为通配符(开发模式)
- scripts/dev.sh: 统一启动脚本(自动清理端口)
- docs/opendesign/: Open Design 设计规格 HTML 原型稿

验证结果: flutter analyze 0 error, cargo test 77/77 通过, 17个页面全部渲染正常
2026-06-02 01:03:58 +08:00

104 lines
3.8 KiB
JavaScript

/* ─────────────────────────────────────────────────────────
* 暖记 Theme Switcher
* Handles theme toggling + localStorage persistence
* ───────────────────────────────────────────────────────── */
(function () {
var THEMES = [
{ id: 'warm', name: '暖阳', swatch: '#E07A5F' },
{ id: 'pine', name: '松风', swatch: '#4A7B9D' }
];
var STORAGE_KEY = 'warmnotes-theme';
function getCurrent() {
return localStorage.getItem(STORAGE_KEY) || 'warm';
}
function apply(id) {
if (id === 'warm') {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', id);
}
localStorage.setItem(STORAGE_KEY, id);
}
function cycle() {
var cur = getCurrent();
var idx = 0;
for (var i = 0; i < THEMES.length; i++) {
if (THEMES[i].id === cur) { idx = i; break; }
}
var next = THEMES[(idx + 1) % THEMES.length];
apply(next.id);
updateBtn(next);
}
function updateBtn(theme) {
var btn = document.getElementById('theme-toggle-btn');
if (!btn) return;
var dot = btn.querySelector('.theme-dot');
var label = btn.querySelector('.theme-label');
if (dot) dot.style.background = theme.swatch;
if (label) label.textContent = theme.name;
btn.setAttribute('aria-label', 'Switch theme (current: ' + theme.name + ')');
}
// Apply stored theme immediately (prevent flash)
var saved = getCurrent();
apply(saved);
// Wait for DOM then inject toggle button
function init() {
if (document.getElementById('theme-toggle-btn')) return;
var cur = THEMES.filter(function (t) { return t.id === saved; })[0] || THEMES[0];
var wrap = document.createElement('div');
wrap.id = 'theme-toggle-btn';
wrap.setAttribute('role', 'button');
wrap.setAttribute('tabindex', '0');
wrap.setAttribute('aria-label', 'Switch theme (current: ' + cur.name + ')');
wrap.onclick = function () { saved = getCurrent(); cycle(); };
wrap.onkeydown = function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); cycle(); } };
var style = document.createElement('style');
style.textContent = '\
#theme-toggle-btn{position:fixed;top:8px;right:8px;z-index:10000;display:flex;align-items:center;gap:6px;\
padding:5px 12px 5px 8px;border-radius:20px;border:1px solid var(--border);\
background:var(--surface);color:var(--fg);font-size:11px;font-family:var(--font-body);\
cursor:pointer;box-shadow:var(--elev-medium);transition:all .2s ease;user-select:none;line-height:1;}\
#theme-toggle-btn:hover{box-shadow:var(--elev-float);}\
#theme-toggle-btn:focus-visible{outline:none;box-shadow:var(--focus-ring);}\
.theme-dot{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:1.5px solid var(--border);}\
.theme-label{white-space:nowrap;font-weight:600;}';
document.head.appendChild(style);
var dot = document.createElement('span');
dot.className = 'theme-dot';
dot.style.background = cur.swatch;
var label = document.createElement('span');
label.className = 'theme-label';
label.textContent = cur.name;
wrap.appendChild(dot);
wrap.appendChild(label);
document.body.appendChild(wrap);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Cross-tab sync
window.addEventListener('storage', function (e) {
if (e.key === STORAGE_KEY && e.newValue) {
apply(e.newValue);
var t = THEMES.filter(function (th) { return th.id === e.newValue; })[0] || THEMES[0];
updateBtn(t);
}
});
})();