fix(web): 首屏 favicon - 不再先闪内置品牌图标 (#570)

Original-Commit: d049f7014d49e93ab0e6aa8b7e715d5155b94dc0
fix(web): 首屏 favicon - 不再先闪内置品牌图标

index.html 静态声明 /logo.svg 作 icon,而 favicon 由 use-appearance-store 在解析出外观后写入
(link[rel~="icon"] + appearanceLogoURL)。自定义品牌部署于是会先闪一下内置 logo。

换成空占位 <link rel="icon" href="data:," />:外观系统照旧覆盖它,占位只是为了不让浏览器去
请求 /favicon.ico(那是一条 404)。代价是外观接口不可用时标签页没有图标,这是有意的取舍。

新增源码级守卫用例(上游原态 1 pass/1 fail,改后 2 pass)。
This commit is contained in:
AetherNo2332
2026-09-21 07:59:59 +08:00
committed by ddCat
parent e95903fc24
commit 9dd8c954d0
2 changed files with 25 additions and 1 deletions

View File

@@ -4,7 +4,9 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="AI 影视与短剧创作工作台" />
<link rel="icon" href="/logo.svg" type="image/svg+xml" />
<!-- 不写内置品牌资源:首屏必须品牌中性,favicon 由 use-appearance-store 解析出外观后写入。
空 data URL 只是为了不让浏览器去请求 /favicon.ico(那会是一条 404)。 -->
<link rel="icon" href="data:," />
<title>正在加载</title>
<style>
/* Fix layer order before production chunks can register utilities ahead of base. */

View File

@@ -0,0 +1,22 @@
import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
const readProjectFile = (relative: string) => readFileSync(new URL(`../${relative}`, import.meta.url), "utf8");
// 首屏必须品牌中性:静态的 /logo.svg 会让自定义品牌部署在标签页上先闪一下内置 logo,
// 而 favicon 本来就由 use-appearance-store 在解析出外观后写入。空 data URL 只是占位,
// 避免浏览器去请求 /favicon.ico(那是一条 404)。
describe("app icon bootstrap", () => {
const indexHtml = readProjectFile("index.html");
const appearanceStore = readProjectFile("src/stores/use-appearance-store.ts");
test("index.html declares a neutral placeholder icon instead of the built-in brand", () => {
expect(indexHtml).toContain('<link rel="icon" href="data:," />');
expect(indexHtml).not.toContain('<link rel="icon" href="/logo.svg"');
});
test("the appearance store remains the single writer of the favicon", () => {
expect(appearanceStore).toContain('link[rel~="icon"]');
expect(appearanceStore).toContain("favicon.href = appearanceLogoURL(");
});
});