diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc4fc9a..380d0a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
本项目按 `v主版本.次版本.修订版本` 标记发布。下列内容以对应 Git tag 和已提交功能为准。
+## v1.9.0 — 2026-09-28
+
+- 增加轻量浏览器 App,默认在 iframe 中打开 Google,支持地址栏搜索、后退、前进与刷新。
+- 对不允许嵌入的网页提供始终可见的新标签页入口;浏览器入口同步接入桌面、任务栏、快捷键和双语界面。
+
## v1.8.0 — 2026-09-28
- 目标倒计时成为可拖动、可缩放的小部件,位置和尺寸保存在本设备。
diff --git a/README.md b/README.md
index 36fe1fe..9bdcdb2 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
以桌面操作系统为交互方式的个人记录网站。用 App 记录日常、管理目标与待办,并沿时间线回看已经留下的内容。
-在线站点:[Life OS](https://life-os-personal-desktop.biss0.chatgpt.site) · 当前应用版本:**v1.8.0** · [更新日志](CHANGELOG.md)
+在线站点:[Life OS](https://life-os-personal-desktop.biss0.chatgpt.site) · 当前应用版本:**v1.9.0** · [更新日志](CHANGELOG.md)
## 功能
@@ -15,11 +15,12 @@
| 日历 | 在月视图与日视图中查看记忆、任务和目标节点,并为选定日期新增记忆。 |
| 洞察 | 周报和月报展示记录频率、完成任务数、常见天气、能量记录及收藏或标记高光的记忆;仅依据已保存的数据统计。 |
| 档案 | 查看记录概况并维护个人资料。 |
+| 浏览器 | 在桌面窗口里打开网页或使用 Google 搜索,支持后退、前进、刷新和外部打开。默认打开 Google;第三方网站可能禁止 iframe 嵌入。 |
| 系统设置 | 选择语言、主题、壁纸、桌面快捷方式和开机 App,管理备份。 |
桌面端支持单击选择、双击打开 App、多窗口、任务栏恢复、图标拖动排列和窗口布局记忆。拖动图标或小部件时会显示吸附网格;目标倒计时、时钟、今日概览与快捷操作小部件可移动、调整大小和分别开关。移动端以横排图标和全屏 App 为主,小部件在桌面下方排列。可选必应每日图片、内置背景或自定义 HTTPS 图片链接。中文与英文可自动跟随浏览器语言,也可手动选择。
-快捷键:`Ctrl / ⌘ + K` 打开启动器,`Ctrl / ⌘ + F` 搜索记忆,`Ctrl / ⌘ + S` 保存今日记忆;`Alt + 1–8` 打开对应 App。站点包含 PWA 清单和 Service Worker,离线时可打开已缓存的应用外壳;云同步与必应图片仍需要联网。
+快捷键:`Ctrl / ⌘ + K` 打开启动器,`Ctrl / ⌘ + F` 搜索记忆,`Ctrl / ⌘ + S` 保存今日记忆;`Alt + 1–9` 打开对应 App。站点包含 PWA 清单和 Service Worker,离线时可打开已缓存的应用外壳;云同步、外部网页与必应图片仍需要联网。
## 数据与备份
@@ -46,6 +47,7 @@ npm run validate
public/index.html 桌面框架与窗口基础交互
public/tasks.js 目标与待办
public/calendar.js 日历
+public/browser-app.js iframe 浏览器
public/i18n-renderers.js 记忆、洞察、设置等双语界面
public/desktop-personalization.js 图标、窗口与壁纸偏好
public/desktop-widgets.js 吸附网格与桌面小部件
diff --git a/package.json b/package.json
index 20d94ef..f0a93b5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "life-os",
- "version": "1.8.0",
+ "version": "1.9.0",
"private": true,
"type": "module",
"scripts": {
diff --git a/public/addons.js b/public/addons.js
index efa4b06..f56b0a8 100644
--- a/public/addons.js
+++ b/public/addons.js
@@ -3,9 +3,9 @@
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.8.0';
+ const APP_VERSION = '1.9.0';
window.LifeOSVersion = APP_VERSION;
- const appIds = ['today', 'timeline', 'museum', 'insights', 'profile', 'tasks', 'calendar', 'system'];
+ const appIds = ['today', 'timeline', 'museum', 'insights', 'profile', 'tasks', 'calendar', 'system', 'browser'];
const t = (key, values) => LifeI18n.t(key, values);
function readDraft() {
@@ -171,7 +171,7 @@
if (mod && e.key.toLowerCase() === 'k') { e.preventDefault(); toggleLauncher(); }
if (mod && e.key.toLowerCase() === 'f') { e.preventDefault(); openSearch(); }
if (mod && e.key.toLowerCase() === 's' && document.querySelector('#entry')) { e.preventDefault(); archiveToday(); }
- if (e.altKey && /^[1-8]$/.test(e.key)) { e.preventDefault(); openApp(appIds[Number(e.key) - 1]); }
+ if (e.altKey && /^[1-9]$/.test(e.key)) { e.preventDefault(); openApp(appIds[Number(e.key) - 1]); }
if (e.key === 'Escape') { closeSearch(); document.querySelector('#launcher')?.classList.add('hidden'); document.querySelector('#calendar')?.classList.add('hidden'); document.querySelector('#life-context')?.remove(); }
});
})();
diff --git a/public/browser-app.js b/public/browser-app.js
new file mode 100644
index 0000000..1d7bfd5
--- /dev/null
+++ b/public/browser-app.js
@@ -0,0 +1,65 @@
+/* Small iframe browser. Cross-origin pages decide whether they allow embedding. */
+(() => {
+ const HOME = 'https://www.google.com/webhp?igu=1';
+ const pages = [HOME];
+ let position = 0;
+ const t = key => LifeI18n.t(`browser.${key}`);
+ const container = () => document.querySelector('#content-browser');
+ const current = () => pages[position];
+
+ function address(value) {
+ const input = String(value || '').trim();
+ if (!input) return HOME;
+ if (/^[a-z][a-z\d+.-]*:/i.test(input) && !/^https?:\/\//i.test(input)) return null;
+ let url;
+ try {
+ if (/^https?:\/\//i.test(input)) url = new URL(input);
+ else if (/^[^\s/]+\.[^\s]+/.test(input)) url = new URL(`https://${input}`);
+ else return `https://www.google.com/search?igu=1&q=${encodeURIComponent(input)}`;
+ } catch { return null; }
+ if (!['http:', 'https:'].includes(url.protocol) || !url.hostname) return null;
+ if (url.protocol === 'http:') url.protocol = 'https:';
+ if (['google.com', 'www.google.com'].includes(url.hostname.toLowerCase())) {
+ if (url.pathname === '/') url.pathname = '/webhp';
+ if (['/webhp', '/search'].includes(url.pathname)) url.searchParams.set('igu', '1');
+ }
+ return url.href;
+ }
+
+ function updateChrome(node, navigate = false) {
+ const frame = node.querySelector('iframe'), input = node.querySelector('.browser-address');
+ if (!frame) return;
+ if (navigate || !frame.getAttribute('src')) frame.src = current();
+ if (input && document.activeElement !== input) input.value = current();
+ node.querySelector('[data-action="back"]').disabled = position === 0;
+ node.querySelector('[data-action="forward"]').disabled = position === pages.length - 1;
+ node.querySelector('.browser-external').href = current();
+ node.querySelector('.browser-hint-link').href = current();
+ }
+
+ function go(value) {
+ const url = address(value);
+ if (!url) { toast(t('invalid')); return; }
+ if (url !== current()) { pages.splice(position + 1); pages.push(url); position = pages.length - 1; }
+ const node = container(); if (node) updateChrome(node, true);
+ }
+
+ window.browserApp = node => {
+ if (node.querySelector('.browser-shell') && node.dataset.browserLocale === lang()) { updateChrome(node); return; }
+ node.dataset.browserLocale = lang();
+ node.innerHTML = `
`;
+ const toolbar = node.querySelector('.browser-toolbar');
+ toolbar.addEventListener('submit', event => { event.preventDefault(); go(node.querySelector('.browser-address').value); });
+ toolbar.addEventListener('click', event => {
+ const action = event.target.closest('[data-action]')?.dataset.action;
+ if (action === 'back' && position > 0) { position--; updateChrome(node, true); }
+ if (action === 'forward' && position < pages.length - 1) { position++; updateChrome(node, true); }
+ if (action === 'reload') updateChrome(node, true);
+ });
+ updateChrome(node, true);
+ };
+
+ const style = document.createElement('style');
+ style.textContent = `#content-browser{padding:0;overflow:hidden}.browser-shell{height:100%;min-height:0;display:flex;flex-direction:column}.browser-toolbar{display:flex;align-items:center;gap:6px;padding:10px;background:var(--panel);border-bottom:1px solid var(--line)}.browser-toolbar button,.browser-external{flex:none;display:grid;place-items:center;min-width:34px;height:34px;border:1px solid var(--line);border-radius:8px;background:var(--panel2);color:var(--ink);text-decoration:none;font-size:18px}.browser-toolbar button:disabled{opacity:.38;cursor:default}.browser-toolbar .browser-go{font-size:12px;padding:0 11px;background:var(--accent);color:var(--bg);border-color:var(--accent)}.browser-address{flex:1;min-width:0;height:36px;border:1px solid var(--line);border-radius:8px;background:var(--panel2);color:var(--ink);padding:0 11px;font:13px Manrope,system-ui;outline:none}.browser-address:focus{border-color:var(--accent)}.browser-frame-wrap{flex:1;min-height:0;background:#fff}.browser-frame-wrap iframe{display:block;width:100%;height:100%;border:0}.browser-hint{margin:0;padding:7px 12px;border-top:1px solid var(--line);color:var(--muted);font-size:12px;line-height:1.4}.browser-hint a{color:var(--accent)}@media(max-width:650px){.browser-toolbar{gap:4px;padding:8px}.browser-toolbar button,.browser-external{min-width:30px;height:32px}.browser-address{font-size:12px}.browser-hint{padding:7px 9px;font-size:11px}}`;
+ document.head.append(style);
+})();
diff --git a/public/i18n-renderers.js b/public/i18n-renderers.js
index 4da1616..af0e03d 100644
--- a/public/i18n-renderers.js
+++ b/public/i18n-renderers.js
@@ -110,7 +110,7 @@
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(), 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 = `${t('system.eyebrow')}
${t('system.title')}
${t('system.appearance')}
${['dark','light','auto'].map(item => ``).join('')}
${t('system.language')}
${detected}
${t('system.storage')}
${t('system.local')}
${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))})}
${t('system.dataStorage')}
${t('system.stored')}
${t('system.backups')}
${t('system.lastBackup',{value:state.lastBackup ? short(state.lastBackup.slice(0,10)) : t('common.never')})}${oldBackup ? `
${t('system.recommended')}` : ''}
${t('system.version')}
${t('system.release',{version:window.LifeOSVersion||'1.8.0'})}
`;
+ container.innerHTML = `${t('system.eyebrow')}
${t('system.title')}
${t('system.appearance')}
${['dark','light','auto'].map(item => ``).join('')}
${t('system.language')}
${detected}
${t('system.storage')}
${t('system.local')}
${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))})}
${t('system.dataStorage')}
${t('system.stored')}
${t('system.backups')}
${t('system.lastBackup',{value:state.lastBackup ? short(state.lastBackup.slice(0,10)) : t('common.never')})}${oldBackup ? `
${t('system.recommended')}` : ''}
${t('system.version')}
${t('system.release',{version:window.LifeOSVersion||'1.9.0'})}
`;
};
window.importPreview = () => { const data = importData, board = data.system?.tasksBoard || {}; rootModal().innerHTML = `${t('dialog.backup')}
${t('dialog.ready')}
${t('dialog.exported',{date:short(data.exportedAt.slice(0,10))})}
${t('dialog.memories',{count:data.memories.length})}
${t('dialog.museumItems',{count:data.museum.length})}
${t('dialog.goals',{count:board.goals?.length || 0})}
${t('dialog.tasks',{count:board.tasks?.length || 0})}
`; };
window.replaceConfirm = () => { rootModal().innerHTML = `${t('dialog.final')}
${t('dialog.replaceQuestion')}
${t('dialog.replaceWarning')}
`; };
diff --git a/public/index.html b/public/index.html
index 9872310..cfa90fd 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,7 +5,7 @@
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-family:Manrope,system-ui;overflow:hidden}button,input,textarea{font:inherit;color:inherit}button{cursor:pointer}.desktop{height:100vh;background:radial-gradient(ellipse at 20% 0%,#3f4a503d,transparent 40%),radial-gradient(ellipse at 93% 65%,#59604224,transparent 42%),var(--bg);position:relative}.brand{position:absolute;top:28px;left:32px;font:500 11px 'DM Mono';letter-spacing:.18em;color:var(--muted)}.desktop-icons{padding:72px 32px;display:grid;grid-template-columns:repeat(3,96px);gap:22px}.app-icon{border:0;background:transparent;color:var(--ink);display:grid;gap:9px;justify-items:center;font:500 10px 'DM Mono';letter-spacing:.12em}.glyph{width:48px;height:48px;background:var(--panel);border:1px solid var(--line);border-radius:14px;display:grid;place-items:center;box-shadow:0 8px 22px #0002;font-size:20px}.app-icon:hover .glyph,.taskapp.active{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 12%,transparent)}.taskbar{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);height:54px;display:flex;align-items:center;gap:5px;padding:6px;background:color-mix(in srgb,var(--panel) 94%,transparent);border:1px solid var(--line);border-radius:17px;box-shadow:var(--shadow);z-index:1000}.life,.taskapp,.clock{height:40px;border:0;border-radius:11px;background:transparent;color:var(--ink);padding:0 12px;font:500 11px 'DM Mono';letter-spacing:.1em}.life{background:var(--accent);color:#151812;font-weight:500}.taskapp{min-width:42px;font-size:17px;padding:0 10px}.clock{border-left:1px solid var(--line);line-height:1.35;text-align:left;font-size:10px}.launcher,.calendar{position:absolute;bottom:82px;background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:17px;padding:19px;z-index:1200}.launcher{left:calc(50% - 310px);width:300px}.calendar{right:calc(50% - 310px);width:230px}.hidden{display:none!important}.launchhead{display:flex;justify-content:space-between;font:500 11px 'DM Mono';letter-spacing:.12em}.launchgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:18px 0}.launchitem{border:1px solid transparent;background:var(--panel2);border-radius:10px;padding:13px 7px;color:var(--ink);font:500 9px 'DM Mono';letter-spacing:.08em}.launchitem:hover{border-color:var(--accent)}.systemline{border-top:1px solid var(--line);padding-top:12px;color:var(--muted);font:10px 'DM Mono';letter-spacing:.05em}.window{position:absolute;width:min(700px,calc(100vw - 100px));height:min(590px,calc(100vh - 130px));background:var(--panel);border:1px solid var(--line);border-radius:15px;box-shadow:var(--shadow);overflow:hidden;animation:in .18s ease;display:flex;flex-direction:column}.window.max{width:calc(100vw - 36px)!important;height:calc(100vh - 98px)!important;left:18px!important;top:18px!important}.titlebar{height:48px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--line);padding:0 13px;cursor:grab;flex:none}.titlebar:active{cursor:grabbing}.titleicon{width:22px;height:22px;border:1px solid var(--line);border-radius:7px;display:grid;place-items:center;font-size:12px}.wtitle{font:500 11px 'DM Mono';letter-spacing:.13em;flex:1}.controls{display:flex;gap:5px}.controls button{width:25px;height:25px;border:0;background:transparent;color:var(--muted);border-radius:6px}.controls button:hover{background:var(--panel2);color:var(--ink)}.content{padding:24px;overflow:auto;height:100%}.eyebrow{font:10px 'DM Mono';letter-spacing:.16em;color:var(--muted);margin-bottom:8px}h1,h2,p{margin-top:0}h1{font-size:28px;letter-spacing:-.05em;margin-bottom:3px}h2{font-size:13px;letter-spacing:.07em}.sub{color:var(--muted);font-size:14px}.today-grid{display:grid;grid-template-columns:1.3fr .7fr;gap:24px}.entry{width:100%;height:190px;resize:none;background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:15px;outline:none;line-height:1.55}.entry:focus{border-color:var(--accent)}.choices{display:flex;gap:7px;flex-wrap:wrap}.chip{border:1px solid var(--line);border-radius:99px;background:transparent;padding:8px 10px;color:var(--muted);font:10px 'DM Mono';letter-spacing:.06em}.chip.selected{background:var(--accent);border-color:var(--accent);color:#171b12}.energy{display:flex;gap:7px}.energy button{width:31px;height:31px;border:1px solid var(--line);background:var(--panel2);color:var(--muted);border-radius:6px}.energy button.selected{background:var(--accent);color:#171b12;border-color:var(--accent)}.film{border-top:1px solid var(--line);margin-top:18px;padding-top:16px}.film-title{font-size:20px;letter-spacing:-.03em}.film-copy{color:var(--muted);font-size:13px;line-height:1.55}.primary,.secondary,.danger{border:0;padding:11px 14px;border-radius:9px;font:500 10px 'DM Mono';letter-spacing:.1em}.primary{background:var(--accent);color:#151812}.secondary{background:var(--panel2);border:1px solid var(--line)}.danger{background:#a94e4e;color:#fff}.actions{display:flex;gap:9px;margin-top:18px}.status{font:10px 'DM Mono';letter-spacing:.1em;color:var(--accent);align-self:center}.timeline{position:relative;margin:8px 0}.memory{border-left:1px solid var(--line);padding:0 0 23px 25px;position:relative}.memory:before{content:'';position:absolute;left:-4px;top:5px;width:7px;height:7px;background:var(--accent);border-radius:50%}.memory button{all:unset;cursor:pointer;width:100%}.memory .date{font:10px 'DM Mono';color:var(--muted);letter-spacing:.1em}.memory .mtitle{font-size:18px;margin:5px 0}.memory .snippet{color:var(--muted);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.empty{display:grid;place-items:center;min-height:280px;color:var(--muted);text-align:center;font-size:14px}.museum{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.artifact{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:17px;text-align:left;color:var(--ink)}.artifact:hover{border-color:var(--accent)}.artifact span{display:block;font:10px 'DM Mono';color:var(--muted);letter-spacing:.08em}.artifact strong{display:block;font-size:18px;margin:18px 0 7px;letter-spacing:-.04em}.insights{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.metric{border:1px solid var(--line);border-radius:12px;padding:18px;min-height:120px}.metric .big{font-size:36px;letter-spacing:-.07em}.bars{display:flex;align-items:end;height:56px;gap:5px;margin-top:12px}.bar{background:var(--accent);opacity:.78;width:12%;border-radius:3px 3px 0 0}.profile-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:20px 0}.stat{border-top:1px solid var(--line);padding-top:10px}.stat b{display:block;font-size:24px;letter-spacing:-.06em}.stat span{font:10px 'DM Mono';color:var(--muted);letter-spacing:.08em}.formrow{display:grid;grid-template-columns:1fr 1.5fr;gap:12px;align-items:center;margin:9px 0}.formrow input{border:1px solid var(--line);background:var(--panel2);border-radius:8px;padding:10px}.settings section{border-top:1px solid var(--line);padding:17px 0}.settings section:first-child{border-top:0}.settings p{color:var(--muted);font-size:13px;line-height:1.55}.modal{position:fixed;inset:0;background:#0008;display:grid;place-items:center;z-index:5000}.modalbox{position:relative;width:min(410px,calc(100vw - 36px));background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:23px;box-shadow:var(--shadow)}.modal-close{position:absolute;top:12px;right:12px;width:32px;height:32px;border:1px solid var(--line);border-radius:50%;background:transparent;color:var(--ink);font-size:24px;line-height:1;cursor:pointer}.modal-close:hover,.modal-close:focus-visible{background:var(--accent);color:#fff;outline:none}.toast{position:fixed;top:24px;left:50%;transform:translateX(-50%);background:var(--accent);color:#131611;padding:12px 15px;border-radius:9px;z-index:6000;font:500 10px 'DM Mono';letter-spacing:.1em;animation:in .18s ease}.boot{position:fixed;inset:0;background:var(--bg);z-index:9000;display:grid;place-content:center;text-align:center;gap:13px;font:12px 'DM Mono';letter-spacing:.16em}.boot b{font-size:28px;color:var(--ink)}@keyframes in{from{opacity:0;transform:scale(.98)}to{opacity:1;transform:scale(1)}}
@media(max-width:650px){body{overflow:auto}.desktop{min-height:100vh}.brand{left:22px;top:20px}.desktop-icons{padding:79px 20px 100px;grid-template-columns:repeat(3,1fr);gap:28px 16px}.taskbar{bottom:11px;width:calc(100% - 22px);justify-content:space-between}.taskapp{display:inline-grid;place-items:center;min-width:32px;padding:0 6px}.clock{display:none}.window{position:fixed!important;inset:0!important;width:100%!important;height:100%!important;border:0;border-radius:0;z-index:2000!important}.window.min{display:none}.titlebar{padding-top:env(safe-area-inset-top);height:53px}.content{padding:20px}.today-grid{grid-template-columns:1fr}.launcher{left:11px;bottom:76px;width:calc(100% - 22px)}.calendar{right:11px;bottom:76px}.museum,.insights,.profile-grid{grid-template-columns:1fr 1fr}.profile-grid{gap:15px}.formrow{grid-template-columns:1fr}.boot{font-size:10px}}
LIFE OSINITIALIZING PERSONAL SYSTEM...