Skip to content

feat(web-shell): support custom Hex session group colors - #6752

Merged
samuelhsin merged 7 commits into
QwenLM:mainfrom
samuelhsin:feat/session-group-custom-color
Jul 12, 2026
Merged

feat(web-shell): support custom Hex session group colors#6752
samuelhsin merged 7 commits into
QwenLM:mainfrom
samuelhsin:feat/session-group-custom-color

Conversation

@samuelhsin

@samuelhsin samuelhsin commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Named session groups can now use either the existing preset colors or a custom six-digit Hex color. The daemon validates and canonicalizes custom values to lowercase, the SDK carries the expanded contract, and WebShell provides both a native color picker and a Hex text field while rendering custom group dots with the persisted color. Quick session color tags intentionally remain limited to the six presets.

Why it's needed

Six fixed colors are not enough for larger group catalogs or teams that use an established project palette. Supporting a precise Hex value extends the existing named-group model without creating a parallel color system or changing the lightweight quick-tag behavior.

Reviewer Test Plan

How to verify

Open WebShell session management and create a named group. Choose Custom, select a color with the picker, then enter an uppercase six-digit Hex value and save. Confirm the saved group dot uses that color and the value is returned in lowercase after reload. Reopen and rename the group while keeping or changing the custom color. Enter an invalid value such as #abc or 12abef and confirm Save is disabled with an accessible validation message. Finally, open the quick color menu for a session and confirm it still offers only the six preset colors.

Evidence (Before & After)

Before: named groups accept only six preset colors and reject custom values with invalid_group_color.

After: focused core, REST, ACP HTTP, TypeScript SDK, and WebShell tests cover Hex normalization, persistence after service restart, invalid input, preset-only quick tags, request/response round trips, picker/input synchronization, and custom-dot rendering.

WebShell custom Hex group color editor

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

macOS local checkout. Focused suites, package typechecks, and the root build pass after syncing upstream main with the unrelated scheduled-tasks import fix from #6748. The screenshot above was captured from the local WebShell against a fixture daemon; CI should validate the supported platform matrix.

Risk & Scope

  • Main risk or tradeoff: Custom colors are represented as validated strings at runtime, so consumers must use the preset type when they require exhaustive preset-only handling.
  • Not validated / out of scope: Custom quick-session colors, alpha channels, short Hex forms, and Windows/Linux manual UI checks.
  • Breaking changes / migration notes: None. Existing preset values and sidecar schema version 1 remain compatible.

Linked Issues

Closes #6744

中文说明

此 PR 的改动

命名会话分组现在既可使用现有预设颜色,也可使用自定义六位 Hex 颜色。daemon 会验证自定义值并统一转为小写,SDK 会传递扩展后的契约,WebShell 同时提供原生调色板和 Hex 文本输入框,并使用持久化的颜色渲染自定义分组圆点。会话的快捷颜色标签仍然只支持六种预设颜色。

为什么需要此改动

当分组数量较多或团队已有项目配色规范时,六种固定颜色并不足够。支持精确 Hex 值是在现有命名分组模型上的扩展,不会引入平行的颜色系统,也不会改变轻量快捷标签的行为。

Reviewer 测试计划

如何验证

打开 WebShell 会话管理并创建命名分组。选择“自定义”,先用调色板选色,再输入一个大写六位 Hex 值并保存。确认保存后的分组圆点使用该颜色,并在重新加载后返回小写值。重新打开并重命名分组,同时保留或修改自定义颜色。输入 #abc12abef 等无效值,确认保存按钮禁用并显示可访问的校验提示。最后打开会话的快捷颜色菜单,确认其中仍然只有六种预设颜色。

前后证据

改动前:命名分组只接受六种预设颜色,自定义值会以 invalid_group_color 被拒绝。

改动后:聚焦的 core、REST、ACP HTTP、TypeScript SDK 和 WebShell 测试覆盖 Hex 规范化、服务重启后的持久化、无效输入、仅预设的快捷标签、请求与响应往返、调色板与输入框同步,以及自定义圆点渲染。

测试平台

系统 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境(可选)

macOS 本地检出。同步包含 #6748 无关定时任务导入修复的 upstream main 后,聚焦测试、各包 typecheck 和根目录构建均通过。上方截图来自连接本地 fixture daemon 的 WebShell;支持平台矩阵交由 CI 验证。

风险与范围

  • 主要风险或取舍:自定义颜色在运行时以经过验证的字符串表示,因此需要穷举预设值的消费者必须使用预设颜色类型。
  • 未验证或不在范围内:会话快捷自定义色、Alpha 通道、短 Hex,以及 Windows/Linux 手工 UI 检查。
  • 破坏性变更或迁移说明:无。现有预设值与 sidecar schema version 1 保持兼容。

关联 Issue

Closes #6744

@samuelhsin

samuelhsin commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator Author

E2E / validation results

Verified on macOS against upstream main at 545e5b3c8:

  • Core session organization: 24 passed, including uppercase-to-lowercase normalization, malformed Hex rejection, preset-only quick colors, and persistence after recreating the service.
  • REST named-group Hex path: 1 focused test passed; the full server file otherwise had 674 passes and 1 unrelated pre-existing extension-update status failure (expected 202, received 200).
  • ACP HTTP named-group Hex path: 1 focused test passed.
  • TypeScript SDK: 229 passed.
  • WebShell sidebar: 61 passed, including surrounding-whitespace normalization, invalid input with a stable last-valid picker value, cross-dialog state reset, editing an existing custom group back to a preset, and custom dot rendering. Only pre-existing React act(...) warnings remain.
  • Core, CLI, ACP Bridge, and TypeScript SDK package typechecks passed.
  • ESLint and Prettier checks for all changed source/test files passed.

After syncing upstream main with the unrelated #6748 scheduled-tasks import fix, the root build and WebShell typecheck pass. The PR includes a local WebShell screenshot captured against a fixture daemon. Windows and Linux verification are delegated to CI.

@samuelhsin
samuelhsin marked this pull request as ready for review July 12, 2026 05:19
@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Re-run after merge conflict resolution (resolved by @qwen-code-dev-bot, verified locally by @wenshao). Gate findings unchanged.

Template looks good ✓ — all sections present, bilingual, linked to #6744.

Problem: well-defined feature request. Issue #6744 provides clear acceptance criteria, and the daemon currently rejects any non-preset group color with invalid_group_color. The problem is real and well-scoped.

Direction: aligned. Named session groups are a durable organizational feature, and six preset colors genuinely limit larger catalogs. Extending the existing model with #RRGGBB support is a natural evolution — no parallel system, no scope creep. scope/session-management is on the roadmap.

Size: 63 core production lines (session-organization-service.ts), 239 other production lines across 4 packages, 387 test lines, 180 docs/E2E plan. Well within bounds — no escalation needed.

Approach: the scope feels right. The change correctly splits SessionGroupPresetColor (the 6-value union used by quick tags) from SessionGroupColor (the union that also accepts hex), propagating the split through core → CLI routes → SDK types → WebShell UI. Quick session tags remain preset-only. Core normalizes hex to lowercase. WebShell adds a native picker + hex text field with client-side validation. Every edit in the diff serves the stated goal — no drive-by refactors, no unrelated changes.

Moving on to code review. 🔍

中文说明

合并冲突解决后重新运行(由 @qwen-code-dev-bot 解决,@wenshao 本地验证)。门控结论不变。

模板完整 ✓ — 所有部分齐全,双语,关联 #6744

问题:明确的功能需求。Issue #6744 提供了清晰的验收标准,daemon 目前会以 invalid_group_color 拒绝所有非预设分组颜色。问题是真实且范围明确的。

方向:对齐。命名会话分组是持久的组织功能,六种预设颜色确实限制了更大的分类目录。在现有模型上扩展 #RRGGBB 支持是自然的演进——没有平行系统,没有范围蔓延。scope/session-management 在路线图上。

规模:63 行核心生产代码(session-organization-service.ts),4 个包共 239 行其他生产代码、387 行测试、180 行文档/E2E 计划。远在合理范围内——无需升级。

方案:范围合理。改动正确地将 SessionGroupPresetColor(快捷标签使用的 6 值联合)与 SessionGroupColor(同时接受 hex 的联合类型)分离,并在 core → CLI 路由 → SDK 类型 → WebShell UI 中传播。快捷会话标签保持仅预设。Core 将 hex 统一为小写。WebShell 添加了原生调色板和 hex 文本输入框,含客户端验证。diff 中的每处改动都服务于目标——没有顺手重构,没有无关改动。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@samuelhsin samuelhsin left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review

Verdict: Approve with suggestions — direction matches #6744; named groups gain validated #RRGGBB while quick session tags stay preset-only. No Critical findings against commit e54e7eae46b05d9c7df51a0e0d347999c0c29565.

Why this looks correct

  1. Core normalizeGroupColor() accepts presets or /^#[0-9a-f]{6}$/i and canonicalizes Hex to lowercase; assertSessionColor() keeps quick tags preset-only.
  2. Type split is consistent across core / SDK / bridge / REST / ACP: named-group color is Preset | Hex; session organization and colorOptions remain preset.
  3. Named-group create/update routes pass color through to core validation; organization routes still reject Hex via GROUP_COLOR_OPTIONS.
  4. WebShell adds Custom + native picker + Hex field; custom dots use inline backgroundColor; invalid Hex disables Save and surfaces role="alert".
  5. Focused tests cover normalization, persistence after restart, malformed Hex, preset-only quick tags, picker/input sync, and custom-dot rendering.

Critical

None.

CI Test / web-shell E2E Smoke failures are the unrelated upstream ScheduledTasksDialog → missing ../composerTagIcons build break already disclosed in the PR description, not introduced by this diff.

Suggestions

  1. Trim Hex input before validate"#12abef " currently fails both client normalizeGroupColorInput and core normalizeGroupColor. Trimming would avoid accidental invalidation.
  2. Picker value while Hex is invalidvalue={normalizedGroupColor ?? DEFAULT_CUSTOM_GROUP_COLOR} can show the default swatch while the text field still holds an illegal value. Prefer freezing the last valid color (or leaving the picker unbound) until the Hex parses again.
  3. #${string} is wider than runtime — runtime regex is authoritative; a one-line comment on the type would help callers not assume any #… string is legal.
  4. Light custom dots — custom dots have no border/outline; #ffffff can disappear on a light sidebar. A subtle ring would help contrast.
  5. Merge hygiene — rebase or wait for the composerTagIcons fix on main so CI green is attributable to this change.

#6744 checklist

Criterion Result
Named groups accept #RRGGBB Pass
Lowercase normalize + persist Pass
Picker + Hex text field Pass
Quick tags remain six presets Pass
SDK contract expanded Pass

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Code Review

The implementation is clean and well-structured. The type split between SessionGroupPresetColor (6-value union) and SessionGroupColor (preset | hex) propagates correctly through all layers — core → CLI routes → ACP → SDK → WebShell. Quick session tags stay preset-only as intended.

No critical blockers or AGENTS.md violations found.

Notable design choices (all sound):

  • normalizeGroupColor() in core is the single source of truth for validation/canonicalization — clients can validate early for UX, but core remains authoritative.
  • SessionGroupHexColor = \#${string}`is intentionally loose at the type level; runtime validation enforces the#RRGGBB` shape.
  • getGroupColorClass() returns undefined for hex colors (falling through to inline style via getGroupColorStyle()), replacing the previous never exhaustive check. Clean.
  • normalizeSessionGroup gracefully handles stored hex colors via normalizeGroupColor() with a try/catch fallback — backward compatible with schema version 1.

One observation (non-blocking): normalizeGroupColorInput() in WebShellSidebar.tsx duplicates core's hex validation regex on the client side. This is expected and correct for UX (disables Save button immediately), but worth knowing it's a second copy if the validation rule ever changes.

Unit Tests (re-run on PR head)

Suite Result
Core session-organization-service.test.ts 24 passed ✅
CLI server.test.ts (REST) 675 passed ✅
CLI transport.test.ts (ACP HTTP) 272 passed ✅
SDK DaemonClient.test.ts 230 passed ✅
Total 1,201 passed

WebShell WebShellSidebar.test.tsx could not run in this CI worktree — @qwen-code/webui/daemon-react-sdk import resolution fails because the webui package hasn't been built in this environment. This is a pre-existing environment issue unrelated to the PR. Both the author and @wenshao independently verified all 61 WebShell tests pass locally.

REST API Live Test (tmux)

Started npm run dev -- serve from the PR branch in the worktree. Exercised the daemon REST API:

$ npm run dev -- serve
qwen serve listening on http://127.0.0.1:4170 (mode=http-bridge, workspace=.../worktrees/triage)

=== Test 1: Create group with uppercase hex ===
$ curl -X POST .../session-groups -d '{"name":"HexDemo","color":"#12ABEF"}'
{"group":{"id":"8d9de2ed-...","name":"HexDemo","color":"#12abef",...}}
HTTP 201

=== Test 2: Invalid hex (#abc) ===
$ curl -X POST .../session-groups -d '{"name":"Bad","color":"#abc"}'
{"error":"`color` must be a supported preset or a #RRGGBB hex value","code":"invalid_group_color","field":"color"}
HTTP 400

=== Test 3: Hex without # prefix ===
$ curl -X POST .../session-groups -d '{"name":"Bad2","color":"12abef"}'
{"error":"`color` must be a supported preset or a #RRGGBB hex value","code":"invalid_group_color","field":"color"}
HTTP 400

=== Test 4: Preset color still works ===
$ curl -X POST .../session-groups -d '{"name":"PresetDemo","color":"blue"}'
{"group":{"id":"eb04771a-...","name":"PresetDemo","color":"blue",...}}
HTTP 201

=== Test 5: List groups ===
$ curl .../session-groups
{"groups":[
  {"name":"HexDemo","color":"#12abef",...},
  {"name":"PresetDemo","color":"blue",...}],
 "colorOptions":["red","orange","yellow","green","blue","purple"]}

=== Test 6: Update group with new hex ===
$ curl -X PATCH .../session-groups/<id> -d '{"name":"HexRenamed","color":"#FEDCBA"}'
{"group":{"name":"HexRenamed","color":"#fedcba",...}}
HTTP 200

=== Test 7: Quick session color tag rejects hex ===
$ curl -X PATCH .../session/<id>/organization -d '{"color":"#12abef"}'
{"error":"`color` must be a supported color or null","code":"invalid_session_organization","field":"color"}
HTTP 400

=== Test 8: Quick session color tag accepts preset ===
$ curl -X PATCH .../session/<id>/organization -d '{"color":"blue"}'
{"sessionId":"...","color":"blue",...}
HTTP 200

Every acceptance criterion from #6744 verified: hex normalization to lowercase, invalid input rejection, preset-only quick tags, group CRUD with hex, colorOptions still preset-only.

WebShell UI could not be tested live — same pre-existing environment blocker. WebShell unit tests (61 passed, verified by author and maintainer) cover the custom color UI, picker/hex sync, validation, and dot rendering.

中文说明

代码审查

实现整洁且结构良好。SessionGroupPresetColor(6 值联合)与 SessionGroupColor(preset | hex)的类型分离在所有层正确传播——core → CLI 路由 → ACP → SDK → WebShell。快捷会话标签如预期保持仅预设。

未发现关键阻断项或 AGENTS.md 违规。

值得注意的设计选择(均合理):

  • core 中的 normalizeGroupColor() 是验证/规范化的唯一真实来源——客户端可以为 UX 提前验证,但 core 保持权威性。
  • SessionGroupHexColor = \#${string}`在类型层面故意宽松;运行时验证强制#RRGGBB` 格式。
  • getGroupColorClass() 对 hex 颜色返回 undefined(通过 getGroupColorStyle() 使用内联 style),替代了之前的 never 穷举检查。简洁。
  • normalizeSessionGroup 通过 normalizeGroupColor() 和 try/catch 回退优雅处理存储的 hex 颜色——与 schema version 1 向后兼容。

一个观察(不阻断): WebShellSidebar.tsx 中的 normalizeGroupColorInput() 在客户端重复了 core 的 hex 验证正则。这对 UX 是正确且预期的(立即禁用保存按钮),但如果验证规则将来更改,需知这是第二份副本。

单元测试(PR 头重新运行)

套件 结果
Core session-organization-service.test.ts 24 通过 ✅
CLI server.test.ts(REST) 675 通过 ✅
CLI transport.test.ts(ACP HTTP) 272 通过 ✅
SDK DaemonClient.test.ts 230 通过 ✅
总计 1,201 通过

WebShell WebShellSidebar.test.tsx 无法在此 CI worktree 中运行——@qwen-code/webui/daemon-react-sdk 引入解析失败,因为 webui 包未在此环境中构建。这是与本 PR 无关的预存环境问题。作者和 @wenshao 均已独立验证全部 61 个 WebShell 测试在本地通过。

REST API 实机测试(tmux)

在 worktree 的 PR 分支上启动 npm run dev -- serve 并测试 daemon REST API。所有 #6744 验收标准均已验证:hex 规范化为小写、无效输入拒绝、仅预设快捷标签、hex 分组 CRUD、colorOptions 保持仅预设。

WebShell UI 无法实机测试——同一预存环境阻塞。WebShell 单元测试(61 通过,由作者和维护者验证)覆盖了自定义颜色 UI、调色板/hex 同步、验证和圆点渲染。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Re-run after merge conflict resolution. All findings hold.

This PR ships the feature cleanly. The type design is correct — splitting SessionGroupPresetColor from SessionGroupColor gives a clear contract where quick tags stay preset-only and named groups accept both. Core validation is the single source of truth, propagation through all layers is consistent, and the WebShell UI adds a sensible picker + hex field with proper validation feedback.

Live REST API testing on the PR head confirms: hex normalization to lowercase works, invalid values are rejected with structured errors, preset colors remain backward compatible, and quick session tags correctly reject hex. All 1,201 unit tests across the affected packages pass. The one gap is WebShell live UI testing, blocked by an unrelated upstream environment issue — the 61 WebShell unit tests (independently verified by both the author and a maintainer) cover the behavior well enough.

The merge conflict resolution was clean — the PR's custom color constants and main's footer/tooltip infrastructure are additive and independent. No semantic conflict.

Scope is tight (63 core + 239 other production lines), documentation and E2E plan are included, and every change serves the stated goal. This is ready to ship.

中文说明

合并冲突解决后重新运行。所有结论不变。

这个 PR 干净地交付了该功能。类型设计正确——将 SessionGroupPresetColorSessionGroupColor 分离,提供了清晰的契约:快捷标签保持仅预设,命名分组同时接受预设和 hex。Core 验证是唯一的真实来源,所有层的传播一致,WebShell UI 添加了合理的调色板 + hex 输入框和适当的验证反馈。

PR 头上的 REST API 实机测试确认:hex 规范化为小写有效、无效值以结构化错误拒绝、预设颜色向后兼容、快捷会话标签正确拒绝 hex。受影响包的所有 1,201 个单元测试通过。唯一的缺口是 WebShell 实机 UI 测试,被无关的上游环境问题阻塞——61 个 WebShell 单元测试(由作者和维护者独立验证)已充分覆盖该行为。

合并冲突解决干净——PR 的自定义颜色常量与 main 的页脚/提示基础设施是独立的增量添加,无语义冲突。

范围紧凑(63 行核心 + 239 行其他生产代码),包含文档和 E2E 计划,每处改动都服务于目标。可以合入。

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. ✅

@samuelhsin

Copy link
Copy Markdown
Collaborator Author

Added a WebShell screenshot of the custom Hex group color editor to the PR description.

@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.

Reviewed — no blockers. Suggestions are inline.

Comment thread packages/web-shell/client/components/sidebar/WebShellSidebar.tsx Outdated
@wenshao

wenshao commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /resolve

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

Qwen Code resolved the merge conflicts and pushed the branch update.

Merge conflict resolution summary for PR #6752

Conflicted file

packages/web-shell/client/components/sidebar/WebShellSidebar.tsx

Conflict details

The single conflict was in the module-level constants block (around line 54), where two independent additions overlapped:

HEAD (PR branch) added two constants for the custom Hex color feature:

const CUSTOM_GROUP_COLOR_OPTION = '__custom__';
const DEFAULT_CUSTOM_GROUP_COLOR: DaemonSessionGroupHexColor = '#416ef5';

origin/main added tooltip layout constants, exported types/interfaces (WebShellSidebarFooterItem, WebShellSidebarBranding, WebShellSidebarFooterOptions), and footer layout constants (DEFAULT_FOOTER_ITEMS, FOOTER_OVERFLOW_PRIORITY) — all from PR #6750 (configurable session sidebar).

Resolution

Kept both sides. The changes are additive and independent — the PR's custom color constants and main's footer/tooltip infrastructure do not conflict semantically. The PR branch's two constants were placed first (preserving their original position), followed by main's block.

Other auto-merged files

Git auto-merged several other files without conflicts (CSS, test files, i18n, App.tsx, etc.). These were reviewed via the staged diff and appear correct.

Commit

chore: resolve merge conflicts with main for custom Hex session group colors

@wenshao

wenshao commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

✅ Local verification — merge reference

Built and tested this PR from a clean isolated worktree at the PR head (06a97e93a, checked against origin/main merge-base 0579be6ee). Every focused suite, typecheck, lint, and format check is green, and the WebShell UI was rendered in a real browser to confirm the feature behaves end‑to‑end.

Environment: macOS (darwin 24.6), Node v22.23.1 / npm 10.9.8, fresh npm ci (full bundle build via prepare).

Results

Check Scope Result
core vitest session-organization-service.test.ts 24/24
sdk-typescript vitest DaemonClient.test.ts 230/230
web-shell vitest WebShellSidebar.test.tsx 61/61
cli vitest (REST) create/update hex + invalid-color reject 2/2
cli vitest (ACP HTTP) session_groups round-trip 1/1
typecheck core · sdk-typescript · cli · web-shell · acp-bridge ✅ all exit 0
eslint 16 changed .ts/.tsx ✅ exit 0
prettier --check 16 changed files ✅ clean

Before / After proof

To confirm the new tests actually exercise the new code (not just pass trivially), I re-ran the PR's new core test against origin/main's pre-PR session-organization-service.ts overlaid into the tree:

  • On main: 1 failed | 23 passedaccepts and normalizes custom hex colors for named groups throws SessionOrganizationError: color must be one of the supported color options at the old assertGroupColor guard (session-organization-service.ts:142).
  • On this PR: 24 passed.

So #RRGGBB acceptance + lowercase canonicalization is genuinely new behavior, and the guard for preset-only quick session tags is preserved (updateSessionOrganization({color:'#12abef'}) and even ' blue ' are still rejected — verified in core, and the REST layer returns 400 invalid_session_organization).

verification

WebShell UI (rendered in a real browser via the mock daemon)

The named‑group editor now offers a Custom… option that reveals a native color picker synced with a #RRGGBB text field, in both light and dark themes:

editor light and dark

Invalid input disables Save and shows an accessible (role="alert", aria-invalid) message; the persisted custom color renders as the group dot in the sidebar via inline background-color (computed rgb(168, 85, 247) for #a855f7):

invalid state and custom dot

Notes for the reviewer

  • The type split (SessionGroupColor = preset or hex; SessionGroupPresetColor = preset‑only) is applied consistently across core → acp‑bridge → cli serve (REST + ACP HTTP) → SDK → WebShell, and all five packages typecheck clean. Quick session color tags remain intentionally preset‑only.
  • Sidecar back‑compat holds: unknown stored colors still fall back + warn once; a valid custom hex now persists through a service restart (covered by the core test).
  • Not covered here: Windows/Linux manual UI (matches the author's own matrix note), and a live end‑to‑end daemon round‑trip — though the REST/ACP integration suites already assert the wire contract with hex values.

Verdict: LGTM from a build/test standpoint — behavior, contract, i18n (EN + ZH), and theming all verified locally. 👍

中文说明(点击展开)

✅ 本地验证 —— 合并参考

从干净的独立 worktree 检出 PR 头 06a97e93a(与 origin/main 的 merge-base 为 0579be6ee)进行构建与测试。所有聚焦测试、类型检查、lint 与格式检查均通过,并在真实浏览器中渲染 WebShell UI 以确认端到端行为。

环境: macOS(darwin 24.6),Node v22.23.1 / npm 10.9.8,全新 npm ci(通过 prepare 完成完整打包构建)。

结果

检查项 范围 结果
core vitest session-organization-service.test.ts 24/24
sdk-typescript vitest DaemonClient.test.ts 230/230
web-shell vitest WebShellSidebar.test.tsx 61/61
cli vitest(REST) 创建/更新 hex + 非法颜色拒绝 2/2
cli vitest(ACP HTTP) session_groups 往返 1/1
typecheck core · sdk-typescript · cli · web-shell · acp-bridge ✅ 全部退出码 0
eslint 16 个改动的 .ts/.tsx ✅ 退出码 0
prettier --check 16 个改动文件 ✅ 通过

前后对比证明

为确认新增测试确实覆盖了新代码(而非平凡通过),我把 origin/main 改动前的 session-organization-service.ts 覆盖回工作区,用 PR 的测试重跑:

  • main 上: 1 失败 | 23 通过 —— accepts and normalizes custom hex colors for named groups 在旧的 assertGroupColorsession-organization-service.ts:142)处抛出 SessionOrganizationError: color must be one of the supported color options
  • 在本 PR 上: 24 通过

因此 #RRGGBB 的接受 + 小写规范化确为新增行为;同时会话快捷颜色标签仍保持仅预设(updateSessionOrganization({color:'#12abef'}) 甚至 ' blue ' 仍被拒绝 —— 已在 core 层验证,REST 层返回 400 invalid_session_organization)。

WebShell UI(通过 mock daemon 在真实浏览器中渲染)

命名分组编辑器新增 Custom… 选项,展开后是与 #RRGGBB 文本框联动的原生调色板,明暗主题均正常(见上方英文部分图 2)。非法输入会禁用 Save 并显示可访问的提示(role="alert"aria-invalid);持久化的自定义颜色通过内联 background-color 渲染为侧边栏分组圆点(#a855f7 计算值为 rgb(168, 85, 247),见上方英文部分图 3)。

给 Reviewer 的说明

  • 类型拆分(SessionGroupColor = 预设 hex;SessionGroupPresetColor = 仅预设)在 core → acp-bridge → cli serve(REST + ACP HTTP)→ SDK → WebShell 全链路一致,五个包 typecheck 全部干净。会话快捷颜色标签有意保持仅预设。
  • Sidecar 向后兼容:未知的已存储颜色仍会回退并告警一次;合法自定义 hex 现在可跨服务重启持久化(core 测试已覆盖)。
  • 本次未覆盖: Windows/Linux 手动 UI(与作者的平台矩阵说明一致),以及真实的端到端 daemon 往返 —— 不过 REST/ACP 集成测试已用 hex 值断言了线上契约。

结论: 从构建/测试角度 LGTM —— 行为、契约、i18n(中英)与主题均已在本地验证。👍

Verified locally on an isolated worktree; screenshots rendered from the real WebShell against a fixture daemon.

…o-prefix Hex input

Review follow-ups for the custom Hex group color editor:

- Validate the preset branch against the daemon-provided color catalog
  instead of the hardcoded palette, so future preset additions stay
  selectable in the editor.
- Auto-prefix bare values with '#' in the Hex field so pasted bare Hex
  validates, and free text can no longer collide with a preset name and
  silently flip the select out of Custom mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wenshao
wenshao previously approved these changes Jul 12, 2026
@samuelhsin
samuelhsin enabled auto-merge July 12, 2026 08:41

@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.

Reviewed — no blockers. Suggestions are inline.


.groupColorError {
grid-column: 2;
color: var(--destructive, #dc2626);

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.

[Suggestion] .groupColorError uses var(--destructive, #dc2626) but every other error-text element in this module uses var(--error-color) (see .dangerButton and error messages elsewhere). The --destructive variable is not defined anywhere in the web-shell package, so the fallback #dc2626 always fires — and it differs from the --error-color values (#fc8181 light / #c0362c dark), producing a visually inconsistent red.

Suggested change
color: var(--destructive, #dc2626);
color: var(--error-color);

— qwen3.7-max via Qwen Code /review

@@ -353,8 +381,13 @@ function getGroupColorClass(color: DaemonSessionGroupColor): string {
case 'purple':
return styles.groupColorPurple;
}

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.

[Suggestion] The old getGroupColorClass had a const exhaustive: never = color compile-time exhaustiveness check that caught presets added to the type without a corresponding case. Now that DaemonSessionGroupColor includes the open-ended hex template type, the switch can no longer be exhaustive — but a future preset typo (e.g. 'purpl') would silently return undefined instead of failing at compile time. Consider adding a comment marking the switch as needing manual upkeep when presets are added, or an explicit default that logs a warning.

— qwen3.7-max via Qwen Code /review

).toBe('#416ef5');
});

it('edits an existing custom group and switches it back to a preset', async () => {

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.

[Suggestion] The edit tests cover the custom-to-preset direction but not the reverse: opening the rename dialog on a group whose color is a preset (e.g. 'red'), switching to Custom mode, entering a hex value, saving, and asserting updateSessionGroup is called with the new hex color. This exercises a different initialization path in handleRenameGroup — specifically normalizeHexColorInput(group.color) ?? DEFAULT_CUSTOM_GROUP_COLOR taking the fallback branch.

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 12, 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.

LGTM, looks ready to ship. ✅

@samuelhsin
samuelhsin added this pull request to the merge queue Jul 12, 2026
Merged via the queue into QwenLM:main with commit 7468e75 Jul 12, 2026
75 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.

feat(web-shell): support custom Hex colors for named session groups

4 participants