docs: qualify the favicon fallback for older Safari and state manifest facts in present tense

Safari 26 and iOS 26 render SVG favicons, so the PNG fallback serves Safari
versions before 26; both Agent Notes now say so instead of claiming Safari
never renders SVG. The install-manifest Consequences section drops the
change narration and states the current manifest contract in present tense.
This commit is contained in:
_Kerman
2026-08-10 12:56:31 +08:00
parent c4203677c0
commit 7628b9cc4d
6 changed files with 20 additions and 20 deletions
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority; # side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with: # after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-10-web-favicon-dark-mode.md # pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-10-web-favicon-dark-mode.md
2026-08-10-web-favicon-dark-mode.md: 45a23ea31d369a7806733edac0c0aff32784a4c9 2026-08-10-web-favicon-dark-mode.md: 65f41dda72e0fe0251ec43a42a11619b22d79f67
2026-08-10-web-favicon-dark-mode.zh.md: 161e12e9d2749fb7dd0b65382be03cf38dd199d0 2026-08-10-web-favicon-dark-mode.zh.md: b783f48a146a91e16157de696b06fc51b16a0e5f
@@ -6,20 +6,20 @@ English | [中文](2026-08-10-web-favicon-dark-mode.zh.md)
## Problem ## Problem
`apps/web/public/favicon.svg` paints the DeepSeek mark solid black (`fill="#000"`), and `index.html` declares only that single SVG icon. Under an OS or browser dark color scheme the tab strip is dark too, so the black mark is effectively invisible. Safari additionally ignores SVG favicons entirely, so its users get no tab icon in any scheme. `apps/web/public/favicon.svg` paints the DeepSeek mark solid black (`fill="#000"`), and `index.html` declares only that single SVG icon. Under an OS or browser dark color scheme the tab strip is dark too, so the black mark is effectively invisible. Safari versions before 26 do not render SVG favicons, so their users get no tab icon in any scheme.
## Decision ## Decision
The favicon stays one file and adapts through the browser's own color-scheme signal: `favicon.svg` embeds `@media (prefers-color-scheme: dark) { path { fill: #fff } }`, switching the mark to white under a dark scheme while the light scheme keeps black. `index.html` and `manifest.webmanifest` also declare a 32×32 PNG fallback (`favicon-32x32.png`, DeepSeek brand blue `#4D6BFE`) that Safari renders and that stays visible on both light and dark tab strips, extending the [web-install-manifest decision](../feature/2026-08-06-web-install-manifest.md). The favicon stays one file and adapts through the browser's own color-scheme signal: `favicon.svg` embeds `@media (prefers-color-scheme: dark) { path { fill: #fff } }`, switching the mark to white under a dark scheme while the light scheme keeps black. `index.html` and `manifest.webmanifest` also declare a 32×32 PNG fallback (`favicon-32x32.png`, DeepSeek brand blue `#4D6BFE`) that Safari versions before 26 render and that stays visible on both light and dark tab strips, extending the [web-install-manifest decision](../feature/2026-08-06-web-install-manifest.md).
The theme signal is the OS/browser scheme, not the GUI's in-app `dsh.theme` toggle: the favicon lives in browser chrome, whose background follows the browser scheme, so `prefers-color-scheme` is the correct semantic and needs no JavaScript. Known browser quirks — Chromium may not repaint the tab icon until reload after a scheme switch, and Safari ignores the SVG variant — are accepted and the PNG fallback covers the Safari case. The theme signal is the OS/browser scheme, not the GUI's in-app `dsh.theme` toggle: the favicon lives in browser chrome, whose background follows the browser scheme, so `prefers-color-scheme` is the correct semantic and needs no JavaScript. Known browser quirks — Chromium may not repaint the tab icon until reload after a scheme switch, and Safari versions before 26 ignore the SVG variant — are accepted and the PNG fallback covers the older-Safari case.
## Alternatives considered ## Alternatives considered
- **A second `<link rel="icon" media="(prefers-color-scheme: dark)">` pointing at a separate dark SVG.** Rejected: the same scheme semantics with two files to keep in sync, and no benefit over the in-file media query. - **A second `<link rel="icon" media="(prefers-color-scheme: dark)">` pointing at a separate dark SVG.** Rejected: the same scheme semantics with two files to keep in sync, and no benefit over the in-file media query.
- **A theme-presenter that swaps the icon href on `theme/change`.** Rejected: it would follow the in-app toggle rather than the browser scheme that actually colors the tab strip, and it adds client code and a presenter for a chrome asset. - **A theme-presenter that swaps the icon href on `theme/change`.** Rejected: it would follow the in-app toggle rather than the browser scheme that actually colors the tab strip, and it adds client code and a presenter for a chrome asset.
- **No PNG fallback.** Rejected: Safari never renders SVG favicons, so the fallback is the only way it gets a tab icon at all. - **No PNG fallback.** Rejected: Safari versions before 26 never render SVG favicons, so the fallback is the only way those versions get a tab icon at all.
## Consequences ## Consequences
Light scheme still shows the black mark, dark scheme shows white, and Safari shows the blue PNG in both. `apps/web/tests/pwa-manifest.e2e.ts` now pins the PNG link, both manifest icons, and the dark media query inside the shipped SVG. The Chromium repaint quirk remains a browser behavior the app cannot fix. Light scheme still shows the black mark, dark scheme shows white, and Safari versions before 26 show the blue PNG in both. `apps/web/tests/pwa-manifest.e2e.ts` pins the PNG link and its order before the SVG, both manifest icons, the shipped PNG's format and dimensions, and the dark media query inside the shipped SVG. The Chromium repaint quirk remains a browser behavior the app cannot fix.
@@ -6,20 +6,20 @@ Status: implemented
## 问题 ## 问题
`apps/web/public/favicon.svg` 把 DeepSeek 图标绘制为纯黑色(`fill="#000"`),而 `index.html` 只声明了这一个 SVG 图标。当操作系统或浏览器处于暗色配色方案时,标签栏同样是深色,黑色图标实际上不可见。此外 Safari 完全不渲染 SVG favicon,因此无论何种配色方案,Safari 用户都看不到标签页图标。 `apps/web/public/favicon.svg` 把 DeepSeek 图标绘制为纯黑色(`fill="#000"`),而 `index.html` 只声明了这一个 SVG 图标。当操作系统或浏览器处于暗色配色方案时,标签栏同样是深色,黑色图标实际上不可见。Safari 26 之前的版本不渲染 SVG favicon,因此这些版本的 Safari 用户无论何种配色方案都看不到标签页图标。
## 决策 ## 决策
favicon 保持单一文件,并通过浏览器自身的配色方案信号自适应:`favicon.svg` 内嵌 `@media (prefers-color-scheme: dark) { path { fill: #fff } }`,在暗色方案下把图标切换为白色,浅色方案保持黑色。`index.html``manifest.webmanifest` 同时声明 32×32 PNG 兜底(`favicon-32x32.png`DeepSeek 品牌蓝 `#4D6BFE`),Safari 会渲染该 PNG,且它在浅色与深色标签栏上都清晰可见;这是对 [Web 安装 manifest 决策](../feature/2026-08-06-web-install-manifest.md) 的扩展。 favicon 保持单一文件,并通过浏览器自身的配色方案信号自适应:`favicon.svg` 内嵌 `@media (prefers-color-scheme: dark) { path { fill: #fff } }`,在暗色方案下把图标切换为白色,浅色方案保持黑色。`index.html``manifest.webmanifest` 同时声明 32×32 PNG 兜底(`favicon-32x32.png`DeepSeek 品牌蓝 `#4D6BFE`),Safari 26 之前的版本会渲染该 PNG,且它在浅色与深色标签栏上都清晰可见;这是对 [Web 安装 manifest 决策](../feature/2026-08-06-web-install-manifest.md) 的扩展。
主题信号取操作系统/浏览器方案,而不是 GUI 应用内 `dsh.theme` 开关:favicon 位于浏览器 chrome 中,其背景跟随浏览器方案,因此 `prefers-color-scheme` 是正确语义,无需任何 JavaScript。已知的浏览器怪癖——Chromium 在切换方案后可能要到刷新页面才重绘标签图标,Safari 忽略 SVG 变体——均被接受,Safari 场景由 PNG 兜底覆盖。 主题信号取操作系统/浏览器方案,而不是 GUI 应用内 `dsh.theme` 开关:favicon 位于浏览器 chrome 中,其背景跟随浏览器方案,因此 `prefers-color-scheme` 是正确语义,无需任何 JavaScript。已知的浏览器怪癖——Chromium 在切换方案后可能要到刷新页面才重绘标签图标,Safari 26 之前的版本忽略 SVG 变体——均被接受,旧版 Safari 场景由 PNG 兜底覆盖。
## 曾考虑的替代方案 ## 曾考虑的替代方案
- **新增指向独立暗色 SVG 的第二个 `<link rel="icon" media="(prefers-color-scheme: dark)">`。** 不予采纳:语义相同却要多维护一个文件,相比文件内媒体查询没有任何收益。 - **新增指向独立暗色 SVG 的第二个 `<link rel="icon" media="(prefers-color-scheme: dark)">`。** 不予采纳:语义相同却要多维护一个文件,相比文件内媒体查询没有任何收益。
- **由主题 presenter 在 `theme/change` 时替换图标 href。** 不予采纳:它会跟随应用内开关,而不是真正决定标签栏颜色的浏览器方案,并且为一个 chrome 资源引入客户端代码和 presenter。 - **由主题 presenter 在 `theme/change` 时替换图标 href。** 不予采纳:它会跟随应用内开关,而不是真正决定标签栏颜色的浏览器方案,并且为一个 chrome 资源引入客户端代码和 presenter。
- **不提供 PNG 兜底。** 不予采纳:Safari 从不渲染 SVG favicon,兜底是获得标签图标的唯一途径。 - **不提供 PNG 兜底。** 不予采纳:Safari 26 之前的版本从不渲染 SVG favicon,兜底是这些版本获得标签图标的唯一途径。
## 后果 ## 后果
浅色方案仍显示黑色图标,暗色方案显示白色,Safari 两种方案都显示蓝色 PNG。`apps/web/tests/pwa-manifest.e2e.ts` 现在固定断言 PNG 链接、manifest 中的两个图标以及交付 SVG 内部的暗色媒体查询。Chromium 的重绘怪癖仍是浏览器行为,应用无法修复。 浅色方案仍显示黑色图标,暗色方案显示白色,Safari 26 之前的版本两种方案都显示蓝色 PNG。`apps/web/tests/pwa-manifest.e2e.ts` 固定断言 PNG 链接及其位于 SVG 之前的顺序、manifest 中的两个图标、交付 PNG 的格式与尺寸,以及交付 SVG 内部的暗色媒体查询。Chromium 的重绘怪癖仍是浏览器行为,应用无法修复。
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority; # side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with: # after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-06-web-install-manifest.md # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-06-web-install-manifest.md
2026-08-06-web-install-manifest.md: 1b3b852fe0d9fbe5643f5ec5d84a8a120b730e34 2026-08-06-web-install-manifest.md: 87c8cd5442c616f078153599f42b03e8fb933ec4
2026-08-06-web-install-manifest.zh.md: 9d16b6556888eaebb0e4d501774e2ee8343b25ed 2026-08-06-web-install-manifest.zh.md: 8405cf5e82220254001eb703811497469efa4d9b
@@ -10,7 +10,7 @@ The Web build has a document title and favicon but no manifest from which a brow
## Decision ## Decision
The Web entry links `/manifest.webmanifest`, which Vite copies from `apps/web/public/` into the production build. The manifest names the product `DeepSeek Harness`, gives installed chrome the compact name `DSH`, and fixes `id`, `start_url`, and `scope` at `/`. It requests `display: "fullscreen"` so supporting browsers can give the installed editor-like surface the available display area while leaving ordinary tabs unchanged; browsers may apply user overrides or fall back to another display mode. Its icon entries reuse `/favicon.svg` as an SVG of size `any` and purpose `any` and add a 32×32 PNG fallback (`/favicon-32x32.png`) for browsers that cannot render SVG favicons, per the [web-favicon-dark-mode decision](../bug-fix/2026-08-10-web-favicon-dark-mode.md). The Web entry links `/manifest.webmanifest`, which Vite copies from `apps/web/public/` into the production build. The manifest names the product `DeepSeek Harness`, gives installed chrome the compact name `DSH`, and fixes `id`, `start_url`, and `scope` at `/`. It requests `display: "fullscreen"` so supporting browsers can give the installed editor-like surface the available display area while leaving ordinary tabs unchanged; browsers may apply user overrides or fall back to another display mode. Its icon entries reuse `/favicon.svg` as an SVG of size `any` and purpose `any` and add a 32×32 PNG fallback (`/favicon-32x32.png`) for Safari versions before 26, which cannot render SVG favicons, per the [web-favicon-dark-mode decision](../bug-fix/2026-08-10-web-favicon-dark-mode.md).
This follows code-server's fullscreen choice without copying its `window-controls-overlay` display override. DSH has no custom title bar or layout around native window controls, so such an override would supersede fullscreen without owning the required safe layout. This follows code-server's fullscreen choice without copying its `window-controls-overlay` display override. DSH has no custom title bar or layout around native window controls, so such an override would supersede fullscreen without owning the required safe layout.
@@ -30,10 +30,10 @@ The built-Web test parses the emitted manifest and pins the complete metadata ob
**Choose one static background and theme color.** Rejected because the app resolves light and dark palettes at runtime, so either fixed value is knowingly wrong for one supported state. **Choose one static background and theme color.** Rejected because the app resolves light and dark palettes at runtime, so either fixed value is knowingly wrong for one supported state.
**Ship raster and maskable icon variants immediately.** The raster half shipped once Safari demonstrated a requirement the scalable favicon cannot meet: it cannot render SVG favicons, so the manifest now carries a 32×32 PNG alongside the SVG ([web-favicon-dark-mode decision](../bug-fix/2026-08-10-web-favicon-dark-mode.md)). Maskable variants remain rejected until a supported installation target demonstrates a requirement. **Ship raster and maskable icon variants immediately.** The raster half shipped once Safari demonstrated a requirement the scalable favicon cannot meet: Safari versions before 26 cannot render SVG favicons, so the manifest now carries a 32×32 PNG alongside the SVG ([web-favicon-dark-mode decision](../bug-fix/2026-08-10-web-favicon-dark-mode.md)). Maskable variants remain rejected until a supported installation target demonstrates a requirement.
**Assert only root and display fields in the built artifact.** Rejected because dropping or changing the product name, compact name, or icon is also a shipped install regression. The test intentionally requires an explicit edit whenever any manifest metadata changes. **Assert only root and display fields in the built artifact.** Rejected because dropping or changing the product name, compact name, or icon is also a shipped install regression. The test intentionally requires an explicit edit whenever any manifest metadata changes.
## Consequences ## Consequences
Supporting browsers can discover a stable root-scoped installed identity and fullscreen preference without the application promising offline behavior. Deploying this build below a path prefix requires revisiting the absolute link, identity, launch, scope, and icon URLs together. Browser-specific icon requirements added a 32×32 PNG fallback once Safari could not render SVG favicons ([web-favicon-dark-mode decision](../bug-fix/2026-08-10-web-favicon-dark-mode.md)), and every intentional metadata change updates the exact built-artifact contract. Supporting browsers can discover a stable root-scoped installed identity and fullscreen preference without the application promising offline behavior. Deploying this build below a path prefix requires revisiting the absolute link, identity, launch, scope, and icon URLs together. The manifest carries a 32×32 PNG fallback for Safari versions before 26, which cannot render SVG favicons, alongside the SVG ([web-favicon-dark-mode decision](../bug-fix/2026-08-10-web-favicon-dark-mode.md)); every intentional metadata change updates the exact built-artifact contract.
@@ -10,7 +10,7 @@ Web 构建产物已有文档标题和 favicon,却没有可供浏览器发现
## 决策 ## 决策
Web 入口链接 `/manifest.webmanifest`Vite 会将其从 `apps/web/public/` 复制到生产构建产物。manifest 将产品命名为 `DeepSeek Harness`,为安装后的浏览器界面提供简称 `DSH`,并把 `id``start_url``scope` 固定为 `/`。它请求 `display: "fullscreen"`,使支持这一模式的浏览器能够把可用显示区域交给安装后的编辑器式界面,同时不改变普通标签页;浏览器可以应用用户覆盖设置,或回退到其他显示模式。其图标条目复用 `/favicon.svg`,将它作为尺寸为 `any`、用途为 `any` 的 SVG,并额外提供 32×32 的 PNG 兜底(`/favicon-32x32.png`),供无法渲染 SVG favicon 的浏览器使用,详见[网页图标随配色方案决策](../bug-fix/2026-08-10-web-favicon-dark-mode.md)。 Web 入口链接 `/manifest.webmanifest`Vite 会将其从 `apps/web/public/` 复制到生产构建产物。manifest 将产品命名为 `DeepSeek Harness`,为安装后的浏览器界面提供简称 `DSH`,并把 `id``start_url``scope` 固定为 `/`。它请求 `display: "fullscreen"`,使支持这一模式的浏览器能够把可用显示区域交给安装后的编辑器式界面,同时不改变普通标签页;浏览器可以应用用户覆盖设置,或回退到其他显示模式。其图标条目复用 `/favicon.svg`,将它作为尺寸为 `any`、用途为 `any` 的 SVG,并额外提供 32×32 的 PNG 兜底(`/favicon-32x32.png`),供无法渲染 SVG favicon 的 Safari 26 之前版本使用,详见[网页图标随配色方案决策](../bug-fix/2026-08-10-web-favicon-dark-mode.md)。
这一选择沿用了 code-server 的全屏方案,但没有照搬其 `window-controls-overlay` 显示覆盖项。DSH 没有自定义标题栏,也没有围绕原生窗口控件安排布局,因此使用这类覆盖项会在未落实所需安全布局的情况下取代全屏模式。 这一选择沿用了 code-server 的全屏方案,但没有照搬其 `window-controls-overlay` 显示覆盖项。DSH 没有自定义标题栏,也没有围绕原生窗口控件安排布局,因此使用这类覆盖项会在未落实所需安全布局的情况下取代全屏模式。
@@ -30,10 +30,10 @@ Web 构建产物测试解析输出的 manifest,并固定完整的元数据对
**选择一组静态背景色和主题色。** 不予采纳,因为应用会在运行时解析浅色和深色调色板,因此选择任一固定值,都是明知它与其中一种受支持状态不符。 **选择一组静态背景色和主题色。** 不予采纳,因为应用会在运行时解析浅色和深色调色板,因此选择任一固定值,都是明知它与其中一种受支持状态不符。
**立即交付光栅和可遮罩图标变体。** 光栅部分已交付:Safari 证明了现有可缩放 favicon 无法满足其要求——无法渲染 SVG favicon,因此 manifest 现在在 SVG 之外同时携带 32×32 PNG(见[网页图标随配色方案决策](../bug-fix/2026-08-10-web-favicon-dark-mode.md))。可遮罩变体仍保持不予采纳,直到有受支持的安装目标证明其必要性。 **立即交付光栅和可遮罩图标变体。** 光栅部分已交付:Safari 证明了现有可缩放 favicon 无法满足其要求——Safari 26 之前的版本无法渲染 SVG favicon,因此 manifest 现在在 SVG 之外同时携带 32×32 PNG(见[网页图标随配色方案决策](../bug-fix/2026-08-10-web-favicon-dark-mode.md))。可遮罩变体仍保持不予采纳,直到有受支持的安装目标证明其必要性。
**只断言构建产物中的根路径字段和显示字段。** 不予采纳,因为产品名称、简称或图标被删除或更改,同样属于已交付安装体验的回归。任何 manifest 元数据发生变化时,测试都有意要求显式改动。 **只断言构建产物中的根路径字段和显示字段。** 不予采纳,因为产品名称、简称或图标被删除或更改,同样属于已交付安装体验的回归。任何 manifest 元数据发生变化时,测试都有意要求显式改动。
## 后果 ## 后果
支持这一机制的浏览器可以发现以根路径为作用域的稳定安装身份和全屏偏好,而应用无需承诺离线行为。在路径前缀下部署该构建产物时,必须同时重新审视绝对路径的 manifest 链接,以及身份、启动、作用域和图标 URL。浏览器特有的图标要求已在 Safari 无法渲染 SVG favicon 时新增了 32×32 PNG 兜底(见[网页图标随配色方案决策](../bug-fix/2026-08-10-web-favicon-dark-mode.md));每一项有意的元数据变更都会同步更新精确的构建产物约定。 支持这一机制的浏览器可以发现以根路径为作用域的稳定安装身份和全屏偏好,而应用无需承诺离线行为。在路径前缀下部署该构建产物时,必须同时重新审视绝对路径的 manifest 链接,以及身份、启动、作用域和图标 URL。manifest 为无法渲染 SVG favicon 的 Safari 26 之前版本携带 32×32 PNG 兜底(见[网页图标随配色方案决策](../bug-fix/2026-08-10-web-favicon-dark-mode.md));每一项有意的元数据变更都会同步更新精确的构建产物约定。