Give the app a mark that survives being 16 pixels tall
The icon is now a container with its right wall opened, so the enclosure itself is the letter C, holding a >_ prompt: the two things the app is, in one closed shape. It carries no type, so nothing goes illegible when the shell draws it small, and it uses the app's own accent tokens rather than a saturated orange field that fights the chrome behind it. icon.ico contained a single 16x16 image, which Windows was upscaling into the taskbar and every other slot — the likely cause of the artefact in screenshot_for_fix/. It now carries 16, 24, 32, 48, 64, 128 and 256, each rendered from vector rather than downsampled from one bitmap, and the entries at 32 and below come from a separate optical source: at that size the cursor bar closes up against the chevron, so the small variant drops it, widens the mouth and thickens the strokes. A test asserts the .ico keeps its small sizes so this cannot regress silently. Also adds the icon.icns that macOS bundles have been building without, points the favicon at our own mark instead of the missing /vite.svg, and puts the SVG sources, the lockups and the regeneration script in branding/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Triple-C</title>
|
||||
</head>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128" role="img" aria-label="Triple-C">
|
||||
<title>Triple-C application icon, small-size variant</title>
|
||||
<!-- Source for every raster ≤ 32 px: the small mark, drawn at 82% so the strokes
|
||||
survive being resampled down to 16 px. See build-icons.py. -->
|
||||
<rect width="128" height="128" rx="28.16" fill="#0D1117"/>
|
||||
<g transform="translate(2.9236 2.9236) scale(0.95418)">
|
||||
<g fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M112 50 L112 38 A22 22 0 0 0 90 16 L38 16 A22 22 0 0 0 16 38 L16 90 A22 22 0 0 0 38 112 L90 112 A22 22 0 0 0 112 90 L112 78"
|
||||
stroke="#58A6FF" stroke-width="14"/>
|
||||
<path d="M46 50 L62 66 L46 82" stroke="#F0821E" stroke-width="13"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 812 B |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 918 B After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 16 KiB |
@@ -33,6 +33,7 @@
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.ico",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.png"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -33,4 +33,33 @@ describe("Window icon configuration", () => {
|
||||
expect(config.bundle.icon).toContain("icons/icon.ico");
|
||||
expect(config.bundle.icon).toContain("icons/icon.png");
|
||||
});
|
||||
|
||||
it("icon.ico carries the small sizes Windows draws in the taskbar", () => {
|
||||
// A single-image .ico is the taskbar bug: Windows upscales 16x16 into every
|
||||
// other slot. Regenerate with `python3 branding/build-icons.py`.
|
||||
const ico = readFileSync(resolve(srcTauriDir, "icons/icon.ico"));
|
||||
const count = ico.readUInt16LE(4);
|
||||
expect(count).toBeGreaterThan(1);
|
||||
|
||||
// Directory entries start at byte 6; width/height of 0 means 256.
|
||||
const widths = new Set<number>();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const w = ico[6 + i * 16];
|
||||
widths.add(w === 0 ? 256 : w);
|
||||
}
|
||||
for (const size of [16, 24, 32, 48, 256]) {
|
||||
expect(widths).toContain(size);
|
||||
}
|
||||
});
|
||||
|
||||
it("icon.icns exists and is bundled for macOS", () => {
|
||||
const icnsPath = resolve(srcTauriDir, "icons/icon.icns");
|
||||
expect(existsSync(icnsPath)).toBe(true);
|
||||
expect(readFileSync(icnsPath).subarray(0, 4).toString("ascii")).toBe("icns");
|
||||
|
||||
const config = JSON.parse(
|
||||
readFileSync(resolve(srcTauriDir, "tauri.conf.json"), "utf-8")
|
||||
);
|
||||
expect(config.bundle.icon).toContain("icons/icon.icns");
|
||||
});
|
||||
});
|
||||
|
||||