Skip to content

feat(webui): add pwa support#1688

Merged
piorpua merged 7 commits intoiOfficeAI:mainfrom
Ericwong5021:feat/webui-pwa
Mar 27, 2026
Merged

feat(webui): add pwa support#1688
piorpua merged 7 commits intoiOfficeAI:mainfrom
Ericwong5021:feat/webui-pwa

Conversation

@Ericwong5021
Copy link
Copy Markdown
Contributor

@Ericwong5021 Ericwong5021 commented Mar 24, 2026

Summary
Add PWA support to the WebUI for mobile installability and basic offline capability
Add manifest.webmanifest, PWA icons, service worker registration, and manifest metadata in the WebUI renderer
Add tests covering service worker registration and PWA-related build output

Test plan

  • WebUI serves a valid manifest.webmanifest
  • PWA icons are included in the WebUI build output
  • Service worker registers successfully in supported browsers
  • WebUI can be added to the home screen in a secure context
  • Installed WebUI opens in standalone mode on supported mobile browsers
  • Basic offline access works for previously cached core assets
  • All tests pass: bun run test

Notes
PWA installability requires a secure context:

  • https:// for remote/mobile access
  • http://localhost for local development only
  • Plain public HTTP such as http://<public-ip>:<port> will not enable full PWA behavior in Android Chrome

@kaizhou-lab
Copy link
Copy Markdown
Collaborator

public/sw.jsnetworkFirststaleWhileRevalidate 两个策略在 cache.put 之前没有检查 response.ok,4xx/5xx 的响应也会被写进缓存。

比如服务器短暂返回 500 之后网络中断,离线时会把缓存的 500 页面返回给用户;staleWhileRevalidate 的后台更新也可能用错误响应覆盖掉正常缓存。

建议加个 response.ok 判断再写缓存:

// networkFirst
const response = await fetch(request);
if (response.ok) {
  cache.put(request, response.clone());
}
return response;

// staleWhileRevalidate
.then((response) => {
  if (response.ok) {
    cache.put(request, response.clone());
  }
  return response;
})

@kaizhou-lab kaizhou-lab self-assigned this Mar 26, 2026
@Ericwong5021
Copy link
Copy Markdown
Contributor Author

public/sw.jsnetworkFirststaleWhileRevalidate 两个策略在 cache.put 之前没有检查 response.ok,4xx/5xx 的响应也会被写进缓存。

比如服务器短暂返回 500 之后网络中断,离线时会把缓存的 500 页面返回给用户;staleWhileRevalidate 的后台更新也可能用错误响应覆盖掉正常缓存。

建议加个 response.ok 判断再写缓存:

// networkFirst
const response = await fetch(request);
if (response.ok) {
  cache.put(request, response.clone());
}
return response;

// staleWhileRevalidate
.then((response) => {
  if (response.ok) {
    cache.put(request, response.clone());
  }
  return response;
})

已处理,感谢指出。

这边修了两处:

  • networkFirst / staleWhileRevalidate 中增加 response.ok 判断,避免把 4xx/5xx 响应写入缓存,防止错误页污染已有缓存。
  • 额外补充了一处缓存硬化:将 /qr-login 排除出 service worker 的处理范围,避免带一次性 token 的二维码登录页进入离线缓存。

同时补充了对应测试用例,覆盖上述两个场景。

@piorpua piorpua added bot:reviewing Review in progress (mutex) and removed bot:reviewing Review in progress (mutex) labels Mar 27, 2026
@sentry
Copy link
Copy Markdown

sentry bot commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 83.33333% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/renderer/services/registerPwa.ts 88.23% 1 Missing and 1 partial ⚠️
src/renderer/main.tsx 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@piorpua piorpua added bot:reviewing Review in progress (mutex) and removed bot:reviewing Review in progress (mutex) labels Mar 27, 2026
@piorpua piorpua added bot:reviewing Review in progress (mutex) and removed bot:reviewing Review in progress (mutex) labels Mar 27, 2026
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 27, 2026

Code Review:feat(webui): add pwa support (#1688)

变更概述

本 PR 为 WebUI 添加 PWA 支持,包含:manifest、PWA 图标、service worker(public/sw.js)、service worker 注册逻辑(registerPwa.ts)及 HTML meta 标签。同时更新了 vite.renderer.config.tselectron.vite.config.ts,将 public/ 目录正确配置为 publicDir。PR 讨论中提到的 response.ok 校验和 /qr-login 排除问题已在本版本中修复。


方案评估

结论:✅ 方案合理

方案采用原生 Service Worker API 而非重型框架(如 Workbox),对于本场景是合适的轻量选择。networkFirst 用于导航请求、staleWhileRevalidate 用于静态资源,策略分层清晰。registerPwa.ts 通过 isElectronDesktop() 正确跳过 Electron 运行时,publicDir 的两处配置(vite.renderer.config.tselectron.vite.config.ts)确保了开发和生产构建均能正确服务 PWA 文件。


问题清单

🟡 MEDIUM — registerPwa.ts 测试覆盖率不足(约 58%,低于项目 80% 目标)

文件src/renderer/services/registerPwa.ts

问题说明:Codecov 报告 registerPwa.ts 覆盖率约 58%(4 行 missing,3 处 partial branch)。isPwaRegistrationSupported 中的若干防御分支未被测试:

  • !('serviceWorker' in navigator) 为 false 的路径
  • protocol !== 'http:'protocol !== 'https:' 的路径(非 HTTP 协议,如 file:
  • !window.isSecureContext && !LOCALHOST_HOSTS.has(hostname) 的路径(非 localhost 且非安全上下文 → 返回 false)
  • registerPwa()catch 块(serviceWorker.register 抛出异常时的路径)

修复建议:在 registerPwa.dom.test.ts 中补充以下用例:

it('skips registration when serviceWorker is not in navigator', async () => {
  Object.defineProperty(window, 'electronAPI', { configurable: true, value: undefined });
  // navigator.serviceWorker not defined
  await expect(registerPwa()).resolves.toBeUndefined();
});

it('skips registration on insecure non-localhost origin', async () => {
  Object.defineProperty(window, 'electronAPI', { configurable: true, value: undefined });
  Object.defineProperty(navigator, 'serviceWorker', {
    configurable: true,
    value: { register: vi.fn() },
  });
  Object.defineProperty(window, 'location', {
    configurable: true,
    value: { protocol: 'http:', hostname: '192.168.1.1', isSecureContext: false },
  });
  await expect(registerPwa()).resolves.toBeUndefined();
});

it('returns undefined when service worker registration throws', async () => {
  const register = vi.fn().mockRejectedValue(new Error('Security error'));
  Object.defineProperty(window, 'electronAPI', { configurable: true, value: undefined });
  Object.defineProperty(navigator, 'serviceWorker', { configurable: true, value: { register } });
  await expect(registerPwa()).resolves.toBeUndefined();
});

汇总

# 严重级别 文件 问题
1 🟡 MEDIUM src/renderer/services/registerPwa.ts 覆盖率约 58%,低于 80% 目标,缺少 isPwaRegistrationSupported 的多条防御分支及 catch 路径测试

结论

⚠️ 有条件批准 — 实现本身正确、PR 讨论反馈已处理,仅需补充 registerPwa.ts 的缺失测试分支(约需 3–4 个用例)后可合并。


本报告由本地 pr-review skill 生成,包含完整项目上下文,无截断限制。

@piorpua piorpua added bot:ready-to-fix CONDITIONAL review done, waiting for bot fix bot:fixing Fix in progress (mutex) and removed bot:reviewing Review in progress (mutex) bot:ready-to-fix CONDITIONAL review done, waiting for bot fix labels Mar 27, 2026
- Add test for missing serviceWorker in navigator
- Add test for non-http protocol (file: scheme)
- Add test for insecure non-localhost origin
- Add test for service worker registration failure (catch path)

Review follow-up for iOfficeAI#1688
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 27, 2026

PR Fix 验证报告

原始 PR: #1688
修复方式: 直接推送到 feat/webui-pwa

# 严重级别 文件 问题 修复方式 状态
1 🟡 MEDIUM tests/unit/renderer/services/registerPwa.dom.test.ts 覆盖率约 58%,缺少 isPwaRegistrationSupported 多条防御分支及 catch 路径测试 新增 4 个测试用例:serviceWorker 缺失、非 HTTP 协议、不安全非 localhost 源、注册异常 catch 路径 ✅ 已修复

总结: ✅ 已修复 1 个 | ❌ 未能修复 0 个

覆盖率由约 58% 提升,新增测试覆盖所有缺失的防御分支。全部 141 个测试文件通过(6 个测试用例→全部通过)。

@piorpua piorpua enabled auto-merge (squash) March 27, 2026 08:42
@piorpua piorpua added bot:done Auto-merged by bot and removed bot:fixing Fix in progress (mutex) labels Mar 27, 2026
@piorpua piorpua merged commit 44e32cf into iOfficeAI:main Mar 27, 2026
6 of 13 checks passed
@Ericwong5021 Ericwong5021 deleted the feat/webui-pwa branch March 30, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:done Auto-merged by bot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants