mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-31 16:33:34 +08:00
Two reviews of the icon swap, run independently from opposite directions, turned up three places the new mark never reached. The og:image was the worst. `site/index.html:20` points at `/images/banner.png`, and that file does not come from `docs/images/banner.png` — the one this rebrand replaced. `site/scripts/prepare-static-output.mjs:129` copies `docs/public/` wholesale into the site root before the two selective image passes run, and neither of those matches an absolute https:// URL, so `docs/public/images/banner.png` is what shipped. It was a 1200x630 screenshot of the old site: blue panels, old circular CC badge. Every link shared to WeChat or Slack would have previewed the pre-rebrand brand while the site itself rendered the new one. Replaced with a card built from the new lockup; `site/dist/images/banner.png` now hashes to it. `desktop/src-tauri/app-icon.png` is the canonical 1024 RGBA source the platform icon set gets regenerated from. That rule lived only in the body of 3f2ce2a6c, and nothing references the file in code, so the rebrand skipped it — breaking an invariant that had held since the file was introduced, where it and `desktop/public/app-icon.png` were the same git blob. Left as it was, whoever regenerated icons next would have restored the entire old set. Linux had no icon above 310px. electron-builder points `linux.icon` at the whole icons directory and keeps only files named NxN, so `icon.png` (512, no dimensions in the name) and `128x128@2x.png` (256, collides with `128x128.png`) were both dropped, leaving a Windows Store asset as the largest entry. Adding 256x256.png and 512x512.png takes the resolved set from 12 entries topping out at 310 to 14 topping out at 512, confirmed by running app-builder's icon resolver against the directory. index.html also had no favicon, and that document is what the H5 remote client loads in a phone browser. CI could not have caught any of this — `scripts/quality-gate/package-smoke/` asserts nothing about icons. `desktop/icon-assets.test.ts` now pins the source invariant, the Linux sizes, the three packaged icons and the favicon; each assertion was checked to fail when its subject is reverted.
58 lines
2.6 KiB
TypeScript
58 lines
2.6 KiB
TypeScript
import { createHash } from 'node:crypto'
|
|
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
/**
|
|
* Guards on the icon assets, because nothing else does.
|
|
*
|
|
* `scripts/quality-gate/package-smoke/` makes no icon assertions, so a build
|
|
* that ships a stale or half-replaced icon set goes green on every pipeline.
|
|
* The rules below were each learned by breaking them.
|
|
*/
|
|
const desktopRoot = __dirname
|
|
const icons = path.join(desktopRoot, 'src-tauri', 'icons')
|
|
|
|
const sha = (file: string) => createHash('sha256').update(readFileSync(file)).digest('hex')
|
|
|
|
describe('icon assets', () => {
|
|
it('keeps src-tauri/app-icon.png byte-identical to public/app-icon.png', () => {
|
|
// src-tauri/app-icon.png is the canonical 1024 RGBA source the platform
|
|
// icon set gets regenerated from — a directive that lived only in a commit
|
|
// body, which is exactly why a later rebrand replaced everything except
|
|
// this file and left a stale source primed to overwrite the new set.
|
|
const source = path.join(desktopRoot, 'src-tauri', 'app-icon.png')
|
|
const runtime = path.join(desktopRoot, 'public', 'app-icon.png')
|
|
expect(sha(source)).toBe(sha(runtime))
|
|
})
|
|
|
|
it('ships the sizes Linux actually looks for', () => {
|
|
// electron-builder points `linux.icon` at the whole icons/ directory and
|
|
// keeps only files named NxN. `icon.png` (512) has no dimensions in its
|
|
// name and `128x128@2x.png` (256) collides with `128x128.png`, so both are
|
|
// dropped — which once left the largest Linux icon at 310px, from a
|
|
// Windows Store asset, and blurry on HiDPI.
|
|
const present = new Set(readdirSync(icons))
|
|
for (const size of [32, 64, 128, 256, 512]) {
|
|
expect(present.has(`${size}x${size}.png`)).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('carries a packaged icon for every desktop platform', () => {
|
|
// mac.icon / win.icon in package.json name these two directly; a missing
|
|
// file makes electron-builder silently fall back to the Electron logo.
|
|
for (const file of ['icon.icns', 'icon.ico', 'icon.png']) {
|
|
const full = path.join(icons, file)
|
|
expect(existsSync(full)).toBe(true)
|
|
expect(readFileSync(full).byteLength).toBeGreaterThan(1024)
|
|
}
|
|
})
|
|
|
|
it('serves a favicon to the H5 client', () => {
|
|
// The same index.html the Electron window loads is what a phone browser
|
|
// gets over H5 remote access, where a missing icon means a blank tab.
|
|
const html = readFileSync(path.join(desktopRoot, 'index.html'), 'utf8')
|
|
expect(html).toMatch(/<link\s+rel="icon"/)
|
|
})
|
|
})
|