Skip to content

fix(console): PWA manifest declares the branding icon's real type and size, with 192/512 PNG fallbacks - #29405

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-vmwtew
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-vmwtew

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29332

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Console 运行时 PWA manifest 的图标不再一律写成 image/svg+xml + sizes: 'any'。品牌 favicon 是 SVG 时照旧;是 PNG/ICO 等位图时按真实类型与实测像素尺寸声明;没有 ≥192 px 正方形位图时,补上平台默认的 192/512 PNG(由现有 favicon.svg 渲染,新增两个静态文件)。
  • 为什么改:配了小尺寸 PNG favicon 的品牌 Console 在 Chrome 里不可安装(实测 no-acceptable-icon,最低 144 px),与 Console as an installable PWA on desktop: register the Service Worker for the app shell, and prepare a web_push delivery path #29155 要的「品牌化可安装」相悖。
  • 风险与回滚:风险低,只动 Console 入口的 manifest 生成;manifest 链接改为在位图测量完成后才挂上(不阻塞首屏,最长 10 秒超时)。回滚即 revert 本 PR。取舍:小尺寸品牌 PNG 安装后,启动器图标显示的是平台默认图标而非品牌图标。
  • 先看:ui/apps/console/src/pwaManifestIcons.ts(resolveManifestIcons 的判定);ui/apps/console/src/main.tsx(injectPwaManifest,异步注入)。
  • 两分钟验证:pnpm --dir ui exec vitest run apps/console/src/pwaManifestIcons.test.ts,预期 23 个用例全绿。
  • 需要你做的:无,席位落地。

What changes

  • ui/apps/console/src/pwaManifestIcons.ts (new, console-local): resolveManifestIcons(faviconUrl, baseURI) builds the manifest icon entries.
    • SVG favicon, or no branding (the default favicon.svg): one entry, image/svg+xml, sizes: 'any', as before. Nothing is loaded.
    • Raster favicon (PNG, ICO, WebP, JPEG, GIF, AVIF, by path extension or data: media type): the image is measured through HTMLImageElement and declared with its own type and WIDTHxHEIGHT. A favicon whose type is unknown, or that fails to load, measures zero or does not answer within 10 s, is left out rather than guessed.
    • Whenever the result has no square raster of at least 192 px, icon-192.png and icon-512.png are appended.
  • ui/apps/console/public/icon-192.png, icon-512.png (new): the platform default icons, rendered once with Chromium from the existing public/favicon.svg. No default PNG icon existed anywhere in the repository and no server-rendered resize exists, so these are the card's "platform default icons" option (seat approved before the assets were added).
  • ui/apps/console/src/main.tsx: the manifest block moves into injectPwaManifest, called without await. The static manifest is still removed synchronously, the native-host check is unchanged, and the Blob manifest link lands once the icon measurement settles, so first paint is never held for it. start_url, scope and the absolute icon URLs from feat(console): installable desktop PWA with an offline app shell and a push-ready Service Worker #29326 are unchanged.
  • Changeset: @object-ui/console patch.

Trade-off: a branding with a small raster favicon installs with the platform default icon at launcher sizes, since browsers pick the icon closest to the size they need. Upscaling a 32 px favicon client-side would look worse and needs canvas and CSP work, so the branded icon stays in the manifest at its true size and the defaults cover the large sizes.

Tests

  • pnpm --dir ui exec vitest run apps/console/src/pwaManifestIcons.test.ts: 23/23 passed. Covers the manifest (through generatePWAManifest) for no branding, SVG, small PNG, large square PNG, large non-square PNG, ICO, an unmeasurable raster and an unknown type, plus iconTypeOf and the browser measurement (load, error, zero size, timeout).
  • Ablation (back to the hard-coded image/svg+xml declaration, scripts/ablation-replace.mjs on const type = iconTypeOf(src);): 6 failed / 17 passed, the PNG, ICO, unmeasurable and unknown-type cases red; no branding and SVG stay green, as they should. Restored, blob equal to HEAD, git diff HEAD empty.
  • turbo run type-check --filter=@object-ui/console: 71/71 tasks successful.
  • Derived gates (dispatch-gates --commands): 33/33 exit 0 at 29065950, reconciled with --ran (0 NOT-MEASURED, 0 UNRUN), vitest run scripts/ included (169 files passed). origin/main was merged afterwards (1b6eb74f); none of the merged commits touch these files, and the unit test re-ran green there (23/23).

Acceptance notes

  • The favicon link a few lines above the manifest block (and the same line in index.html) still labels every non-SVG favicon image/png, an ICO included. Out of this card's surface; browsers sniff favicon bytes, so nothing visible follows from it.

Verification

Installability, read with CDP (Page.getInstallabilityErrors, Page.getAppManifest) in full Chromium (launchPersistentContext, channel: 'chromium'), on a vite build of the Console served under /console/ with its base tag and a stubbed GET /api/v1/runtime/config carrying branding.faviconUrl.

branding favicon before (main's manifest block) after (this branch)
32 px PNG no-acceptable-icon (minimum 144 px); icon declared image/svg+xml, any no errors; icons 32x32 PNG + 192x192 + 512x512
256 px PNG no errors; icon declared image/svg+xml, any no errors; one icon 256x256 image/png
SVG no errors; one SVG any no errors; one SVG any
none no errors; default SVG any no errors; default SVG any

Generated by Claude Code

…ing favicon

The runtime manifest declared every icon as an SVG at sizes 'any'. A PNG or
ICO branding favicon now declares its real type and measured size, and a
branding without a square raster of at least 192 px gains the Console's own
192 px and 512 px PNGs (rendered from public/favicon.svg), so a branded
Console stays installable. The raster favicon is measured off the boot path:
the manifest link lands once the measurement settles.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jSqNEXXfJECHQqN7mG
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

席位核对 · ACCEPT · 2026-11-02T14:27Z

Head-sha: 1b6eb74f07c881bd15c3a1d951d95f9a6879ef23

  • Shape: draft → main, Fixes #29332 in the body only. 6 files, +339 / −30 (369 lines ≤ 3000). No governed path: check-governed-merges --pr 29405 reports NOT governed (0 of 6 paths). Clause-②: no and Metadata-protocol: none are line-initial in the body and the changeset, as claimed (6578722036). The changeset is patch for @object-ui/console.
  • Contract review: none owed (Clause-②: no; the new helper is console-local, @object-ui/console is an app, and @object-ui/mobile is untouched).
  • File surface: inside the claim: main.tsx (the manifest block only), the console-local pwaManifestIcons.ts and its test, public/icon-192.png and icon-512.png (the default PNGs the claim allowed, named and approved before they were added), and the changeset. generatePWAManifest is called, not edited.
  • Read in the diff: an SVG favicon (and the default favicon.svg) keeps the single image/svg+xml / any entry and loads nothing. A raster favicon is measured through HTMLImageElement and declared with its own MIME type and WxH. A load error, zero size or 10 s timeout settles null, so the manifest is never held forever. The 192/512 defaults are appended whenever no square raster ≥ 192 px is present. injectPwaManifest is called without await after the static manifest is removed synchronously; start_url, scope, the absolute icon URLs and the native-host check are unchanged.
  • Accepted deviation: a raster favicon whose type cannot be established (no known extension or data media type) is left out rather than guessed; the defaults then cover installability.
  • Tests (dev): pwaManifestIcons.test.ts 23 / 23 at this head. Ablation (back to the hard-coded SVG declaration) reds 6 / 23 on the PNG, ICO, unmeasurable and unknown-type cases; restored, blob equal to HEAD. Console type-check 71 / 71 tasks; derived gates 33 of 33 at 2906595, unit test re-run green after the main merge.
  • Measured (dev): CDP Page.getInstallabilityErrors in full Chromium on a built Console under /console/: a 32 px PNG branding gives no-acceptable-icon on main and no errors on this branch (32 px PNG + 192 + 512); 256 px PNG, SVG and no branding install on both, with correct declarations on the branch.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state is clean.
  • Not filed: the favicon <link> labelling every non-SVG favicon image/png (dev's acceptance note). Browsers sniff favicon bytes, so nothing visible follows; not worth a card.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #29332 closes the card on merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 14:28
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 1d03901 Nov 2, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-vmwtew branch November 2, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Console PWA manifest: declare the branded icon's real type and offer 192/512 PNG icons so a branded Console stays installable

1 participant