Compare commits

..

4 Commits

12 changed files with 52 additions and 106 deletions

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@qwen-code/qwen-code",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@qwen-code/qwen-code",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"workspaces": [
"packages/*"
],
@@ -17343,7 +17343,7 @@
},
"packages/cli": {
"name": "@qwen-code/qwen-code",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"dependencies": {
"@google/genai": "1.30.0",
"@iarna/toml": "^2.2.5",
@@ -17977,7 +17977,7 @@
},
"packages/core": {
"name": "@qwen-code/qwen-code-core",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"hasInstallScript": true,
"dependencies": {
"@anthropic-ai/sdk": "^0.36.1",
@@ -21442,7 +21442,7 @@
},
"packages/test-utils": {
"name": "@qwen-code/qwen-code-test-utils",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"dev": true,
"license": "Apache-2.0",
"devDependencies": {
@@ -21454,7 +21454,7 @@
},
"packages/vscode-ide-companion": {
"name": "qwen-code-vscode-ide-companion",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"license": "LICENSE",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.25.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"engines": {
"node": ">=20.0.0"
},
@@ -13,7 +13,7 @@
"url": "git+https://github.com/QwenLM/qwen-code.git"
},
"config": {
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.8.0-preview.1"
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.8.0"
},
"scripts": {
"start": "cross-env node scripts/start.js",

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"description": "Qwen Code",
"repository": {
"type": "git",
@@ -33,7 +33,7 @@
"dist"
],
"config": {
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.8.0-preview.1"
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.8.0"
},
"dependencies": {
"@google/genai": "1.30.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code-core",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"description": "Qwen Code Core",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code-test-utils",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"private": true,
"main": "src/index.ts",
"license": "Apache-2.0",

View File

@@ -2,7 +2,7 @@
"name": "qwen-code-vscode-ide-companion",
"displayName": "Qwen Code Companion",
"description": "Enable Qwen Code with direct access to your VS Code workspace.",
"version": "0.8.0-preview.1",
"version": "0.8.0",
"publisher": "qwenlm",
"icon": "assets/icon.png",
"repository": {

View File

@@ -0,0 +1,14 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
export interface PermissionResponsePayload {
optionId: string;
}
export interface PermissionResponseMessage {
type: string;
data: PermissionResponsePayload;
}

View File

@@ -431,6 +431,7 @@ export const App: React.FC = () => {
type: 'permissionResponse',
data: { optionId },
});
setPermissionRequest(null);
},
[vscode],

View File

@@ -6,6 +6,7 @@
import type { QwenAgentManager } from '../services/qwenAgentManager.js';
import type { ConversationStore } from '../services/conversationStore.js';
import type { PermissionResponseMessage } from '../types/webviewMessageTypes.js';
import { MessageRouter } from './handlers/MessageRouter.js';
/**
@@ -55,7 +56,7 @@ export class MessageHandler {
* Set permission handler
*/
setPermissionHandler(
handler: (message: { type: string; data: { optionId: string } }) => void,
handler: (message: PermissionResponseMessage) => void,
): void {
this.router.setPermissionHandler(handler);
}

View File

@@ -8,6 +8,7 @@ import * as vscode from 'vscode';
import { QwenAgentManager } from '../services/qwenAgentManager.js';
import { ConversationStore } from '../services/conversationStore.js';
import type { AcpPermissionRequest } from '../types/acpTypes.js';
import type { PermissionResponseMessage } from '../types/webviewMessageTypes.js';
import { PanelManager } from '../webview/PanelManager.js';
import { MessageHandler } from '../webview/MessageHandler.js';
import { WebViewContent } from '../webview/WebViewContent.js';
@@ -251,10 +252,7 @@ export class WebViewProvider {
}
}
};
const handler = (message: {
type: string;
data: { optionId: string };
}) => {
const handler = (message: PermissionResponseMessage) => {
if (message.type !== 'permissionResponse') {
return;
}
@@ -270,6 +268,16 @@ export class WebViewProvider {
optionId.toLowerCase().includes('reject');
if (isCancel) {
// Close any open qwen-diff editors first
try {
void vscode.commands.executeCommand('qwen.diff.closeAll');
} catch (err) {
console.warn(
'[WebViewProvider] Failed to close diffs after reject:',
err,
);
}
// Fire and forget do not block the ACP resolve
(async () => {
try {
@@ -296,7 +304,6 @@ export class WebViewProvider {
const title =
(request.toolCall as { title?: string } | undefined)
?.title || '';
// Normalize kind for UI fall back to 'execute'
let kind = ((
request.toolCall as { kind?: string } | undefined
)?.kind || 'execute') as string;
@@ -319,7 +326,6 @@ export class WebViewProvider {
title,
kind,
status: 'failed',
// Best-effort pass-through (used by UI hints)
rawInput: (request.toolCall as { rawInput?: unknown })
?.rawInput,
locations: (

View File

@@ -24,10 +24,7 @@ export const PermissionDrawer: React.FC<PermissionDrawerProps> = ({
onClose,
}) => {
const [focusedIndex, setFocusedIndex] = useState(0);
const [customMessage, setCustomMessage] = useState('');
const containerRef = useRef<HTMLDivElement>(null);
// Correct the ref type for custom input to HTMLInputElement to avoid subsequent forced casting
const customInputRef = useRef<HTMLInputElement>(null);
console.log('PermissionDrawer rendered with isOpen:', isOpen, toolCall);
// Prefer file name from locations, fall back to content[].path if present
@@ -94,10 +91,7 @@ export const PermissionDrawer: React.FC<PermissionDrawerProps> = ({
// Number keys 1-9 for quick select
const numMatch = e.key.match(/^[1-9]$/);
if (
numMatch &&
!customInputRef.current?.contains(document.activeElement)
) {
if (numMatch) {
const index = parseInt(e.key, 10) - 1;
if (index < options.length) {
e.preventDefault();
@@ -109,7 +103,10 @@ export const PermissionDrawer: React.FC<PermissionDrawerProps> = ({
// Arrow keys for navigation
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
const totalItems = options.length + 1; // +1 for custom input
if (options.length === 0) {
return;
}
const totalItems = options.length;
if (e.key === 'ArrowDown') {
setFocusedIndex((prev) => (prev + 1) % totalItems);
} else {
@@ -118,10 +115,7 @@ export const PermissionDrawer: React.FC<PermissionDrawerProps> = ({
}
// Enter to select
if (
e.key === 'Enter' &&
!customInputRef.current?.contains(document.activeElement)
) {
if (e.key === 'Enter') {
e.preventDefault();
if (focusedIndex < options.length) {
onResponse(options[focusedIndex].optionId);
@@ -234,28 +228,6 @@ export const PermissionDrawer: React.FC<PermissionDrawerProps> = ({
</button>
);
})}
{/* Custom message input (extracted component) */}
{(() => {
const isFocused = focusedIndex === options.length;
const rejectOptionId = options.find((o) =>
o.kind.includes('reject'),
)?.optionId;
return (
<CustomMessageInputRow
isFocused={isFocused}
customMessage={customMessage}
setCustomMessage={setCustomMessage}
onFocusRow={() => setFocusedIndex(options.length)}
onSubmitReject={() => {
if (rejectOptionId) {
onResponse(rejectOptionId);
}
}}
inputRef={customInputRef}
/>
);
})()}
</div>
</div>
@@ -263,50 +235,3 @@ export const PermissionDrawer: React.FC<PermissionDrawerProps> = ({
</div>
);
};
/**
* CustomMessageInputRow: Reusable custom input row component (without hooks)
*/
interface CustomMessageInputRowProps {
isFocused: boolean;
customMessage: string;
setCustomMessage: (val: string) => void;
onFocusRow: () => void; // Set focus when mouse enters or input box is focused
onSubmitReject: () => void; // Triggered when Enter is pressed (selecting reject option)
inputRef: React.RefObject<HTMLInputElement | null>;
}
const CustomMessageInputRow: React.FC<CustomMessageInputRowProps> = ({
isFocused,
customMessage,
setCustomMessage,
onFocusRow,
onSubmitReject,
inputRef,
}) => (
<div
className={`flex items-center gap-2 px-2 py-1.5 text-left w-full box-border rounded-[4px] border-0 shadow-[inset_0_0_0_1px_var(--app-transparent-inner-border)] cursor-text text-[var(--app-primary-foreground)] ${
isFocused ? 'text-[var(--app-list-active-foreground)]' : ''
}`}
onMouseEnter={onFocusRow}
onClick={() => inputRef.current?.focus()}
>
<input
ref={inputRef as React.LegacyRef<HTMLInputElement> | undefined}
type="text"
placeholder="Tell Qwen what to do instead"
spellCheck={false}
className="flex-1 bg-transparent border-0 outline-none text-sm placeholder:opacity-70"
style={{ color: 'var(--app-input-foreground)' }}
value={customMessage}
onChange={(e) => setCustomMessage(e.target.value)}
onFocus={onFocusRow}
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey && customMessage.trim()) {
e.preventDefault();
onSubmitReject();
}
}}
/>
</div>
);

View File

@@ -7,6 +7,7 @@
import type { IMessageHandler } from './BaseMessageHandler.js';
import type { QwenAgentManager } from '../../services/qwenAgentManager.js';
import type { ConversationStore } from '../../services/conversationStore.js';
import type { PermissionResponseMessage } from '../../types/webviewMessageTypes.js';
import { SessionMessageHandler } from './SessionMessageHandler.js';
import { FileMessageHandler } from './FileMessageHandler.js';
import { EditorMessageHandler } from './EditorMessageHandler.js';
@@ -22,7 +23,7 @@ export class MessageRouter {
private authHandler: AuthMessageHandler;
private currentConversationId: string | null = null;
private permissionHandler:
| ((message: { type: string; data: { optionId: string } }) => void)
| ((message: PermissionResponseMessage) => void)
| null = null;
constructor(
@@ -80,9 +81,7 @@ export class MessageRouter {
// Handle permission response specially
if (message.type === 'permissionResponse') {
if (this.permissionHandler) {
this.permissionHandler(
message as { type: string; data: { optionId: string } },
);
this.permissionHandler(message as PermissionResponseMessage);
}
return;
}
@@ -131,7 +130,7 @@ export class MessageRouter {
* Set permission handler
*/
setPermissionHandler(
handler: (message: { type: string; data: { optionId: string } }) => void,
handler: (message: PermissionResponseMessage) => void,
): void {
this.permissionHandler = handler;
}