From 79bc668b71a0c4bf32d0303928d914dcef8491a3 Mon Sep 17 00:00:00 2001
From: DennisYu07 <617072224@qq.com>
Date: Wed, 8 Jul 2026 11:25:32 +0800
Subject: [PATCH] feat(cli): Show permission mode badge in footer for DEFAULT
mode (#6498)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Always display the current permission/approval mode in the footer,
including when in the default (Ask permissions) mode. Previously,
non-default modes showed indicators but the default mode showed nothing,
creating ambiguity for users switching between modes.
- Add grey ⏸ badge with 'Ask permissions' text for DEFAULT mode
- Update both main Footer and AgentFooter to render DEFAULT indicator
- Use theme.text.secondary for subtle, unobtrusive styling
- Badge is i18n-aware using existing t('Ask permissions') key
- Other mode indicators remain unchanged
Closes #6496
---
.../components/AutoAcceptIndicator.test.tsx | 49 +++++++++++++++++++
.../src/ui/components/AutoAcceptIndicator.tsx | 3 ++
.../cli/src/ui/components/Footer.test.tsx | 14 ++++++
packages/cli/src/ui/components/Footer.tsx | 4 +-
.../ui/components/agent-view/AgentFooter.tsx | 5 +-
.../src/ui/components/approvalModeVisuals.ts | 1 +
6 files changed, 70 insertions(+), 6 deletions(-)
create mode 100644 packages/cli/src/ui/components/AutoAcceptIndicator.test.tsx
diff --git a/packages/cli/src/ui/components/AutoAcceptIndicator.test.tsx b/packages/cli/src/ui/components/AutoAcceptIndicator.test.tsx
new file mode 100644
index 0000000000..e9f58f49ad
--- /dev/null
+++ b/packages/cli/src/ui/components/AutoAcceptIndicator.test.tsx
@@ -0,0 +1,49 @@
+/**
+ * @license
+ * Copyright 2025 Qwen Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { render } from 'ink-testing-library';
+import { describe, it, expect } from 'vitest';
+import { AutoAcceptIndicator } from './AutoAcceptIndicator.js';
+import { ApprovalMode } from '@qwen-code/qwen-code-core';
+
+describe('', () => {
+ it('renders DEFAULT mode with pause badge and Ask permissions text', () => {
+ const { lastFrame } = render(
+ ,
+ );
+ const frame = lastFrame()!;
+ expect(frame).toContain('⏸');
+ expect(frame).toContain('Ask permissions');
+ });
+
+ it('renders PLAN mode indicator', () => {
+ const { lastFrame } = render(
+ ,
+ );
+ expect(lastFrame()).toContain('plan mode');
+ });
+
+ it('renders AUTO_EDIT mode indicator', () => {
+ const { lastFrame } = render(
+ ,
+ );
+ expect(lastFrame()).toContain('auto-accept edits');
+ });
+
+ it('renders AUTO mode indicator', () => {
+ const { lastFrame } = render(
+ ,
+ );
+ expect(lastFrame()).toContain('auto mode (classifier-evaluated)');
+ });
+
+ it('renders YOLO mode indicator', () => {
+ const { lastFrame } = render(
+ ,
+ );
+ expect(lastFrame()).toContain('YOLO mode');
+ });
+});
diff --git a/packages/cli/src/ui/components/AutoAcceptIndicator.tsx b/packages/cli/src/ui/components/AutoAcceptIndicator.tsx
index 60ffd126a9..caa9234c17 100644
--- a/packages/cli/src/ui/components/AutoAcceptIndicator.tsx
+++ b/packages/cli/src/ui/components/AutoAcceptIndicator.tsx
@@ -45,6 +45,9 @@ export const AutoAcceptIndicator: React.FC = ({
subText = cycleText;
break;
case ApprovalMode.DEFAULT:
+ textContent = `⏸ ${t('Ask permissions')}`;
+ subText = cycleText;
+ break;
default:
break;
}
diff --git a/packages/cli/src/ui/components/Footer.test.tsx b/packages/cli/src/ui/components/Footer.test.tsx
index ba71e4527a..66321a80e3 100644
--- a/packages/cli/src/ui/components/Footer.test.tsx
+++ b/packages/cli/src/ui/components/Footer.test.tsx
@@ -8,6 +8,7 @@ import { render } from 'ink-testing-library';
import { act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { Footer } from './Footer.js';
+import { ApprovalMode } from '@qwen-code/qwen-code-core';
import * as useTerminalSize from '../hooks/useTerminalSize.js';
import * as useStatusLineModule from '../hooks/useStatusLine.js';
import { type UIState, UIStateContext } from '../contexts/UIStateContext.js';
@@ -233,6 +234,19 @@ describe('', () => {
}
});
+ it('shows the default approval mode badge when in DEFAULT mode', () => {
+ const { lastFrame } = renderWithWidth(
+ 120,
+ createMockUIState({
+ showAutoAcceptIndicator: ApprovalMode.DEFAULT,
+ }),
+ );
+ const frame = lastFrame()!;
+ expect(frame).toContain('⏸');
+ expect(frame).toContain('Ask permissions');
+ expect(frame).not.toContain('? for shortcuts');
+ });
+
it('does not display the working directory or branch name', () => {
const { lastFrame } = renderWithWidth(120, createMockUIState());
expect(lastFrame()).not.toMatch(/\(.*\*\)/);
diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx
index a0843c3fb9..85f55e5497 100644
--- a/packages/cli/src/ui/components/Footer.tsx
+++ b/packages/cli/src/ui/components/Footer.tsx
@@ -21,7 +21,6 @@ import { useUIState } from '../contexts/UIStateContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
import { useSettings } from '../contexts/SettingsContext.js';
import { useVimModeState } from '../contexts/VimModeContext.js';
-import { ApprovalMode } from '@qwen-code/qwen-code-core';
import { GeminiSpinner } from './GeminiRespondingSpinner.js';
import { GoalPill, useFooterGoalState } from './GoalPill.js';
import { CronPill, useFooterCronTaskCount } from './CronPill.js';
@@ -107,8 +106,7 @@ export const Footer: React.FC = () => {
message: uiState.startupIdeConnectionStatus.message,
})}
- ) : showAutoAcceptIndicator !== undefined &&
- showAutoAcceptIndicator !== ApprovalMode.DEFAULT ? (
+ ) : showAutoAcceptIndicator !== undefined ? (
) : suppressHint ? null : (
{t('? for shortcuts')}
diff --git a/packages/cli/src/ui/components/agent-view/AgentFooter.tsx b/packages/cli/src/ui/components/agent-view/AgentFooter.tsx
index 7b05e4e478..0f40c4ad77 100644
--- a/packages/cli/src/ui/components/agent-view/AgentFooter.tsx
+++ b/packages/cli/src/ui/components/agent-view/AgentFooter.tsx
@@ -12,7 +12,7 @@
import type React from 'react';
import { Box, Text } from 'ink';
-import { ApprovalMode } from '@qwen-code/qwen-code-core';
+import type { ApprovalMode } from '@qwen-code/qwen-code-core';
import { AutoAcceptIndicator } from '../AutoAcceptIndicator.js';
import { ContextUsageDisplay } from '../ContextUsageDisplay.js';
import { theme } from '../../semantic-colors.js';
@@ -30,8 +30,7 @@ export const AgentFooter: React.FC = ({
contextWindowSize,
terminalWidth,
}) => {
- const showApproval =
- approvalMode !== undefined && approvalMode !== ApprovalMode.DEFAULT;
+ const showApproval = approvalMode !== undefined;
const showContext = promptTokenCount > 0 && contextWindowSize !== undefined;
if (!showApproval && !showContext) {
diff --git a/packages/cli/src/ui/components/approvalModeVisuals.ts b/packages/cli/src/ui/components/approvalModeVisuals.ts
index 9a432871f4..46d8ad4d7d 100644
--- a/packages/cli/src/ui/components/approvalModeVisuals.ts
+++ b/packages/cli/src/ui/components/approvalModeVisuals.ts
@@ -20,6 +20,7 @@ export function getApprovalModeIndicatorColor(
case ApprovalMode.YOLO:
return theme.status.error;
case ApprovalMode.DEFAULT:
+ return theme.text.secondary;
default:
return undefined;
}