mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-22 17:57:46 +00:00
refactor(vscode-ide-companion): introduce ApprovalMode enum and improve mode handling
- Create new approvalModeTypes.ts with ApprovalMode enum and helper functions - Replace hardcoded string literals with ApprovalModeValue type - Consolidate mode mapping logic using new helper functions
This commit is contained in:
@@ -42,7 +42,8 @@ import {
|
||||
import { InputForm } from './components/layout/InputForm.js';
|
||||
import { SessionSelector } from './components/layout/SessionSelector.js';
|
||||
import { FileIcon, UserIcon } from './components/icons/index.js';
|
||||
import type { EditMode } from '../types/chatTypes.js';
|
||||
import { ApprovalMode } from '../types/acpTypes.js';
|
||||
import type { ApprovalModeValue } from '../types/acpTypes.js';
|
||||
import type { PlanEntry } from '../types/chatTypes.js';
|
||||
|
||||
export const App: React.FC = () => {
|
||||
@@ -77,7 +78,9 @@ export const App: React.FC = () => {
|
||||
null,
|
||||
) as React.RefObject<HTMLDivElement>;
|
||||
|
||||
const [editMode, setEditMode] = useState<EditMode>('ask');
|
||||
const [editMode, setEditMode] = useState<ApprovalModeValue>(
|
||||
ApprovalMode.DEFAULT,
|
||||
);
|
||||
const [thinkingEnabled, setThinkingEnabled] = useState(false);
|
||||
const [isComposing, setIsComposing] = useState(false);
|
||||
// When true, do NOT auto-attach the active editor file/selection to message context
|
||||
@@ -461,30 +464,22 @@ export const App: React.FC = () => {
|
||||
});
|
||||
}, [vscode]);
|
||||
|
||||
// Handle toggle edit mode (Ask -> Auto -> Plan -> YOLO -> Ask)
|
||||
// Handle toggle edit mode (Default -> Auto-edit -> Plan -> YOLO -> Default)
|
||||
const handleToggleEditMode = useCallback(() => {
|
||||
setEditMode((prev) => {
|
||||
const next: EditMode =
|
||||
prev === 'ask'
|
||||
? 'auto'
|
||||
: prev === 'auto'
|
||||
? 'plan'
|
||||
: prev === 'plan'
|
||||
? 'yolo'
|
||||
: 'ask';
|
||||
const next: ApprovalModeValue =
|
||||
prev === ApprovalMode.DEFAULT
|
||||
? ApprovalMode.AUTO_EDIT
|
||||
: prev === ApprovalMode.AUTO_EDIT
|
||||
? ApprovalMode.PLAN
|
||||
: prev === ApprovalMode.PLAN
|
||||
? ApprovalMode.YOLO
|
||||
: ApprovalMode.DEFAULT;
|
||||
// Notify extension to set approval mode via ACP
|
||||
try {
|
||||
const toAcp =
|
||||
next === 'plan'
|
||||
? 'plan'
|
||||
: next === 'auto'
|
||||
? 'auto-edit'
|
||||
: next === 'yolo'
|
||||
? 'yolo'
|
||||
: 'default';
|
||||
vscode.postMessage({
|
||||
type: 'setApprovalMode',
|
||||
data: { modeId: toAcp },
|
||||
data: { modeId: next },
|
||||
});
|
||||
} catch {
|
||||
/* no-op */
|
||||
|
||||
@@ -15,7 +15,7 @@ import { MessageHandler } from '../webview/MessageHandler.js';
|
||||
import { WebViewContent } from '../webview/WebViewContent.js';
|
||||
import { CliInstaller } from '../cli/cliInstaller.js';
|
||||
import { getFileName } from './utils/webviewUtils.js';
|
||||
import { authMethod } from '../types/acpTypes.js';
|
||||
import { authMethod, type ApprovalModeValue } from '../types/acpTypes.js';
|
||||
|
||||
export class WebViewProvider {
|
||||
private panelManager: PanelManager;
|
||||
@@ -31,8 +31,7 @@ export class WebViewProvider {
|
||||
private pendingPermissionRequest: AcpPermissionRequest | null = null;
|
||||
private pendingPermissionResolve: ((optionId: string) => void) | null = null;
|
||||
// Track current ACP mode id to influence permission/diff behavior
|
||||
private currentModeId: 'plan' | 'default' | 'auto-edit' | 'yolo' | null =
|
||||
null;
|
||||
private currentModeId: ApprovalModeValue | null = null;
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext,
|
||||
@@ -885,7 +884,7 @@ export class WebViewProvider {
|
||||
}
|
||||
|
||||
/** Get current ACP mode id (if known). */
|
||||
getCurrentModeId(): 'plan' | 'default' | 'auto-edit' | 'yolo' | null {
|
||||
getCurrentModeId(): ApprovalModeValue | null {
|
||||
return this.currentModeId;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
} from '../icons/index.js';
|
||||
import { CompletionMenu } from '../layout/CompletionMenu.js';
|
||||
import type { CompletionItem } from '../../../types/completionItemTypes.js';
|
||||
import type { EditMode } from '../../../types/chatTypes.js';
|
||||
import { getApprovalModeInfoFromString } from '../../../types/acpTypes.js';
|
||||
import type { ApprovalModeValue } from '../../../types/acpTypes.js';
|
||||
|
||||
interface InputFormProps {
|
||||
inputText: string;
|
||||
@@ -29,7 +30,7 @@ interface InputFormProps {
|
||||
isStreaming: boolean;
|
||||
isWaitingForResponse: boolean;
|
||||
isComposing: boolean;
|
||||
editMode: EditMode;
|
||||
editMode: ApprovalModeValue;
|
||||
thinkingEnabled: boolean;
|
||||
activeFileName: string | null;
|
||||
activeSelection: { startLine: number; endLine: number } | null;
|
||||
@@ -53,41 +54,35 @@ interface InputFormProps {
|
||||
onCompletionClose?: () => void;
|
||||
}
|
||||
|
||||
// Get edit mode display info
|
||||
const getEditModeInfo = (editMode: EditMode) => {
|
||||
switch (editMode) {
|
||||
case 'ask':
|
||||
return {
|
||||
text: 'Ask before edits',
|
||||
title: 'Qwen will ask before each edit. Click to switch modes.',
|
||||
icon: <EditPencilIcon />,
|
||||
};
|
||||
// Get edit mode display info using helper function
|
||||
const getEditModeInfo = (editMode: ApprovalModeValue) => {
|
||||
const info = getApprovalModeInfoFromString(editMode);
|
||||
|
||||
// Map icon types to actual icons
|
||||
let icon = null;
|
||||
switch (info.iconType) {
|
||||
case 'edit':
|
||||
icon = <EditPencilIcon />;
|
||||
break;
|
||||
case 'auto':
|
||||
return {
|
||||
text: 'Edit automatically',
|
||||
title: 'Qwen will edit files automatically. Click to switch modes.',
|
||||
icon: <AutoEditIcon />,
|
||||
};
|
||||
icon = <AutoEditIcon />;
|
||||
break;
|
||||
case 'plan':
|
||||
return {
|
||||
text: 'Plan mode',
|
||||
title: 'Qwen will plan before executing. Click to switch modes.',
|
||||
icon: <PlanModeIcon />,
|
||||
};
|
||||
icon = <PlanModeIcon />;
|
||||
break;
|
||||
case 'yolo':
|
||||
return {
|
||||
text: 'YOLO',
|
||||
title: 'Automatically approve all tools. Click to switch modes.',
|
||||
// Reuse Auto icon for simplicity; can swap to a distinct icon later.
|
||||
icon: <AutoEditIcon />,
|
||||
};
|
||||
icon = <AutoEditIcon />;
|
||||
break;
|
||||
default:
|
||||
return {
|
||||
text: 'Unknown mode',
|
||||
title: 'Unknown edit mode',
|
||||
icon: null,
|
||||
};
|
||||
icon = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
text: info.label,
|
||||
title: info.title,
|
||||
icon,
|
||||
};
|
||||
};
|
||||
|
||||
export const InputForm: React.FC<InputFormProps> = ({
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* MessageContent component - renders message with code highlighting and clickable file paths
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
@@ -14,9 +12,6 @@ interface MessageContentProps {
|
||||
onFileClick?: (filePath: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* MessageContent component - renders message content with markdown support
|
||||
*/
|
||||
export const MessageContent: React.FC<MessageContentProps> = ({
|
||||
content,
|
||||
onFileClick,
|
||||
|
||||
@@ -37,12 +37,5 @@ export const ThinkingMessage: React.FC<ThinkingMessageProps> = ({
|
||||
</span>
|
||||
<MessageContent content={content} onFileClick={onFileClick} />
|
||||
</div>
|
||||
{/* Timestamp - temporarily hidden */}
|
||||
{/* <div
|
||||
className="text-xs opacity-60"
|
||||
style={{ color: 'var(--app-secondary-foreground)' }}
|
||||
>
|
||||
{new Date(timestamp).toLocaleTimeString()}
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,8 @@ import type {
|
||||
PermissionOption,
|
||||
ToolCall as PermissionToolCall,
|
||||
} from '../components/PermissionDrawer/PermissionRequest.js';
|
||||
import type { ToolCallUpdate, EditMode } from '../../types/chatTypes.js';
|
||||
import type { ToolCallUpdate } from '../../types/chatTypes.js';
|
||||
import type { ApprovalModeValue } from '../../types/acpTypes.js';
|
||||
import type { PlanEntry } from '../../types/chatTypes.js';
|
||||
|
||||
interface UseWebViewMessagesProps {
|
||||
@@ -107,7 +108,7 @@ interface UseWebViewMessagesProps {
|
||||
inputFieldRef: React.RefObject<HTMLDivElement>;
|
||||
setInputText: (text: string) => void;
|
||||
// Edit mode setter (maps ACP modes to UI modes)
|
||||
setEditMode?: (mode: EditMode) => void;
|
||||
setEditMode?: (mode: ApprovalModeValue) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,20 +197,9 @@ export const useWebViewMessages = ({
|
||||
case 'modeInfo': {
|
||||
// Initialize UI mode from ACP initialize
|
||||
try {
|
||||
const current = (message.data?.currentModeId || 'default') as
|
||||
| 'plan'
|
||||
| 'default'
|
||||
| 'auto-edit'
|
||||
| 'yolo';
|
||||
setEditMode?.(
|
||||
(current === 'plan'
|
||||
? 'plan'
|
||||
: current === 'auto-edit'
|
||||
? 'auto'
|
||||
: current === 'yolo'
|
||||
? 'yolo'
|
||||
: 'ask') as EditMode,
|
||||
);
|
||||
const current = (message.data?.currentModeId ||
|
||||
'default') as ApprovalModeValue;
|
||||
setEditMode?.(current);
|
||||
} catch (_error) {
|
||||
// best effort
|
||||
}
|
||||
@@ -218,20 +208,9 @@ export const useWebViewMessages = ({
|
||||
|
||||
case 'modeChanged': {
|
||||
try {
|
||||
const modeId = (message.data?.modeId || 'default') as
|
||||
| 'plan'
|
||||
| 'default'
|
||||
| 'auto-edit'
|
||||
| 'yolo';
|
||||
setEditMode?.(
|
||||
(modeId === 'plan'
|
||||
? 'plan'
|
||||
: modeId === 'auto-edit'
|
||||
? 'auto'
|
||||
: modeId === 'yolo'
|
||||
? 'yolo'
|
||||
: 'ask') as EditMode,
|
||||
);
|
||||
const modeId = (message.data?.modeId ||
|
||||
'default') as ApprovalModeValue;
|
||||
setEditMode?.(modeId);
|
||||
} catch (_error) {
|
||||
// Ignore error when setting mode
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user