Skip to content

Commit d7f0126

Browse files
committed
Fix Windows taskbar icon resource refresh
Root cause: changing src-tauri/icons/icon.ico did not always rebuild the native Windows resource, so target/release/cc-switch.exe and the installed exe could keep an old embedded taskbar icon even when icon.ico itself was current. Declare icon and Tauri config files as build.rs rerun dependencies so Cargo regenerates the native resource when icons change. Regenerate taskbar/app icon assets directly from the CCSwitchMulti brand master instead of maintaining a separate hand-drawn small-icon variant, while keeping DIB-encoded small ICO frames for Windows shell compatibility. Also extend NSIS shortcut repair and install verification to cover an optional pinned taskbar shortcut. Verified with py_compile, full export build after cargo clean, installed exe/source ico associated-icon hash match, shortcut IconLocation checks, Explorer icon cache refresh, and live taskbar screenshot.
1 parent 48584f7 commit d7f0126

14 files changed

Lines changed: 27 additions & 61 deletions

memory.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
- `/models` 获取能力和可路由模型目录是两回事:没有 Base URL/API Key 只代表不能在线刷新模型列表,不代表不能生成路由。已有 `modelCatalog` 的 provider 应显示“已有 modelCatalog,可跳过 /models 在线读取;如需刷新再补 Base URL/API Key”,不要再写“未配置在线获取参数”这种像错误的提示。
88
- 回归测试:`tests/components/CodexMultiRouterWizard.test.tsx` 覆盖 catalog-only provider 不显示旧提示,以及 `OpenAI Official Backup` 带旧 `openai_chat` metadata 时配置页展示 `Responses API` 并提示覆盖旧配置;数据层已有 `inferWizardApiFormat` 测试覆盖最终 route 保存为 `openai_responses`
99

10+
## 2026-06-29 Windows Taskbar Icon Embedded Resource Root Fix
11+
12+
- 用户再次截图反馈任务栏图标仍像白色圆团后,重新沿真实链路排查:`src-tauri/icons/icon.ico` 已更新且小帧仍是 DIB,但 `src-tauri/target/release/cc-switch.exe``%LOCALAPPDATA%\CCSwitchMulti\cc-switch.exe``ExtractAssociatedIcon()` hash 仍是旧值。根因不是安装覆盖失败,而是 Cargo/Tauri 构建复用了旧 `resource.lib``build.rs` 没有声明 `icons/icon.ico`/相关 PNG 为 `rerun-if-changed`
13+
- 修复:`src-tauri/build.rs` 显式声明 `icons/icon.ico``32x32.png``128x128.png``128x128@2x.png``tauri.conf.json` 的构建依赖。修改图标后必须触发 native resource 重新生成,不能只看 `icon.ico` 文件时间。
14+
- 图标生成策略调整:`scripts/generate-windows-icons.py` 不再手绘一套任务栏小图;`16/24/32/48/64` 也从 `assets/brand/ccswitchmulti-codex-app-icon-1024.png` 缩放生成,保持任务栏、安装器、应用内和网站图标同一个品牌 master。ICO 小帧仍写入 32-bit DIB,保留 Windows shell 兼容性。
15+
- 安装链路补强:`src-tauri/nsis/installer-hooks.nsh` 继续修开始菜单/桌面快捷方式,并额外修存在时的任务栏固定项 `CCSwitchMulti.lnk``scripts/verify-windows-install-icon.ps1` 也检查可选任务栏固定项。
16+
- 验证:运行 `python scripts/generate-windows-icons.py``python -m py_compile scripts/generate-windows-icons.py`;执行 `cargo clean --manifest-path src-tauri\Cargo.toml -p cc-switch` 后重新 `scripts\export-latest-ccswitchmulti.ps1 -ReleaseRoot C:\Users\sunda\Documents\LLMservice\ccswitchmulti-iconfix-install`,确认源 `icon.ico``src-tauri\target\release\cc-switch.exe`、raw exe 的 associated icon hash 全部为 `3E1F633E16DBD922D6A7A4538B1450276803745DDCC20C0BE5F8AB3875A9F3E5`;静默安装后 `scripts\verify-windows-install-icon.ps1` 通过,安装目录 exe hash 与源 ico 一致,开始菜单/桌面快捷方式 `IconLocation` 均为 `%LOCALAPPDATA%\CCSwitchMulti\cc-switch.exe,0`。刷新 Explorer 图标缓存并重启安装版后,任务栏截图确认图标为暗色圆角底的新品牌图。
17+
1018
## 2026-06-29 Codex MultiRouter 502 Official Route Diagnosis
1119

1220
- 用户截图里 `OpenAI Multi-Model Router``06/29 16:07` 连续出现 5 条 502,但同一时间段 `codex-router.log` 证明每次都已命中 `route_id=openai-official``effective_provider=codex-openai-router::route::openai-official``effective_endpoint=/responses``upstream_url=https://chatgpt.com/backend-api/codex/responses``responses_to_chat=false``auth_strategy=CodexOAuth`。因此这不是 route miss、不是官方 GPT 被错误转 Chat、也不是模型重名映射错误。

scripts/generate-windows-icons.py

Lines changed: 4 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import struct
88
from pathlib import Path
99

10-
from PIL import Image, ImageDraw, ImageFilter
10+
from PIL import Image
1111

1212

1313
REPO_ROOT = Path(__file__).resolve().parents[1]
@@ -16,68 +16,11 @@
1616
APP_ICON = REPO_ROOT / "src" / "assets" / "icons" / "app-icon.png"
1717

1818

19-
def rounded_rectangle(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int], radius: int, fill) -> None:
20-
"""绘制圆角矩形,统一小尺寸和大尺寸图标的背景形状。"""
21-
draw.rounded_rectangle(box, radius=radius, fill=fill)
22-
23-
24-
def draw_cloud(draw: ImageDraw.ImageDraw, scale: float, offset_x: float, offset_y: float, fill: str) -> None:
25-
"""绘制简化云形主体,小尺寸下只保留高对比轮廓。"""
26-
def p(value: float) -> int:
27-
return int(round(value * scale))
28-
29-
ox = int(round(offset_x))
30-
oy = int(round(offset_y))
31-
circles = [
32-
(15, 31, 25),
33-
(28, 24, 29),
34-
(44, 29, 27),
35-
(23, 43, 26),
36-
(41, 44, 26),
37-
]
38-
for cx, cy, r in circles:
39-
draw.ellipse((ox + p(cx - r), oy + p(cy - r), ox + p(cx + r), oy + p(cy + r)), fill=fill)
40-
draw.rounded_rectangle((ox + p(12), oy + p(30), ox + p(58), oy + p(56)), radius=p(12), fill=fill)
41-
42-
4319
def draw_small_icon(size: int) -> Image.Image:
44-
"""生成专门给 Windows 任务栏使用的小尺寸图标,避免复杂阴影被缓存缩放成糊块。"""
45-
supersample = 4
46-
canvas_size = size * supersample
47-
image = Image.new("RGBA", (canvas_size, canvas_size), (0, 0, 0, 0))
48-
draw = ImageDraw.Draw(image)
49-
s = canvas_size / 64
50-
51-
rounded_rectangle(
52-
draw,
53-
(int(4 * s), int(4 * s), int(60 * s), int(60 * s)),
54-
int(14 * s),
55-
(15, 22, 29, 255),
56-
)
57-
draw_cloud(draw, s * 0.8, 4 * s, 8 * s, "#f4fbff")
58-
59-
stroke = max(2, int(round(5.5 * s)))
60-
dark = (15, 24, 31, 255)
61-
draw.line(
62-
[(int(27 * s), int(27 * s)), (int(34 * s), int(38 * s)), (int(27 * s), int(49 * s))],
63-
fill=dark,
64-
width=stroke,
65-
joint="curve",
66-
)
67-
draw.line(
68-
[(int(39 * s), int(43 * s)), (int(50 * s), int(43 * s))],
69-
fill=dark,
70-
width=max(2, int(round(4.5 * s))),
71-
)
72-
73-
if size >= 32:
74-
route_width = max(2, int(round(4 * s)))
75-
draw.line([(int(48 * s), int(25 * s)), (int(58 * s), int(23 * s))], fill=(247, 216, 106, 255), width=route_width)
76-
draw.line([(int(49 * s), int(34 * s)), (int(60 * s), int(34 * s))], fill=(119, 243, 208, 255), width=route_width)
77-
draw.line([(int(48 * s), int(43 * s)), (int(58 * s), int(45 * s))], fill=(103, 216, 255, 255), width=route_width)
78-
20+
"""从品牌 master 生成任务栏小尺寸图标,保持任务栏、安装器和网站图标一致。"""
21+
source = Image.open(BRAND_ICON).convert("RGBA")
7922
resample = Image.Resampling.LANCZOS if size >= 32 else Image.Resampling.BICUBIC
80-
return image.resize((size, size), resample)
23+
return source.resize((size, size), resample)
8124

8225

8326
def load_large_icon(size: int) -> Image.Image:

scripts/verify-windows-install-icon.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,11 @@ if ($sourceHash -ne $installedHash) {
115115

116116
$startMenuLink = Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs\$AppName.lnk"
117117
$desktopLink = Join-Path $env:USERPROFILE "Desktop\$AppName.lnk"
118+
$taskbarLink = Join-Path $env:APPDATA "Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$AppName.lnk"
118119
$shortcutResults = @(
119120
(Assert-ShortcutIcon -ShortcutPath $startMenuLink -ExpectedTarget $installedExe -Required $true)
120121
(Assert-ShortcutIcon -ShortcutPath $desktopLink -ExpectedTarget $installedExe -Required $false)
122+
(Assert-ShortcutIcon -ShortcutPath $taskbarLink -ExpectedTarget $installedExe -Required $false)
121123
)
122124

123125
[pscustomobject]@{

src-tauri/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
fn main() {
2+
// Windows 任务栏图标最终来自 build script 生成的 native resource。
3+
// Tauri 默认配置文件变化会触发构建,但 Cargo 不一定知道 icons/icon.ico
4+
// 已更新;显式声明依赖,避免复用旧 resource.lib 后 exe 仍嵌旧图标。
5+
println!("cargo:rerun-if-changed=icons/icon.ico");
6+
println!("cargo:rerun-if-changed=icons/32x32.png");
7+
println!("cargo:rerun-if-changed=icons/128x128.png");
8+
println!("cargo:rerun-if-changed=icons/128x128@2x.png");
9+
println!("cargo:rerun-if-changed=tauri.conf.json");
10+
211
tauri_build::build();
312

413
// Windows: Embed Common Controls v6 manifest for test binaries

src-tauri/icons/32x32.png

177 Bytes
Loading

src-tauri/icons/64x64.png

662 Bytes
Loading
160 Bytes
Loading
231 Bytes
Loading
737 Bytes
Loading
766 Bytes
Loading

0 commit comments

Comments
 (0)