# Progressive Web App (PWA) Guide (中文 (简体)) 🌐 **Languages:** 🇺🇸 [English](../../../../guides/PWA_GUIDE.md) · 🇪🇹 [am](../../../am/docs/guides/PWA_GUIDE.md) · 🇸🇦 [ar](../../../ar/docs/guides/PWA_GUIDE.md) · 🇦🇿 [az](../../../az/docs/guides/PWA_GUIDE.md) · 🇧🇬 [bg](../../../bg/docs/guides/PWA_GUIDE.md) · 🇧🇩 [bn](../../../bn/docs/guides/PWA_GUIDE.md) · 🇧🇦 [bs](../../../bs/docs/guides/PWA_GUIDE.md) · 🇨🇿 [cs](../../../cs/docs/guides/PWA_GUIDE.md) · 🇩🇰 [da](../../../da/docs/guides/PWA_GUIDE.md) · 🇩🇪 [de](../../../de/docs/guides/PWA_GUIDE.md) · 🇬🇷 [el](../../../el/docs/guides/PWA_GUIDE.md) · 🇪🇸 [es](../../../es/docs/guides/PWA_GUIDE.md) · 🇪🇪 [et](../../../et/docs/guides/PWA_GUIDE.md) · 🇮🇷 [fa](../../../fa/docs/guides/PWA_GUIDE.md) · 🇫🇮 [fi](../../../fi/docs/guides/PWA_GUIDE.md) · 🇫🇷 [fr](../../../fr/docs/guides/PWA_GUIDE.md) · 🇮🇪 [ga](../../../ga/docs/guides/PWA_GUIDE.md) · 🇮🇳 [gu](../../../gu/docs/guides/PWA_GUIDE.md) · 🇳🇬 [ha](../../../ha/docs/guides/PWA_GUIDE.md) · 🇮🇱 [he](../../../he/docs/guides/PWA_GUIDE.md) · 🇮🇳 [hi](../../../hi/docs/guides/PWA_GUIDE.md) · 🇭🇷 [hr](../../../hr/docs/guides/PWA_GUIDE.md) · 🇭🇺 [hu](../../../hu/docs/guides/PWA_GUIDE.md) · 🇦🇲 [hy](../../../hy/docs/guides/PWA_GUIDE.md) · 🇮🇩 [id](../../../id/docs/guides/PWA_GUIDE.md) · 🇳🇬 [ig](../../../ig/docs/guides/PWA_GUIDE.md) · 🇮🇹 [it](../../../it/docs/guides/PWA_GUIDE.md) · 🇯🇵 [ja](../../../ja/docs/guides/PWA_GUIDE.md) · 🇬🇪 [ka](../../../ka/docs/guides/PWA_GUIDE.md) · 🇰🇭 [km](../../../km/docs/guides/PWA_GUIDE.md) · 🇮🇳 [kn](../../../kn/docs/guides/PWA_GUIDE.md) · 🇰🇷 [ko](../../../ko/docs/guides/PWA_GUIDE.md) · 🇱🇹 [lt](../../../lt/docs/guides/PWA_GUIDE.md) · 🇱🇻 [lv](../../../lv/docs/guides/PWA_GUIDE.md) · 🇮🇳 [ml](../../../ml/docs/guides/PWA_GUIDE.md) · 🇮🇳 [mr](../../../mr/docs/guides/PWA_GUIDE.md) · 🇲🇾 [ms](../../../ms/docs/guides/PWA_GUIDE.md) · 🇲🇹 [mt](../../../mt/docs/guides/PWA_GUIDE.md) · 🇲🇲 [my](../../../my/docs/guides/PWA_GUIDE.md) · 🇳🇵 [ne](../../../ne/docs/guides/PWA_GUIDE.md) · 🇳🇱 [nl](../../../nl/docs/guides/PWA_GUIDE.md) · 🇳🇴 [no](../../../no/docs/guides/PWA_GUIDE.md) · 🇮🇳 [or](../../../or/docs/guides/PWA_GUIDE.md) · 🇮🇳 [pa](../../../pa/docs/guides/PWA_GUIDE.md) · 🇵🇭 [phi](../../../phi/docs/guides/PWA_GUIDE.md) · 🇵🇱 [pl](../../../pl/docs/guides/PWA_GUIDE.md) · 🇵🇹 [pt](../../../pt/docs/guides/PWA_GUIDE.md) · 🇧🇷 [pt-BR](../../../pt-BR/docs/guides/PWA_GUIDE.md) · 🇷🇴 [ro](../../../ro/docs/guides/PWA_GUIDE.md) · 🇷🇺 [ru](../../../ru/docs/guides/PWA_GUIDE.md) · 🇱🇰 [si](../../../si/docs/guides/PWA_GUIDE.md) · 🇸🇰 [sk](../../../sk/docs/guides/PWA_GUIDE.md) · 🇸🇮 [sl](../../../sl/docs/guides/PWA_GUIDE.md) · 🇷🇸 [sr](../../../sr/docs/guides/PWA_GUIDE.md) · 🇸🇪 [sv](../../../sv/docs/guides/PWA_GUIDE.md) · 🇰🇪 [sw](../../../sw/docs/guides/PWA_GUIDE.md) · 🇮🇳 [ta](../../../ta/docs/guides/PWA_GUIDE.md) · 🇮🇳 [te](../../../te/docs/guides/PWA_GUIDE.md) · 🇹🇭 [th](../../../th/docs/guides/PWA_GUIDE.md) · 🇹🇷 [tr](../../../tr/docs/guides/PWA_GUIDE.md) · 🇺🇦 [uk-UA](../../../uk-UA/docs/guides/PWA_GUIDE.md) · 🇵🇰 [ur](../../../ur/docs/guides/PWA_GUIDE.md) · 🇺🇿 [uz](../../../uz/docs/guides/PWA_GUIDE.md) · 🇻🇳 [vi](../../../vi/docs/guides/PWA_GUIDE.md) · 🇳🇬 [yo](../../../yo/docs/guides/PWA_GUIDE.md) · 🇹🇼 [zh-TW](../../../zh-TW/docs/guides/PWA_GUIDE.md) --- OmniRoute 以可完整安装的渐进式 Web 应用形式提供。当您通过任何移动浏览器访问仪表板时——Android(Chrome)或 iOS(Safari)——都可以选择“添加到主屏幕”,无需应用商店即可获得类似原生应用的体验。 ## 什么是 PWA? 渐进式 Web 应用可将 OmniRoute Web 仪表板变成一个外观和使用体验都类似原生移动应用的程序。安装后,它可以: - 使用专属图标从主屏幕启动 - 以全屏模式打开——没有浏览器地址栏或标签页界面 - 通过专用的连接状态页面支持离线使用 - 缓存静态资源以加快加载速度 - 同时支持竖屏和横屏方向 ## 安装 ### Android(Chrome) 1. 在 Chrome 中打开 OmniRoute 仪表板:`http://YOUR_IP:20128` 2. Chrome 会自动显示 **“将 OmniRoute 添加到主屏幕”**横幅,或者: - 点击 **⋮** 菜单(三个点)→ **“添加到主屏幕”**或**“安装应用”** 3. 确认提示 4. OmniRoute 将作为独立应用显示在您的主屏幕上 ### iOS(Safari) 1. 在 Safari 中打开 OmniRoute 仪表板:`http://YOUR_IP:20128` 2. 点击**共享**按钮(带箭头的方框) 3. 向下滚动并点击**“添加到主屏幕”** 4. 为其命名(默认为“OmniRoute”),然后点击**添加** 5. OmniRoute 将使用应用图标显示在您的主屏幕上 ### 桌面端(Chrome / Edge) 1. 打开 OmniRoute 仪表板 2. 点击地址栏中的**安装图标**(或 ⋮ →“安装 OmniRoute...”) 3. 确认提示 4. OmniRoute 将作为独立窗口打开——没有标签页,也没有地址栏 ## 功能 ### 全屏体验 清单配置了 `display: "fullscreen"`,这意味着安装后的应用会使用整个屏幕——没有浏览器界面,也不会与状态栏重叠。这使仪表板真正拥有原生应用般的体验。 ### 离线支持 OmniRoute 包含一个提供智能缓存功能的服务工作线程(`sw.js`): | 资源类型 | 策略 | 行为 | | ----------------------------------------------------- | ------------------ | ---------------------------------------------------------------------------- | | **应用外壳** | 缓存优先 | 安装时预缓存 `/`、`/offline`、清单和图标 | | **静态资源**(CSS、JS、图像、字体) | 网络优先,缓存回退 | 从网络获取最新资源;离线时回退到缓存 | | **Next.js 软件包**(`/_next/`) | 网络优先并更新缓存 | 从网络获取并更新缓存;离线时提供缓存版本 | | **导航请求** | 绕过(从不拦截) | 浏览器负责 HTTP/3→HTTP/2 回退;失效的 QUIC 套接字不得变成 `Response.error()` | | **API / 仪表板路由**(`/api/`、`/a2a`、`/dashboard`) | 绕过(从不缓存) | 始终直接访问服务器——绝不会被服务工作线程拦截 | ### 离线页面 当网络不可用且用户导航到新页面时,服务工作线程会提供专用的 `/offline` 页面,该页面: - 显示清晰的**“连接问题”**消息 - 显示实时更新的**在线/离线状态指示器** - 提供**“重试连接”**按钮,以便在连接恢复时重新加载 - 提供指向**状态页面**的链接以进行诊断 ### 应用图标 OmniRoute 提供了针对各个平台优化的图标: | 文件 | 尺寸 | 使用方 | | ---------------------- | --------------- | -------------------------- | | `icon-512.png` | 512×512 | Android 安装提示、启动画面 | | `apple-touch-icon.png` | 180×180 | iOS 主屏幕图标 | | `icon-192.svg` | 192×192(矢量) | Android 自适应图标 | | `apple-touch-icon.svg` | 180×180(矢量) | Apple 回退图标 | | `favicon.svg` | 矢量 | 浏览器标签页 | | `favicon.ico` | 多尺寸 | 旧版浏览器 | ### 自动注册 服务工作线程通过根布局中的 `` 组件自动注册。无需用户执行任何操作——只要浏览器检测到有效的清单和服务工作线程,该应用即可安装。 ## 技术架构 ### Web 应用清单(`manifest.webmanifest`) 由 Next.js 通过 `src/app/manifest.ts` 生成: ```json { "name": "OmniRoute", "short_name": "OmniRoute", "description": "OmniRoute is an AI gateway for multi-provider LLMs. One endpoint for all your AI providers.", "start_url": "/", "scope": "/", "display": "fullscreen", "orientation": "any", "background_color": "#0b0f1a", "theme_color": "#0b0f1a", "icons": [ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }, { "src": "/apple-touch-icon.png", "sizes": "180x180", "type": "image/png" } ] } ``` ### Service Worker(`public/sw.js`) 一个不依赖任何框架的原生 Service Worker,具有以下功能: - **安装阶段**:预缓存应用外壳(根页面、离线页面、清单和图标) - **激活阶段**:清理旧缓存版本并接管所有客户端 - **获取阶段**:根据请求类型(导航、静态资源、API)进行智能路由 - **缓存版本控制**:`omniroute-pwa-v3` — 更新时递增此版本号可强制刷新缓存 ### 布局元数据(`src/app/layout.tsx`) 根布局提供了符合 PWA 要求所需的全部 meta 标签: - 指向 `/manifest.webmanifest` 的 `manifest` 链接 - 用于 iOS 独立运行模式的 `apple-web-app-capable: true` - `apple-web-app-status-bar-style: black-translucent` - 用于 Android Chrome 的 `mobile-web-app-capable: yes` - `theme-color: #0b0f1a` - 用于边到边渲染的 `viewport-fit: cover` ### 组件:`PwaRegister` 该客户端组件位于 `src/shared/components/PwaRegister.tsx`,其行为如下: 1. 在挂载时运行(仅限客户端) 2. 检查浏览器是否支持 `serviceWorker` 3. 静默注册 `/sw.js`(错误会被忽略,以避免阻塞应用) 4. 不渲染任何内容(`return null`)— 这是一个仅执行副作用的组件 ## 与 Termux(Android)配合使用 通过 Termux 在 Android 上运行 OmniRoute 时,PWA 可无缝工作: 1. 在 Termux 中启动 OmniRoute:`npx omniroute` 2. 在同一部手机上打开 Chrome:`http://localhost:20128` 3. 通过“添加到主屏幕”安装 PWA 4. PWA 会连接到本地 Termux 服务器 — 所有内容均在设备上运行 这种组合意味着你的 Android 手机既是**服务器**(Termux),也是**客户端**(PWA)— 从而构成一个完整的、自包含的 AI 网关。 ## 从其他设备使用 可在任何能够通过浏览器访问 OmniRoute 服务器的设备上安装 PWA: - **另一部手机/平板电脑**:访问 `http://PHONE_IP:20128` 并安装 PWA - **笔记本电脑**:使用 Chrome/Edge 打开,并将其安装为桌面 PWA - **带浏览器的智能电视**:以全屏模式访问仪表板 ## 自定义 ### 实例名称 PWA 标题会采用 `Dashboard → Settings` 中的**实例名称**设置。如果将实例重命名为“My AI Gateway”,已安装的 PWA 将显示该名称。 ### 自定义网站图标 如果通过 `Dashboard → Settings` 上传自定义网站图标,桌面端的 PWA 图标将使用该自定义图标。移动设备主屏幕图标则使用预先生成的 `icon-512.png` 和 `apple-touch-icon.png` 文件。 ## 限制 - **不支持推送通知** — Service Worker 未实现 Push API。通知改由 Electron 应用处理。 - **不支持后台同步** — 离线操作不会排队等待重放。PWA 主要用作仪表板查看器。 - **iOS 限制** — iOS 上的 Safari 不支持所有 PWA 功能(例如,需要手动执行安装提示,且后台 Service Worker 功能受限)。 - **缓存大小** — Service Worker 仅缓存静态资源。来自 `/api/` 路由的大型响应负载不会被缓存。 - **移动端自定义图标** — 在设置中更改 favicon 不会更新移动设备上的主屏幕图标(这需要重新生成 PWA 图标)。 ## 文件参考 | 文件 | 用途 | | --------------------------------------- | -------------------------------------------------------- | | `src/app/manifest.ts` | Next.js 清单路由(生成 `manifest.webmanifest`) | | `public/sw.js` | 包含缓存逻辑的 Service Worker | | `src/shared/components/PwaRegister.tsx` | 用于注册 Service Worker 的客户端组件 | | `src/app/offline/page.tsx` | 带有实时状态指示器的离线回退页面 | | `src/app/layout.tsx` | 包含 PWA 元数据(apple-web-app、theme-color 等)的根布局 | | `public/icon-512.png` | 512×512 PNG 图标(Android、启动画面) | | `public/apple-touch-icon.png` | 180×180 PNG 图标(iOS 主屏幕) | | `public/icon-192.svg` | 192×192 SVG 图标(Android 自适应图标) | | `public/apple-touch-icon.svg` | 180×180 SVG 图标(Apple 回退图标) |