mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-01-23 17:26:23 +00:00
Compare commits
4 Commits
v0.8.0-pre
...
fix/edit-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8106e094e8 | ||
|
|
2818bb604d | ||
|
|
571d2bd762 | ||
|
|
317d0af50e |
12
package-lock.json
generated
12
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -431,6 +431,7 @@ export const App: React.FC = () => {
|
||||
type: 'permissionResponse',
|
||||
data: { optionId },
|
||||
});
|
||||
|
||||
setPermissionRequest(null);
|
||||
},
|
||||
[vscode],
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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: (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user