Skip to content

fix(ui): distinguish auto approval mode indicators#4600

Merged
tanzhenxin merged 3 commits into
QwenLM:mainfrom
he-yufeng:fix/distinguish-auto-approval-mode-ui
Jun 3, 2026
Merged

fix(ui): distinguish auto approval mode indicators#4600
tanzhenxin merged 3 commits into
QwenLM:mainfrom
he-yufeng:fix/distinguish-auto-approval-mode-ui

Conversation

@he-yufeng

Copy link
Copy Markdown
Contributor

Summary

  • add a shared approval-mode visual mapping for the CLI
  • keep auto-accept edits on warning/yellow, but render classifier auto mode with the link/blue color
  • apply the same AUTO styling to the main input prompt and agent-view input prompt
  • add regression coverage for the distinct AUTO vs AUTO_EDIT visual mapping

Fixes #4575

To verify

  • npm run test --workspace=@qwen-code/qwen-code -- approvalModeVisuals.test.ts InputPrompt.test.tsx
  • npm run lint --workspace=@qwen-code/qwen-code
  • npm run typecheck --workspace=@qwen-code/qwen-code
  • npm run build
  • git diff --check

) : approvalModePromptStyle ? (
approvalModePromptStyle.prefix
) : (
'>'

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 : '>' fallback here is dead code. getApprovalModePromptStyle() always returns at least { prefix: '>' }, so approvalModePromptStyle is always truthy when this branch is reached (shell mode is handled above). The fallback can never execute.

Suggested change
'>'
) : (
approvalModePromptStyle.prefix
)}{' '}

— qwen3.7-max via Qwen Code /review

} from './approvalModeVisuals.js';

describe('approval mode visuals', () => {
it('uses distinct colors for auto-edit and classifier auto mode', () => {

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] Several ApprovalMode branches are untested:

  • getApprovalModeIndicatorColor: missing assertions for PLAN (theme.status.success), YOLO (theme.status.error), and DEFAULT (undefined)
  • getApprovalModePromptStyle: the PLAN/DEFAULT/default fallthrough (which returns { prefix: '>' } with no color/label) is completely untested

Adding these would guard against accidental styling regressions, especially for the PLAN/DEFAULT path which is the exact code path that changes behavior in AgentComposer.

— qwen3.7-max via Qwen Code /review

} else if (approvalModePromptStyle.label === 'edits') {
statusText = t('Accepting edits');
} else if (approvalModePromptStyle.label === 'auto') {
statusText = t('Auto mode');

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 'Auto mode' key is missing from all locale files under packages/cli/src/i18n/locales/ (zh, fr, ca, ja, etc.) and from mustTranslateKeys.ts. All peer status-text keys ('Shell mode', 'YOLO mode', 'Accepting edits') are properly translated. Non-English users will see the raw English string "Auto mode" in the prompt status area.

— qwen3.7-max via Qwen Code /review

@pomelo-nwu

Copy link
Copy Markdown
Collaborator

Review

Solid change, right direction. It pulls the approval-mode color logic that was duplicated across 3 components into a shared approvalModeVisuals.ts, makes AUTO visually distinct (blue / text.link) from AUTO_EDIT (yellow) — exactly what #4575 asks for — and adds test coverage. Findings below, by priority.

1. [Should fix] The new t('Auto mode') has no i18n translation

InputPrompt.tsx introduces t('Auto mode'), but this key exists in no locale file — not even en.js. Its sibling status labels are all translated:

key en.js zh.js
'Shell mode' Shell 模式
'YOLO mode' YOLO 模式
'Accepting edits' 接受编辑
'Auto mode' ❌ missing ❌ missing

So non-English users will see the literal English "Auto mode" in the input prompt while every other mode is localized. Suggest adding the key to at least en.js + zh.js, ideally aligning the other 8 locales too.

2. [Minor / author's call] AUTO's prompt color is full-intensity while its siblings are dimmed

In getApprovalModePromptStyle: AUTO_EDIT → warningDim, YOLO → errorDim, but AUTO → theme.text.link (full AccentBlue, not a dim variant). There is no AccentBlueDim/linkDim token in the codebase.

The result is that the AUTO input-prompt border/prefix renders noticeably brighter than the other two. The footer indicator uses full colors for all four modes (consistent there), but the input-prompt border convention is the dim variant.

I would not add a whole dim-blue token just for this (it'd touch ~15 theme files — overkill). But it's worth a conscious decision: accept the brightness difference, or use a slightly darker blue for the prompt. A note in the PR would help.

3. [Confirm intent] AgentComposer's PLAN-mode border behavior changed (not mentioned in the description)

The old AgentComposer logic was isAutoAccept = mode !== DEFAULT, so PLAN mode also got a warningDim (yellow) border. The new code gives PLAN no color, so it falls back to theme.border.focused.

Since APPROVAL_MODES = Object.values(ApprovalMode) includes PLAN and the agent view can cycle into it, the PLAN border genuinely changes from "dim yellow" to "default focused". This actually aligns AgentComposer with the main InputPrompt (which also gives PLAN no special color), so it's arguably a fix rather than a regression — but since the description doesn't mention it, please confirm it's intentional.

Fine as-is (no change needed)

The label: 'edits' | 'auto' | 'yolo' indirection (helper returns a label, the component maps it back to t()) looks a little roundabout, but since the i18n extractor needs literal t('...') strings, keeping the t() calls in the component and using label as a discriminator is reasonable. No refactor needed.

Verdict: Mergeable — core logic and tests are correct. The only thing that really should be addressed is the #1 i18n key; #2 and #3 just need a confirmation of the trade-off / intent.

中文

Review

方向正确,质量不错。把原本分散在 3 个组件里的审批模式配色逻辑抽到了共享模块 approvalModeVisuals.ts,AUTO 模式现在用蓝色(text.link)和 AUTO_EDIT 的黄色区分开,正好对上 #4575 的诉求,还补了测试覆盖。下面按优先级列问题。

1.【应修复】新增的 t('Auto mode') 没有任何 i18n 翻译

InputPrompt.tsx 引入了 t('Auto mode'),但这个 key 在所有 locale 文件里都不存在(包括 en.js)。而它的同类状态标签都是有翻译的:

key en.js zh.js
'Shell mode' Shell 模式
'YOLO mode' YOLO 模式
'Accepting edits' 接受编辑
'Auto mode' ❌ 缺失 ❌ 缺失

后果:中文/日文等用户在输入框看到的会是英文 "Auto mode",而其它模式都是本地化的,不一致。建议至少补 en.js + zh.js,最好对齐其余 8 个 locale。

2.【小问题,作者拍板】AUTO 的输入框配色用了全亮度,与同类 dim 不一致

getApprovalModePromptStyle 里:AUTO_EDIT → warningDim、YOLO → errorDim,唯独 AUTO → theme.text.link(全亮 AccentBlue,不是 dim 变体)。代码库里并没有 AccentBlueDim/linkDim 这个 token。

结果是 AUTO 模式的输入框边框/前缀会比另外两个明显更亮。footer 指示器那边四个模式都用全色是一致的,但输入框边框的既有惯例是用 dim 色。

不建议为此新增一整套 dim 蓝 token(要改 ~15 个主题文件,过度了)。但作者应该有意识地接受这个亮度差异,或者在 prompt 里用个稍暗的蓝。在 PR 里说明一下取舍会更好。

3.【需确认意图】AgentComposer 对 PLAN 模式的边框行为变了(描述未提)

AgentComposer 逻辑是 isAutoAccept = mode !== DEFAULT,所以 PLAN 模式也会拿到 warningDim 黄边;新代码里 PLAN 无 color,回退到 theme.border.focused

由于 APPROVAL_MODES = Object.values(ApprovalMode) 包含 PLAN,agent view 能循环切到 PLAN,所以这个边框确实从"黄-dim"变成了"默认 focused 边"。这其实让 AgentComposer 和主 InputPrompt 对齐了(主 InputPrompt 对 PLAN 同样不特殊上色),算修正而非回归——但描述没提,建议确认是有意为之。

可以不动的(避免过度设计)

label: 'edits' | 'auto' | 'yolo' 这层间接(helper 返回 label、组件再映射回 t())初看有点绕,但因为 i18n 提取工具需要 t('字面量'),把 t() 调用留在组件、用 label 当判别器是合理做法,不必重构

结论: 可以合 —— 核心逻辑和测试都正确。唯一真正该补的是 #1 的 i18n key;#2#3 让作者确认取舍/意图即可。

@pomelo-nwu

pomelo-nwu commented May 29, 2026

Copy link
Copy Markdown
Collaborator

Follow-up (KISS): the label indirection can be dropped — correcting my earlier note

In my previous review I filed the label field under "fine as-is, no refactor needed". Under a strict KISS lens I'd walk that back: label only serves one consumer (InputPrompt)AgentComposer reads only .color and .prefix and never touches .label. Putting it in the shared helper lets one consumer's need leak into the shared contract.

It also adds a needless round-trip: the helper does switch(mode) → label, then InputPrompt does if/else(label) → t(...) — two passes with label as a courier in between.

Now:

// helper returns { color?, prefix, label? }
} else if (approvalModePromptStyle?.color) {
  statusColor = approvalModePromptStyle.color;
  if (label === 'yolo') statusText = t('YOLO mode');
  else if (label === 'edits') statusText = t('Accepting edits');
  else if (label === 'auto') statusText = t('Auto mode');
}

Simplified — helper returns just { color?, prefix }. statusText is InputPrompt-only, and the block is already an if/else chain, so just swap the discriminant (labelapprovalMode) — the structure doesn't change at all:

} else if (approvalModePromptStyle?.color) {
  statusColor = approvalModePromptStyle.color;
  if (approvalMode === ApprovalMode.YOLO) {
    statusText = t('YOLO mode');
  } else if (approvalMode === ApprovalMode.AUTO_EDIT) {
    statusText = t('Accepting edits');
  } else if (approvalMode === ApprovalMode.AUTO) {
    statusText = t('Auto mode');
  }
}

Payoff: the shared interface shrinks to the fields all three components actually use, one mode → label → text hop disappears, and AgentComposer no longer carries a field it ignores. The literal t('...') calls stay in the component, so i18n extraction still works.

One thing NOT to do: don't collapse the two helpers into a single Record<ApprovalMode, {...}> table. The footer uses full colors while the prompt uses dim variants — different semantics, so two functions read clearer. Merging would be lateral churn, not a simplification.

中文

补充(KISS):label 这层间接可以去掉 —— 更正我上一条的说法

我在上一条 review 里把 label 归为"可不改"。按严格的 KISS 标准,这个判断我收回:label 只服务 InputPrompt 一个消费者 —— AgentComposer 只读 .color.prefix,根本不碰 .label。把它放进共享 helper,等于让一个消费者的需求污染了共享契约。

它还制造了一次没必要的往返:helper 里 switch(mode) → label,InputPrompt 里又 if/else(label) → t(...),中间拿 label 当传话筒。

现在:

// helper 返回 { color?, prefix, label? }
} else if (approvalModePromptStyle?.color) {
  statusColor = approvalModePromptStyle.color;
  if (label === 'yolo') statusText = t('YOLO mode');
  else if (label === 'edits') statusText = t('Accepting edits');
  else if (label === 'auto') statusText = t('Auto mode');
}

简化后 —— helper 只返回 { color?, prefix }statusText 本就是 InputPrompt 独有的,而且这段原本就是 if/else 链,所以只需把判断变量从 label 换成 approvalMode,结构一行都不用动:

} else if (approvalModePromptStyle?.color) {
  statusColor = approvalModePromptStyle.color;
  if (approvalMode === ApprovalMode.YOLO) {
    statusText = t('YOLO mode');
  } else if (approvalMode === ApprovalMode.AUTO_EDIT) {
    statusText = t('Accepting edits');
  } else if (approvalMode === ApprovalMode.AUTO) {
    statusText = t('Auto mode');
  }
}

收益:共享接口缩到三个组件都真正用到的字段、去掉一次 mode → label → text 转换、AgentComposer 不再携带它忽略的字段。字面量 t('...') 仍留在组件里,i18n 提取照常工作。

一条明确不要做的: 别把那两个 helper 合并成一张 Record<ApprovalMode, {...}> 查表。footer 用全色、prompt 用 dim 色,语义不同,拆成两个函数更清楚。合并是横向折腾,不是简化。

@wenshao

wenshao commented May 29, 2026

Copy link
Copy Markdown
Collaborator

Maintainer Verification Report

Reviewer: @penwenshao (local build & test)
Branch: fix/distinguish-auto-approval-mode-uimain
Environment: macOS Darwin 25.4.0, Node.js, TypeScript 5.8.3


1. TypeScript Type Check

Package Result Notes
packages/cli ✅ Pass 30 pre-existing TS errors in unrelated files (ToolConfirmationMessage.tsx, useContextualTips.ts, types.ts, errors.ts). Zero errors in PR-modified files (approvalModeVisuals.ts, AutoAcceptIndicator.tsx, InputPrompt.tsx, AgentComposer.tsx)

2. Lint

Scope Result
All 4 modified + 1 new source file ✅ No lint errors

3. Unit Tests

Test Suite Tests Result
approvalModeVisuals.test.ts 3 passed ✅ All pass
InputPrompt.test.tsx 150 passed, 1 skipped ✅ All pass

Total: 153 tests passed, 0 failed

4. Code Review

Design: ✅ Clean extraction

  • New approvalModeVisuals.ts centralizes all approval-mode color/style logic into two pure functions (getApprovalModeIndicatorColor and getApprovalModePromptStyle)
  • Both InputPrompt.tsx (main) and AgentComposer.tsx (agent-view) consume the same shared mapping — no visual drift between the two prompts
  • AutoAcceptIndicator.tsx correctly delegates color selection to the shared function

Visual mapping correctness:

Mode Indicator Color Prompt Color Prefix Before PR
PLAN success (green) > Same
AUTO_EDIT warning (yellow) warningDim > Same
AUTO text.link (blue) text.link (blue) > Was warning (yellow) — now distinct
YOLO error (red) errorDim * Same
DEFAULT > Same
  • Key fix: AUTO (classifier-evaluated) now uses blue (theme.text.link) instead of sharing yellow with AUTO_EDIT — visually distinguishable

Test coverage: ✅

  • New approvalModeVisuals.test.ts covers all three non-trivial modes (AUTO_EDIT vs AUTO vs YOLO) with distinct color/prefix/label assertions — regression guard for the exact bug being fixed

Return type: The getApprovalModePromptStyle return type uses a discriminated union (prefix: '>' | '*') with optional color and label — clean and type-safe


Verdict: ✅ Ready to merge

Focused, well-scoped fix with proper test coverage. Resolves #4575 by giving classifier AUTO mode a distinct blue visual identity separate from AUTO_EDIT yellow.

@he-yufeng

Copy link
Copy Markdown
Contributor Author

Updated the branch for the review feedback: added the missing Auto mode translation key across the existing locale files, removed the label field from the shared approval-mode visual helper, and kept the status-text mapping local to InputPrompt. I left the PLAN border behavior as-is because this PR is intentionally aligning AgentComposer with the existing InputPrompt behavior.

Validation run locally:

  • npm run test --workspace=packages/cli -- approvalModeVisuals.test.ts InputPrompt.test.tsx
  • npm run typecheck --workspace=packages/cli
  • npm run check-i18n --workspace=packages/cli
  • npx eslint packages/cli/src/ui/components/approvalModeVisuals.ts packages/cli/src/ui/components/approvalModeVisuals.test.ts packages/cli/src/ui/components/InputPrompt.tsx packages/cli/src/i18n/locales/en.js packages/cli/src/i18n/locales/zh.js packages/cli/src/i18n/locales/zh-TW.js packages/cli/src/i18n/locales/ja.js packages/cli/src/i18n/locales/de.js packages/cli/src/i18n/locales/fr.js packages/cli/src/i18n/locales/pt.js packages/cli/src/i18n/locales/ru.js packages/cli/src/i18n/locales/ca.js --max-warnings 0

: isAutoAccept
? theme.status.warningDim
: undefined;
const approvalModePromptStyle = getApprovalModePromptStyle(agentApprovalMode);

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] PLAN mode silently loses visual styling in sub-agent views — behavioral regression.

The old code used agentApprovalMode !== ApprovalMode.DEFAULT as the guard, which gave PLAN mode theme.status.warningDim for both border and prefix. The new getApprovalModePromptStyle(PLAN) returns { prefix: '>' } with no color, making PLAN visually identical to DEFAULT in AgentComposer. Meanwhile, AutoAcceptIndicator still shows PLAN as green (theme.status.success), creating conflicting visual signals.

If this is intentional (PLAN shouldn't have warning-style coloring), consider adding a test that asserts getApprovalModePromptStyle(ApprovalMode.PLAN) has no color to lock in the intent. Otherwise, add PLAN to the shared function:

Suggested change
const approvalModePromptStyle = getApprovalModePromptStyle(agentApprovalMode);
const approvalModePromptStyle = getApprovalModePromptStyle(agentApprovalMode);
const statusColor = approvalModePromptStyle.color;
// Note: PLAN mode now renders identically to DEFAULT — confirm this is intended.

— qwen3.7-max via Qwen Code /review

case ApprovalMode.AUTO_EDIT:
return { color: theme.status.warningDim, prefix: '>' };
case ApprovalMode.AUTO:
return { color: theme.text.link, prefix: '>' };

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] AUTO uses full-brightness theme.text.link (AccentBlue) for the prompt, breaking the dim-for-prompt / bright-for-indicator pattern and colliding with border.focused.

Every other mode uses dim tokens for prompts: AUTO_EDIT → status.warningDim (AccentYellowDim), YOLO → status.errorDim (AccentRedDim). AUTO uses text.link which resolves to the same AccentBlue as border.focused across all three themes. This makes the AUTO-mode input border visually indistinguishable from the DEFAULT-mode focused border.

Consider adding an AccentBlueDim token to the theme palette and using it here for consistency:

case ApprovalMode.AUTO:
  return { color: theme.status.infoDim /* new AccentBlueDim token */, prefix: '>' };

— qwen3.7-max via Qwen Code /review

@@ -0,0 +1,44 @@
/**
* @license
* Copyright 2025 Google LLC

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.

Copyright header says "Google LLC" — should be "Qwen Team" to match the rest of the project (e.g. authMethods.ts).

@@ -0,0 +1,42 @@
/**
* @license
* Copyright 2025 Google LLC

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.

Same here — "Google LLC" → "Qwen Team".

@yiliang114

Copy link
Copy Markdown
Collaborator

This PR changes user-visible TUI components (InputPrompt, AutoAcceptIndicator, AgentComposer) and i18n strings — could you add a before/after screenshot or short terminal recording showing the old vs new indicator styling? Per our PR template, UI changes need visual evidence so reviewers can verify without pulling locally.

The test coverage for the mapping logic looks fine, but a quick screenshot of the actual rendered difference would help a lot here.

@he-yufeng

Copy link
Copy Markdown
Contributor Author

Added a before/after terminal-style visual for the approval-mode indicators:

AUTO mode visual evidence

Direct link: https://gist.githubusercontent.com/he-yufeng/08198a2e0dc8954bb5407525e581a427/raw/6c630d808a360f5c6443f62d5d5e2611878bed3f/qwen-code-4600-visual-evidence.svg

This compares the previous upstream/main mapping with the current PR branch mapping under the default dark theme. The visible difference is that AUTO no longer shares the warning / warningDim path with AUTO_EDIT; the footer indicator and prompt styling now use ext.link blue for AUTO, while AUTO_EDIT remains yellow and YOLO remains red.

@tanzhenxin tanzhenxin added the type/bug Something isn't working as expected label Jun 2, 2026
@he-yufeng

Copy link
Copy Markdown
Contributor Author

Fixed the two copyright headers pointed out in review: approvalModeVisuals.ts and approvalModeVisuals.test.ts now use Qwen Team.

Validation:

  • npm run test --workspace=packages/cli -- approvalModeVisuals.test.ts
  • npx eslint packages/cli/src/ui/components/approvalModeVisuals.ts packages/cli/src/ui/components/approvalModeVisuals.test.ts --max-warnings 0
  • git diff --check

@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 new issues found in this round. LGTM! ✅ — qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Verification Report — PR #4600

Commit: ca884e441 (chore: fix approval visuals copyright header)
Base: main
Tester: wenshao
Date: 2026-06-03


Test Results

Check Result Details
Unit tests (approvalModeVisuals, InputPrompt) PASS 2 files, 153 passed + 1 skipped
TypeScript packages/core (tsc --noEmit) PASS 0 errors
TypeScript packages/cli (tsc --noEmit) WARN 1 error in Session.ts:1970 — see note below
ESLint (--max-warnings 0) PASS 0 warnings, 0 errors (5 files)
Build (packages/core + packages/cli) PASS Successfully copied files
git diff --check PASS No whitespace errors

CLI Typecheck Note

The Session.ts:1970 error ('skipClassifier' does not exist in type 'EvaluateAutoModeInput') is not introduced by this PRSession.ts is not in the PR diff. The error comes from PR #4572 (auto-mode-harden) changes that were merged into this branch but are not yet on main. Once PR #4572 lands, this error resolves. The PR's own changed files (approvalModeVisuals.ts, AutoAcceptIndicator.tsx, InputPrompt.tsx, AgentComposer.tsx) have no type errors.

Test File Breakdown

File Tests Time
ui/components/InputPrompt.test.tsx 147 (+1 skipped) 46s
ui/components/approvalModeVisuals.test.ts 6 5ms

Execution Environment

  • Method: tmux parallel execution (4 windows: unit tests, typecheck, lint, build)
  • Node: v22.17.0
  • Vitest: v3.2.4
  • Platform: macOS Darwin 25.4.0

Verdict

All checks pass for the PR's own scope. 153 tests cover InputPrompt and approvalModeVisuals with no regressions. The CLI typecheck error is a cross-branch dependency on PR #4572, not a defect in this PR. Recommend merging after or together with PR #4572.

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

Approving — clean, parity-preserving refactor that distinguishes the auto-approval mode indicators as intended, and full CI is green. Thanks @he-yufeng!

@tanzhenxin tanzhenxin merged commit da4dad5 into QwenLM:main Jun 3, 2026
10 checks passed
BenGuanRan pushed a commit to BenGuanRan/qwen-code that referenced this pull request Jun 11, 2026
QwenLM#4600 unified the approval-mode prompt styling into approvalModePromptStyle and removed the showAutoAcceptStyling / showYoloStyling constants, but left a dangling showYoloStyling reference in the prefixWidth calculation. This broke the tsc build (TS2304: Cannot find name showYoloStyling). npm run bundle (esbuild) skips type-checking, so the break only surfaced under npm run build / tsc --build.

Replace it with approvalMode === ApprovalMode.YOLO, matching how the rest of the file branches on approval mode after QwenLM#4600. Both ternary arms stay 2 because every approval-mode prefix is a single char (getApprovalModePromptStyle returns prefix > or *), so the rendered prefix width and cursor positioning are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

auto-mode and auto-accept edits share the same indicator color with no visual distinction

6 participants