Update Site source
This commit is contained in:
1 parent
223ad634d7
commit
ca116d6cce
8 files changed
+94
-34
No files matched your search
+1
-1
@@ -3,7 +3,7 @@
|
||||
const DRAFT_KEY = 'lifeos_today_draft_v1';
|
||||
const localDay = () => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; };
|
||||
const escapeHtml = value => String(value || '').replace(/[&<>]/g, char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]));
|
||||
const APP_VERSION = '1.5.3';
|
||||
const APP_VERSION = '1.5.4';
|
||||
window.LifeOSVersion = APP_VERSION;
|
||||
const appIds = ['today', 'timeline', 'museum', 'insights', 'profile', 'tasks', 'calendar', 'system'];
|
||||
const t = (key, values) => LifeI18n.t(key, values);
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
window.closeModal = () => { rootModal().innerHTML = ''; };
|
||||
window.applyLanguage = () => { document.documentElement.lang = lang() === 'zh' ? 'zh-CN' : 'en'; document.title = t('shell.pageTitle'); const brand = document.querySelector('#desktop-brand'), launcher = document.querySelector('#life-launcher'), boot = document.querySelector('#boot span'); if (brand) brand.textContent = t('shell.brand'); if (launcher) launcher.textContent = t('shell.life'); if (boot) boot.textContent = t('shell.initializing'); if (settings().wallpaper) applyWallpaper(settings().wallpaper); };
|
||||
window.applyWallpaper = wallpaper => { if (!wallpaper?.url) return; const desktop = document.querySelector('#desktop'); desktop.style.backgroundImage = `linear-gradient(120deg,color-mix(in srgb,var(--bg) 68%,transparent),color-mix(in srgb,var(--bg) 36%,transparent)),url('${wallpaper.url}')`; desktop.style.backgroundSize = 'cover'; desktop.style.backgroundPosition = 'center'; desktop.title = t('shell.wallpaperTitle', { title: wallpaper.title || t('wallpaper.dailyImage') }); };
|
||||
window.setLanguage = value => {
|
||||
const state = settings(); if (!['auto','en','zh'].includes(value)) return; state.language = value; state.languageMode = value === 'auto' ? 'auto' : 'manual'; save('lifeos_settings', state);
|
||||
window.renderIcons(); window.renderAll(); window.applyLanguage();
|
||||
window.toast(value === 'auto' ? t(lang() === 'zh' ? 'feedback.followingZh' : 'feedback.followingEn') : t(value === 'zh' ? 'feedback.languageZh' : 'feedback.languageEn'));
|
||||
};
|
||||
window.today = container => {
|
||||
const entryDate = todayTargetDate || day(), memory = mems().find(item => item.date === entryDate);
|
||||
if (todayDraft === null) { selectedMood = memory?.mood || 'cloudy'; selectedEnergy = memory?.energy || 3; }
|
||||
@@ -77,8 +72,8 @@
|
||||
};
|
||||
window.saveProfile = () => { save('lifeos_profile', { name: pname.value, birthday: pbday.value, startDate: pstart.value || day(), motto: pmotto.value }); renderAll(); toast(t('profile.saved')); };
|
||||
window.systemApp = container => {
|
||||
const state = settings(), bytes = new Blob([JSON.stringify({ mems: mems(), museum: museum(), profile: profile(), system: sys() })]).size, oldBackup = state.lastBackup && Date.now() - new Date(state.lastBackup) > 12096e5, detected = browserLanguage() === 'zh' ? t('system.detectedZh') : t('system.detectedEn');
|
||||
container.innerHTML = `<div class="eyebrow">${t('system.eyebrow')}</div><h1>${t('system.title')}</h1><div class="settings"><section><div class="eyebrow">${t('system.appearance')}</div><div class="choices">${['dark','light','auto'].map(item => `<button class="chip ${state.theme === item ? 'selected' : ''}" onclick="theme('${item}')">${t(`system.theme.${item}`)}</button>`).join('')}</div></section><section><div class="eyebrow">${t('system.language')}</div><p class="sub">${detected}</p><div class="choices"><button class="chip ${state.languageMode !== 'manual' ? 'selected' : ''}" onclick="window.setLanguage('auto')">${t('system.auto')}</button><button class="chip ${state.languageMode === 'manual' && state.language === 'en' ? 'selected' : ''}" onclick="window.setLanguage('en')">${t('system.languageEnglish')}</button><button class="chip ${state.languageMode === 'manual' && state.language === 'zh' ? 'selected' : ''}" onclick="window.setLanguage('zh')">${t('system.languageChinese')}</button></div></section><section><div class="eyebrow">${t('system.data')}</div><div class="actions"><button class="primary" onclick="exportData()">${t('system.export')}</button><button class="secondary" onclick="document.querySelector('#importer').click()">${t('system.import')}</button><button class="danger" onclick="resetConfirm()">${t('system.reset')}</button><input id="importer" class="hidden" type="file" accept=".json" onchange="readImport(this)"></div></section><section><div class="eyebrow">${t('system.storage')}</div><p>${t('system.local')}<br>${t('system.storageSummary',{memories:LifeI18n.number(mems().length),museum:LifeI18n.number(museum().length),tasks:LifeI18n.number(sys().tasksBoard?.tasks?.length||0),size:LifeI18n.number(Math.ceil(bytes/1024))})}</p></section><section><div class="eyebrow">${t('system.dataStorage')}</div><p>${t('system.stored')}<br>${t('system.backups')}</p><p>${t('system.lastBackup',{value:state.lastBackup ? short(state.lastBackup.slice(0,10)) : t('common.never')})}${oldBackup ? `<br><b style="color:var(--accent)">${t('system.recommended')}</b>` : ''}</p></section><section><div class="eyebrow">${t('system.version')}</div><p>${t('system.release',{version:window.LifeOSVersion||'1.5.3'})}</p></section></div>`;
|
||||
const state = settings(), preference = LifeI18n.preference(), bytes = new Blob([JSON.stringify({ mems: mems(), museum: museum(), profile: profile(), system: sys() })]).size, oldBackup = state.lastBackup && Date.now() - new Date(state.lastBackup) > 12096e5, detected = browserLanguage() === 'zh' ? t('system.detectedZh') : t('system.detectedEn');
|
||||
container.innerHTML = `<div class="eyebrow">${t('system.eyebrow')}</div><h1>${t('system.title')}</h1><div class="settings"><section><div class="eyebrow">${t('system.appearance')}</div><div class="choices">${['dark','light','auto'].map(item => `<button class="chip ${state.theme === item ? 'selected' : ''}" onclick="theme('${item}')">${t(`system.theme.${item}`)}</button>`).join('')}</div></section><section><div class="eyebrow">${t('system.language')}</div><p class="sub">${detected}</p><div class="choices"><button class="chip ${preference === 'auto' ? 'selected' : ''}" onclick="window.setLanguage('auto')">${t('system.auto')}</button><button class="chip ${preference === 'en' ? 'selected' : ''}" onclick="window.setLanguage('en')">${t('system.languageEnglish')}</button><button class="chip ${preference === 'zh' ? 'selected' : ''}" onclick="window.setLanguage('zh')">${t('system.languageChinese')}</button></div></section><section><div class="eyebrow">${t('system.data')}</div><div class="actions"><button class="primary" onclick="exportData()">${t('system.export')}</button><button class="secondary" onclick="document.querySelector('#importer').click()">${t('system.import')}</button><button class="danger" onclick="resetConfirm()">${t('system.reset')}</button><input id="importer" class="hidden" type="file" accept=".json" onchange="readImport(this)"></div></section><section><div class="eyebrow">${t('system.storage')}</div><p>${t('system.local')}<br>${t('system.storageSummary',{memories:LifeI18n.number(mems().length),museum:LifeI18n.number(museum().length),tasks:LifeI18n.number(sys().tasksBoard?.tasks?.length||0),size:LifeI18n.number(Math.ceil(bytes/1024))})}</p></section><section><div class="eyebrow">${t('system.dataStorage')}</div><p>${t('system.stored')}<br>${t('system.backups')}</p><p>${t('system.lastBackup',{value:state.lastBackup ? short(state.lastBackup.slice(0,10)) : t('common.never')})}${oldBackup ? `<br><b style="color:var(--accent)">${t('system.recommended')}</b>` : ''}</p></section><section><div class="eyebrow">${t('system.version')}</div><p>${t('system.release',{version:window.LifeOSVersion||'1.5.4'})}</p></section></div>`;
|
||||
};
|
||||
window.importPreview = () => { const data = importData, board = data.system?.tasksBoard || {}; rootModal().innerHTML = `<div class="modal"><div class="modalbox"><div class="eyebrow">${t('dialog.backup')}</div><h2>${t('dialog.ready')}</h2><p class="sub">${t('dialog.exported',{date:short(data.exportedAt.slice(0,10))})}<br>${t('dialog.memories',{count:data.memories.length})}<br>${t('dialog.museumItems',{count:data.museum.length})}<br>${t('dialog.goals',{count:board.goals?.length || 0})}<br>${t('dialog.tasks',{count:board.tasks?.length || 0})}</p><div class="actions"><button class="primary" onclick="applyImport('merge')">${t('dialog.merge')}</button><button class="danger" onclick="replaceConfirm()">${t('dialog.replaceAll')}</button><button class="secondary" onclick="modal.innerHTML=''">${t('common.cancel')}</button></div></div></div>`; };
|
||||
window.replaceConfirm = () => { rootModal().innerHTML = `<div class="modal"><div class="modalbox"><div class="eyebrow">${t('dialog.final')}</div><h2>${t('dialog.replaceQuestion')}</h2><p class="sub">${t('dialog.replaceWarning')}</p><div class="actions"><button class="danger" onclick="applyImport('replace')">${t('dialog.replace')}</button><button class="secondary" onclick="importPreview()">${t('common.cancel')}</button></div></div></div>`; };
|
||||
|
||||
+35
-4
@@ -3,20 +3,51 @@
|
||||
const dictionaries = window.LifeOSLocales || {};
|
||||
const en = dictionaries.en || {};
|
||||
const zh = dictionaries['zh-CN'] || {};
|
||||
const PREFERENCE_KEY = 'lifeos_language_preference_v1';
|
||||
const validPreference = value => ['auto', 'en', 'zh'].includes(value);
|
||||
const lookup = (object, path) => path.split('.').reduce((value, part) => value?.[part], object);
|
||||
const current = () => typeof window.lang === 'function' && window.lang() === 'zh' ? 'zh' : 'en';
|
||||
const detectBrowserLanguage = () => {
|
||||
const preferences = navigator.languages?.length ? navigator.languages : [navigator.language || navigator.userLanguage || 'en'];
|
||||
return preferences.some(value => String(value).toLowerCase().startsWith('zh')) ? 'zh' : 'en';
|
||||
};
|
||||
const readPreference = () => {
|
||||
try {
|
||||
const saved = localStorage.getItem(PREFERENCE_KEY);
|
||||
if (validPreference(saved)) return saved;
|
||||
let oldSettings = {};
|
||||
try { oldSettings = JSON.parse(localStorage.getItem('lifeos_settings') || '{}'); } catch {}
|
||||
const legacyMode = oldSettings.languageMode ?? (['en', 'zh'].includes(oldSettings.language) ? 'manual' : 'auto');
|
||||
const migrated = legacyMode === 'manual' && ['en', 'zh'].includes(oldSettings.language) ? oldSettings.language : 'auto';
|
||||
localStorage.setItem(PREFERENCE_KEY, migrated);
|
||||
return migrated;
|
||||
} catch { return 'auto'; }
|
||||
};
|
||||
let preference = readPreference();
|
||||
const language = () => preference === 'auto' ? detectBrowserLanguage() : preference;
|
||||
|
||||
function t(key, values = {}) {
|
||||
const selected = current() === 'zh' ? zh : en;
|
||||
const selected = language() === 'zh' ? zh : en;
|
||||
const template = lookup(selected, key) ?? lookup(en, key) ?? key;
|
||||
return String(template).replace(/\{(\w+)\}/g, (_, name) => values[name] ?? `{${name}}`);
|
||||
}
|
||||
|
||||
function locale() { return current() === 'zh' ? 'zh-CN' : 'en-US'; }
|
||||
function locale() { return language() === 'zh' ? 'zh-CN' : 'en-US'; }
|
||||
function date(value, options) {
|
||||
const parsed = value instanceof Date ? value : new Date(typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T12:00:00` : value);
|
||||
return new Intl.DateTimeFormat(locale(), options).format(parsed);
|
||||
}
|
||||
function number(value, options) { return new Intl.NumberFormat(locale(), options).format(value); }
|
||||
window.LifeI18n = { t, date, number, locale, dictionaries: { en, zh } };
|
||||
function setLanguage(value) {
|
||||
if (!validPreference(value)) return;
|
||||
preference = value;
|
||||
try { localStorage.setItem(PREFERENCE_KEY, value); } catch {}
|
||||
window.renderIcons?.();
|
||||
window.renderAll?.();
|
||||
window.applyLanguage?.();
|
||||
window.toast?.(value === 'auto' ? (language() === 'zh' ? 'feedback.followingZh' : 'feedback.followingEn') : (value === 'zh' ? 'feedback.languageZh' : 'feedback.languageEn'));
|
||||
}
|
||||
|
||||
window.lang = language;
|
||||
window.setLanguage = setLanguage;
|
||||
window.LifeI18n = { t, date, number, locale, language, preference: () => preference, dictionaries: { en, zh } };
|
||||
})();
|
||||
Loaded 3 of 8 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user