Skip to content

fix(desktop): accept uppercase icon URL schemes - #5470

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/desktop-icon-url-uppercase
Jun 20, 2026
Merged

fix(desktop): accept uppercase icon URL schemes#5470
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/desktop-icon-url-uppercase

Conversation

@tt-a1i

@tt-a1i tt-a1i commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

What this PR does

  • Makes desktop icon URL detection treat http:// and https:// schemes case-insensitively.
  • Reuses the shared icon URL predicate for workspace icon fallback resolution, source/skill/status icon rendering, and source URL opening.
  • Adds regression coverage for uppercase icon URL schemes and workspace icon fallback behavior.

Why it's needed

Desktop icon handling currently checks for lowercase http/https schemes in several places. URLs such as HTTPS://example.com/icon.png are valid, but they can be treated as non-URL icon values and fall back incorrectly. Centralizing the check also keeps the desktop call sites consistent.

Reviewer Test Plan

How to verify

  • Confirm that uppercase HTTP:// and HTTPS:// icon URLs are accepted by the shared predicate and by workspace icon fallback logic.
  • Confirm that non-URL values such as emoji are still handled by the existing fallback paths.
  • Run bun test packages/desktop/packages/shared/src/utils/__tests__/icon-constants.test.ts packages/desktop/packages/shared/src/config/__tests__/workspace-icon-url.test.ts packages/desktop/apps/electron/src/renderer/lib/__tests__/icon-cache.test.ts.
  • Run bun run typecheck:shared.
  • Run npx prettier --check packages/desktop/packages/shared/src/utils/icon-constants.ts packages/desktop/packages/shared/src/utils/__tests__/icon-constants.test.ts packages/desktop/packages/shared/src/config/storage.ts packages/desktop/packages/shared/src/config/__tests__/workspace-icon-url.test.ts packages/desktop/apps/electron/src/renderer/hooks/useWorkspaceIcon.ts packages/desktop/apps/electron/src/renderer/lib/icon-cache.ts packages/desktop/apps/electron/src/renderer/lib/__tests__/icon-cache.test.ts packages/desktop/apps/electron/src/renderer/pages/SourceInfoPage.tsx.
  • Run bun x eslint src/renderer/hooks/useWorkspaceIcon.ts src/renderer/lib/icon-cache.ts src/renderer/lib/__tests__/icon-cache.test.ts src/renderer/pages/SourceInfoPage.tsx from packages/desktop/apps/electron.
  • Run bun x eslint src/utils/icon-constants.ts src/utils/__tests__/icon-constants.test.ts src/config/storage.ts src/config/__tests__/workspace-icon-url.test.ts from packages/desktop/packages/shared.
  • Run git diff --check.

Evidence (Before & After)

Before: uppercase HTTP(S) icon URLs were not consistently recognized by lowercase-only startsWith checks and could fall through to non-URL icon handling. After: the shared predicate uses case-insensitive scheme matching, and the regression tests cover uppercase URL schemes in the shared predicate, workspace icon fallback, and icon cache paths.

Known local validation note: bun run typecheck:electron still fails on pre-existing auto-update.ts and settings-default-thinking.test.ts type errors unrelated to this PR.

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested locally; CI passed
🐧 Linux ⚠️ not tested locally; CI passed

Environment (optional)

Local package tests, typecheck, prettier, eslint, and git diff --check on macOS.

Risk & Scope

  • Main risk or tradeoff: isIconUrl is now shared across more desktop URL-handling paths, so future icon-specific behavior should avoid making the predicate narrower than generic HTTP(S) icon/source URL detection needs.
  • Not validated / out of scope: manually opening the desktop UI on Windows/Linux; those platforms are covered by CI only.
  • Breaking changes / migration notes: none.

Linked Issues

Fixes #5469

中文说明

What this PR does

  • 让 desktop 的图标 URL 判断对 http://https:// 协议大小写不敏感。
  • 在 workspace 图标 fallback、source/skill/status 图标渲染、source URL 打开逻辑里复用同一个共享判断函数。
  • 增加大写图标 URL 协议和 workspace 图标 fallback 行为的回归测试。

Why it's needed

desktop 里有多处只检查小写 http/https 协议。HTTPS://example.com/icon.png 这类 URL 是合法的,但之前可能被当成非 URL 图标值处理并错误 fallback。把判断集中到共享函数里,也能让 desktop 相关调用点保持一致。

Reviewer Test Plan

How to verify

  • 确认大写 HTTP://HTTPS:// 图标 URL 会被共享判断函数和 workspace 图标 fallback 逻辑接受。
  • 确认 emoji 等非 URL 值仍然走既有 fallback 路径。
  • 运行 bun test packages/desktop/packages/shared/src/utils/__tests__/icon-constants.test.ts packages/desktop/packages/shared/src/config/__tests__/workspace-icon-url.test.ts packages/desktop/apps/electron/src/renderer/lib/__tests__/icon-cache.test.ts
  • 运行 bun run typecheck:shared
  • 运行上面英文部分列出的 prettier、eslint 和 git diff --check 命令。

Evidence (Before & After)

修复前:大写 HTTP(S) 图标 URL 不能被小写限定的 startsWith 检查稳定识别,可能落到非 URL 图标处理逻辑。修复后:共享判断函数使用大小写不敏感的协议匹配,回归测试覆盖了共享判断函数、workspace 图标 fallback 和 icon cache 路径里的大写 URL 协议。

本地验证说明:bun run typecheck:electron 仍会因为既有的 auto-update.tssettings-default-thinking.test.ts 类型错误失败,和这个 PR 无关。

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested locally; CI passed
🐧 Linux ⚠️ not tested locally; CI passed

Environment (optional)

在 macOS 上运行了本地 package tests、typecheck、prettier、eslint 和 git diff --check

Risk & Scope

  • 主要风险或取舍:isIconUrl 现在被更多 desktop URL 处理路径复用,后续如果增加图标专属校验,需要避免把它收窄到影响通用 HTTP(S) 图标/source URL 判断。
  • 未验证 / 不在范围内:没有在 Windows/Linux 上手动打开 desktop UI;这些平台仅由 CI 覆盖。
  • Breaking changes / migration notes:无。

Linked Issues

Fixes #5469

AI Assistance Disclosure

I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

Review Summary

Scope: 8 files, +177/-31 — case-insensitive icon URL scheme detection for the desktop app.

What works well:

  • The regex /^https?:\/\//i correctly handles case-insensitive http/https matching
  • All 9 inline startsWith checks across 5 files are properly replaced with the centralized isIconUrl() predicate
  • The useRef removal in useWorkspaceIcon is a correct cleanup (it was dead code)
  • All 21 tests pass (icon-constants, workspace-icon-url, icon-cache)
  • Defense in depth: openUrl in SourceInfoPage still passes through isSafeExternalUrl() downstream
  • No security issues detected — no SSRF, no path traversal, no credential exposure

Minor observations (not blockers):

  • isIconUrl() in SourceInfoPage.tsx:322 handleOpenUrl is used on a general source URL (not an icon URL). Functionally correct, but the name may surprise future readers if isIconUrl ever gains icon-specific validation.
  • isIconUrl(str: string) has a stricter signature than the sibling isEmoji(str: string | undefined). All call sites guard correctly, but consistency with the established pattern would be more defensive.

CI: 16/16 checks passing.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@tt-a1i
tt-a1i marked this pull request as ready for review June 20, 2026 11:57
@wenshao

wenshao commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, @tt-a1i! The fix itself looks well-targeted, but the PR body doesn't follow our PR template, which blocks proper review.

A few things to fix:

  • Headings — the template expects ## What this PR does and ## Reviewer Test Plan, but the PR uses ## Summary and ## Test plan. Please use the template headings so reviewers can find things quickly.
  • Missing sections## Why it's needed, ## Risk & Scope, ### Tested on (OS matrix), and ### Evidence (Before & After) are all absent. Even short answers are fine — they help reviewers assess impact and risk.
  • 中文说明 — the template has a <details>中文说明</details> block for a bilingual translation. Please include it.
  • Linked IssuesFixes #5469 is buried in the Summary; it should be in a ## Linked Issues section so it auto-closes properly.

The actual change (case-insensitive isIconUrl + reusing the shared predicate) is small and well-scoped. Once the template is filled in, this should move through review quickly.

中文说明

感谢 PR,@tt-a1i!修复本身方向正确,但 PR 描述没有按照 PR 模板 填写,会影响审查进度。

需要补充的内容:

  • 标题名称 — 模板要求 ## What this PR does## Reviewer Test Plan,当前用的是 ## Summary## Test plan,请改为模板中的标题。
  • 缺少的章节## Why it's needed## Risk & Scope### Tested on(操作系统矩阵)、### Evidence (Before & After) 均未填写,即使简短回答也有助于审查。
  • 中文说明 — 模板中有 <details>中文说明</details> 区块,请补充。
  • 关联 IssueFixes #5469 放在了 Summary 里面,应放在 ## Linked Issues 区块以便自动关闭。

实际改动(isIconUrl 大小写不敏感 + 复用共享谓函数)小而精准,模板补齐后审查应该很快。

Qwen Code · qwen3.7-max

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

The case-insensitive regex /^https?:\/\//i is correct, all call sites are consistently updated, and tests cover the new behavior at unit, integration, and cache levels.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@tt-a1i

tt-a1i commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

updated the PR description to match the template. thanks.

@wenshao

wenshao commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

✅ Local verification — safe to merge (one non-blocking test note)

I verified this PR locally before merging. Tested PR head d3fe0087 in an isolated git worktree with a fresh bun install (bun 1.3.14, Node v22.22.2), with every step driven under tmux.

Results — all green

Check Command Result
Unit tests (3 files) bun test icon-constants / workspace-icon-url / icon-cache 21 passed
Types (shared) bun run typecheck:shared ✅ pass
Lint (electron) bun x eslint (4 files) ✅ pass
Lint (shared) bun x eslint (4 files) ✅ pass (0 errors; 2 pre-existing storage.ts warnings)
Format prettier --check (8 files) ✅ pass
Whitespace git diff --check ✅ pass

The change is a clean, consistent refactor: isIconUrl becomes case-insensitive (/^https?:\/\//i) and 9 inline startsWith('http://')||startsWith('https://') call sites across 4 files switch to the shared predicate. The useWorkspaceIcon extraction (const workspaceIconUrl = workspace?.iconUrl) is behavior-preserving (adds optional chaining).

Before / after — load-bearing for the predicate and the icon-cache paths

Reverting the 5 source files to pre-fix while keeping the new tests, 3 of the new tests fail (and pass after):

(fail) icon URL detection > treats http and https schemes as case-insensitive
(fail) remote icon URLs > returns source icon URLs with uppercase schemes directly
(fail) remote icon URLs > returns skill icon URLs with uppercase schemes directly

That is the bug: pre-fix, 'HTTPS://…'.startsWith('https://') is false, so uppercase remote icons were rejected. ✔️

⚠️ Non-blocking finding: workspace-icon-url.test.ts is not load-bearing

That test ("preserves uppercase remote icon URL schemes instead of falling back to local icons") passes against both pre-fix and post-fix storage.ts — I confirmed it still passes after reverting the storage.ts change, so it would not catch a regression there.

Root cause: the fixture writes the local icon.svg into the workspace's declared rootPath (<configDir>/workspace), but getWorkspaces() — when config-defaults.json is present — re-resolves the effective rootPath to <CONFIG_DIR>/workspaces/<slug>:

effectiveRootPath=/tmp/…/workspaces/a
icon exists at effRoot/icon.svg=false   ← fixture wrote it to …/workspace, not …/workspaces/a

So findWorkspaceIcon() finds nothing, the local-icon fallback the test means to defeat never runs, and the assertion (URL unchanged) holds whether or not the scheme check is case-sensitive.

The underlying storage.ts change is still correct — it reuses the same isIconUrl predicate, which is directly unit-tested (load-bearing). Only this one integration test doesn't exercise it. Suggestion (non-blocking): strengthen the fixture so the local-icon fallback actually fires (place icon.svg where getWorkspaces() resolves the workspace, or assert through that resolved path) — then the test would fail without the fix.

Extended behavioral check (mine, 19 cases, all pass)

Through the real isIconUrl (no mocks): mixed case (HtTpS://), uppercase host/path/query, and scheme-only are accepted; file://, FILE://, ftp://, data:, httpsx://, single-slash https:/, leading-whitespace, and http: are rejected (fails closed). The predicate is anchored (^) and does not trim — consistent with the previous startsWith behavior.

Note on typecheck:electron

It fails (exit 2) — but only on 4 files unrelated to this PR (auto-update.ts, settings-default-thinking.test.ts, session-delete-navigation.test.ts, skills-loading.test.ts); the PR's own 3 changed electron files are type-clean. These are pre-existing, matching the author's note.

Verdict

The fix is correct, minimal, and consistent, and the core predicate is well-covered. Safe to merge. Consider tightening workspace-icon-url.test.ts (non-blocking) so the storage.ts path is actually guarded. LGTM 👍

🇨🇳 中文版(点击展开)

✅ 本地验证结论 —— 可以合并(含一处不阻塞的测试说明)

合并前我在本地做了验证。在一个独立的 git worktree 中、基于 PR 头 d3fe0087,使用全新的 bun install(bun 1.3.14 / Node v22.22.2),并在 tmux 中运行每一步。

验证结果 —— 全部通过

检查项 命令 结果
单元测试(3 个文件) bun test icon-constants / workspace-icon-url / icon-cache 21 个通过
类型(shared) bun run typecheck:shared ✅ 通过
Lint(electron) bun x eslint(4 个文件) ✅ 通过
Lint(shared) bun x eslint(4 个文件) ✅ 通过(0 error;2 个 storage.ts 既有告警)
格式 prettier --check(8 个文件) ✅ 通过
空白/冲突 git diff --check ✅ 通过

这是一次干净、一致的重构:isIconUrl 改为大小写不敏感(/^https?:\/\//i),并把 4 个文件中 9 处内联的 startsWith('http://')||startsWith('https://') 调用点统一改用该共享判定函数。useWorkspaceIcon 中提取的 const workspaceIconUrl = workspace?.iconUrl 不改变行为(仅增加了可选链)。

前后对比 —— 对判定函数与 icon-cache 路径是“关键且必要的”

把 5 个源文件回退到修复前、但保留新增测试,有 3 个新测试失败(修复后通过):

(fail) icon URL detection > treats http and https schemes as case-insensitive
(fail) remote icon URLs > returns source icon URLs with uppercase schemes directly
(fail) remote icon URLs > returns skill icon URLs with uppercase schemes directly

这正是该 bug:修复前 'HTTPS://…'.startsWith('https://')false,大写的远程图标会被拒绝。✔️

⚠️ 不阻塞的发现:workspace-icon-url.test.ts 并非“关键且必要”

该测试(“保留大写远程图标 URL 协议,而不是回退到本地图标”)在修复前与修复后的 storage.ts 上都通过——我把 storage.ts 回退后它仍然通过,因此它并不能捕捉该处的回归。

根因:用例把本地 icon.svg 写到了工作区声明的 rootPath<configDir>/workspace),但在存在 config-defaults.json 时,getWorkspaces() 会把实际生效的 rootPath 重新解析为 <CONFIG_DIR>/workspaces/<slug>

effectiveRootPath=/tmp/…/workspaces/a
icon exists at effRoot/icon.svg=false   ← 用例把图标写到了 …/workspace,而不是 …/workspaces/a

于是 findWorkspaceIcon() 找不到图标,用例本想触发的“本地图标回退”根本没有发生,断言(URL 不变)无论大小写检查是否敏感都成立。

底层的 storage.ts 改动本身是正确的——它复用了同一个 isIconUrl 判定函数,而该函数有直接的单元测试(关键且必要)。只是这一个集成测试没有真正覆盖它。建议(不阻塞):强化该用例,让“本地图标回退”真正触发(把 icon.svg 放到 getWorkspaces() 实际解析出的目录,或针对该解析后的路径断言)——这样在没有修复时该测试就会失败。

扩展行为验证(我增补的 19 个用例,全部通过)

通过真实的 isIconUrl(无 mock):混合大小写(HtTpS://)、大写的主机/路径/查询串、以及仅含协议头均被接受file://FILE://ftp://data:httpsx://、单斜杠 https:/、前导空白、http: 均被拒绝(失败即关闭)。该正则以 ^ 锚定且不做 trim——与此前的 startsWith 行为一致。

关于 typecheck:electron

它确实失败(exit 2),但涉及 4 个与本 PR 无关的文件(auto-update.tssettings-default-thinking.test.tssession-delete-navigation.test.tsskills-loading.test.ts);本 PR 改动的 3 个 electron 文件类型干净。这些都是既有问题,与作者的说明一致。

结论

修复正确、精简、一致,核心判定函数也有良好覆盖。可以合并。建议(不阻塞)收紧 workspace-icon-url.test.ts,使 storage.ts 路径真正被守护。LGTM 👍

@wenshao

wenshao commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — all required sections present, bilingual, test plan included.

On direction: this is a clear, real bug — uppercase HTTPS:// icon URLs are valid per RFC 3986 but were silently rejected by lowercase-only startsWith checks. Desktop users setting custom icons with uppercase schemes would hit unexpected fallback behavior. Aligned with the project's goal of robust config handling.

On approach: the scope is tight and minimal. The core change is a single-line fix in isIconUrl (startsWith → case-insensitive regex), plus consolidating 9 scattered inline checks into the shared predicate. That's the right move — it eliminates a class of bugs (inconsistent case handling across call sites) rather than patching each one individually. The useWorkspaceIcon hook refactor (removing useRef, extracting local variables) is behavior-preserving cleanup that belongs in this PR since it touches the same code paths. Regression tests cover the predicate directly and the icon-cache call sites. Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓ — 所有必需章节齐全,包含双语说明和测试计划。

方向:这是一个真实且明确的 bug — 按照 RFC 3986,大写的 HTTPS:// 图标 URL 是合法的,但仅检查小写的 startsWith 会静默拒绝。桌面用户如果用大写协议设置自定义图标,会意外触发 fallback 行为。与项目"健壮处理配置"的目标一致。

方案:范围紧凑且最小化。核心改动是 isIconUrl 中的一行修复(startsWith → 大小写不敏感正则),同时将 9 处分散的内联检查统一收敛到共享判定函数。这是正确的做法 — 它消除了一整类 bug(各调用点大小写处理不一致),而不是逐个修补。useWorkspaceIcon hook 的重构(移除 useRef、提取局部变量)是行为不变的清理,因为涉及同一代码路径,放在本 PR 中是合理的。回归测试直接覆盖了判定函数和 icon-cache 调用点。进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal (before reading diff): I would fix isIconUrl with a case-insensitive regex (/^https?:\/\//i), then grep for every inline startsWith('http://') || startsWith('https://') in the desktop packages and replace them with the shared predicate. Add a direct unit test for uppercase schemes. That's the whole PR.

Comparison with the diff: the PR matches this exactly — and goes one step further by cleaning up the useWorkspaceIcon hook (removing useRef, extracting local variables to avoid repeated optional chaining). That's a welcome simplification in the same code path, not scope creep.

Findings:

  • Correctness ✓ — the regex /^https?:\/\//i is anchored, rejects non-http(s) schemes (file://, ftp://, data:), and doesn't trim whitespace (consistent with old startsWith semantics). No edge cases missed.
  • Consistency ✓ — all 9 inline startsWith call sites across 4 files are replaced with isIconUrl. No stragglers.
  • validateIconValue propagation ✓ — this function already calls isIconUrl internally, so the case-insensitive fix automatically flows through to icon validation for skills, sources, and statuses. No additional changes needed.
  • Hook refactor ✓ — the useWorkspaceIcon changes are behavior-preserving. useRef removal is correct since the ref was never read for comparison. Extracting workspaceId and workspaceIconUrl as local variables keeps the dependency array stable and identical in semantics.
  • Testsicon-constants.test.ts directly covers the predicate (load-bearing). icon-cache.test.ts covers loadSourceIcon/loadSkillIcon with uppercase URLs (load-bearing). Per @wenshao's thorough verification, workspace-icon-url.test.ts passes both before and after the fix due to a fixture path mismatch — non-blocking since the storage.ts change is guarded by the predicate's own unit test. Suggestion for a follow-up: tighten that fixture so it actually exercises the local-icon fallback path.
  • No security concerns — the regex is anchored and fails closed.

Real-Scenario Testing

This PR is entirely in the desktop Electron renderer process (useWorkspaceIcon hook, icon-cache, SourceInfoPage, storage.ts). The CLI code is untouched — there is no user-visible terminal behavior to drive via tmux. Verification relies on unit tests (21 passing per @wenshao's local run), typecheck, lint, and format checks.

The core behavioral evidence was captured by reverting the source files to pre-fix while keeping the new tests — 3 tests fail, confirming the bug and the fix:

(fail) icon URL detection > treats http and https schemes as case-insensitive
(fail) remote icon URLs > returns source icon URLs with uppercase schemes directly
(fail) remote icon URLs > returns skill icon URLs with uppercase schemes directly

All 3 pass after the fix. ✔️

中文说明

代码审查

独立方案(读 diff 前):把 isIconUrl 改为大小写不敏感正则(/^https?:\/\//i),然后搜索 desktop 包中所有内联的 startsWith('http://') || startsWith('https://') 替换为共享判定函数,再补一个针对大写协议的单元测试。这就是整个 PR。

与 diff 对比: PR 完全匹配该方案——并且进一步清理了 useWorkspaceIcon hook(移除 useRef、提取局部变量避免重复可选链)。这是同一代码路径上的合理简化,不是范围蔓延。

发现:

  • 正确性 ✓ — 正则 /^https?:\/\//i 有锚点,拒绝非 http(s) 协议(file://ftp://data:),不做 trim(与旧 startsWith 语义一致)。没有遗漏的边界情况。
  • 一致性 ✓ — 4 个文件中 9 处内联 startsWith 调用全部替换为 isIconUrl,无遗漏。
  • validateIconValue 传播 ✓ — 该函数内部已经调用 isIconUrl,大小写不敏感修复自动传导到 skills/sources/statuses 的图标校验,无需额外修改。
  • Hook 重构 ✓ — useWorkspaceIcon 的改动行为不变。移除 useRef 是正确的(该 ref 从未被用于比较)。提取 workspaceIdworkspaceIconUrl 为局部变量,依赖数组稳定且语义不变。
  • 测试icon-constants.test.ts 直接覆盖判定函数(关键且必要)。icon-cache.test.ts 覆盖大写 URL 的 loadSourceIcon/loadSkillIcon(关键且必要)。根据 @wenshao 的详细验证,workspace-icon-url.test.ts 在修复前后都通过(因 fixture 路径不匹配)——不阻塞,因为 storage.ts 的改动已被判定函数自身的单元测试守护。建议后续收紧该 fixture。
  • 无安全问题 — 正则锚定且失败关闭。

真实场景测试

本 PR 完全位于桌面 Electron 渲染进程(useWorkspaceIcon hook、icon-cacheSourceInfoPagestorage.ts)。CLI 代码未改动——没有可通过 tmux 驱动的终端行为。验证依赖单元测试(@wenshao 本地运行 21 个通过)、类型检查、lint 和格式检查。

核心行为证据:将源文件回退到修复前、保留新增测试——3 个测试失败,确认了 bug 和修复的有效性。修复后全部通过。✔️

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Reflection

This is a textbook example of a well-scoped bug fix. The motivation is clear (uppercase URL schemes are valid but rejected), the fix is minimal (one regex change + call-site consolidation), and the tests prove both the bug and the fix.

Going back to my independent proposal: the PR matches it exactly and adds a welcome hook cleanup. I wouldn't have done it differently.

The code is straightforward — no over-abstraction, no speculative features, no drive-by refactors beyond what touches the same paths. Every change in the diff is needed for the stated goal. The shared isIconUrl predicate is now the single source of truth for HTTP(S) icon URL detection across the desktop package, which makes future maintenance easier, not harder.

The one non-blocking finding (workspace-icon-url test fixture mismatch) was already surfaced by @wenshao and doesn't affect the safety of merging — the storage.ts code path is guarded by the predicate's own unit tests.

After seeing the revert-and-rerun evidence (3 tests fail before, pass after), the results match what the PR promised. If I had to maintain this in six months, I'd thank the author for consolidating 9 scattered startsWith checks into one well-tested predicate.

Verdict: LGTM — safe to merge. ✅

中文说明

总结

这是一个教科书级别的、范围恰当的 bug 修复。动机清晰(大写 URL 协议合法但被拒绝),修复最小化(一行正则改动 + 调用点统一),测试同时证明了 bug 和修复的有效性。

回到我的独立方案:PR 完全匹配,并附带了合理的 hook 清理。我不会用不同的方式来做。

代码简洁明了——没有过度抽象、没有投机性功能、没有超出同一路径范围的顺手重构。diff 中的每一处改动都是为了实现声明的目标。共享的 isIconUrl 判定函数现在是 desktop 包中 HTTP(S) 图标 URL 检测的唯一真实来源,让未来维护更容易而非更难。

唯一不阻塞的发现(workspace-icon-url 测试 fixture 路径不匹配)已由 @wenshao 提出,不影响合并安全性——storage.ts 代码路径已被判定函数自身的单元测试守护。

看到回退-重跑证据后(修复前 3 个测试失败,修复后通过),结果与 PR 的承诺一致。如果六个月后需要维护这段代码,我会感谢作者把 9 处分散的 startsWith 检查统一收敛到一个经过良好测试的判定函数中。

结论:LGTM — 可以合并。✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao
wenshao merged commit cf3e57f into QwenLM:main Jun 20, 2026
43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uppercase desktop icon URL schemes are ignored

3 participants