mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
refactor(vscode-ide-companion): update message handling and configuration
This commit is contained in:
@@ -378,7 +378,29 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
} catch (error) {
|
||||
console.error('[SessionMessageHandler] Error sending message:', error);
|
||||
|
||||
const err = error as unknown as Error;
|
||||
const errorMsg = String(error);
|
||||
const lower = errorMsg.toLowerCase();
|
||||
|
||||
// Suppress user-cancelled/aborted errors (ESC/Stop button)
|
||||
const isAbortLike =
|
||||
(err && (err as Error).name === 'AbortError') ||
|
||||
lower.includes('abort') ||
|
||||
lower.includes('aborted') ||
|
||||
lower.includes('request was aborted') ||
|
||||
lower.includes('canceled') ||
|
||||
lower.includes('cancelled') ||
|
||||
lower.includes('user_cancelled');
|
||||
|
||||
if (isAbortLike) {
|
||||
// Do not show VS Code error popup for intentional cancellations.
|
||||
// Ensure the webview knows the stream ended due to user action.
|
||||
this.sendToWebView({
|
||||
type: 'streamEnd',
|
||||
data: { timestamp: Date.now(), reason: 'user_cancelled' },
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Check for session not found error and handle it appropriately
|
||||
if (
|
||||
errorMsg.includes('Session not found') ||
|
||||
|
||||
@@ -105,7 +105,6 @@ export const useMessageHandling = () => {
|
||||
const endStreaming = useCallback(() => {
|
||||
// Finalize streaming; content already lives in the placeholder message
|
||||
setIsStreaming(false);
|
||||
setIsWaitingForResponse(false);
|
||||
streamingMessageIndexRef.current = null;
|
||||
// Remove the thinking message if it exists (collapse thoughts)
|
||||
setMessages((prev) => {
|
||||
|
||||
@@ -14,6 +14,8 @@ interface UseMessageSubmitProps {
|
||||
setInputText: (text: string) => void;
|
||||
inputFieldRef: React.RefObject<HTMLDivElement>;
|
||||
isStreaming: boolean;
|
||||
// When true, do NOT auto-attach the active editor file/selection to context
|
||||
skipAutoActiveContext?: boolean;
|
||||
|
||||
fileContext: {
|
||||
getFileReference: (fileName: string) => string | undefined;
|
||||
@@ -38,6 +40,7 @@ export const useMessageSubmit = ({
|
||||
setInputText,
|
||||
inputFieldRef,
|
||||
isStreaming,
|
||||
skipAutoActiveContext = false,
|
||||
fileContext,
|
||||
messageHandling,
|
||||
}: UseMessageSubmitProps) => {
|
||||
@@ -94,8 +97,8 @@ export const useMessageSubmit = ({
|
||||
}
|
||||
}
|
||||
|
||||
// Add active file selection context if present
|
||||
if (fileContext.activeFilePath) {
|
||||
// Add active file selection context if present and not skipped
|
||||
if (fileContext.activeFilePath && !skipAutoActiveContext) {
|
||||
const fileName = fileContext.activeFileName || 'current file';
|
||||
context.push({
|
||||
type: 'file',
|
||||
@@ -115,7 +118,11 @@ export const useMessageSubmit = ({
|
||||
}
|
||||
| undefined;
|
||||
|
||||
if (fileContext.activeFilePath && fileContext.activeFileName) {
|
||||
if (
|
||||
fileContext.activeFilePath &&
|
||||
fileContext.activeFileName &&
|
||||
!skipAutoActiveContext
|
||||
) {
|
||||
fileContextForMessage = {
|
||||
fileName: fileContext.activeFileName,
|
||||
filePath: fileContext.activeFilePath,
|
||||
@@ -146,6 +153,7 @@ export const useMessageSubmit = ({
|
||||
inputFieldRef,
|
||||
vscode,
|
||||
fileContext,
|
||||
skipAutoActiveContext,
|
||||
messageHandling,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -112,6 +112,9 @@ export const useWebViewMessages = ({
|
||||
}: UseWebViewMessagesProps) => {
|
||||
// VS Code API for posting messages back to the extension host
|
||||
const vscode = useVSCode();
|
||||
// Track active long-running tool calls (execute/bash/command) so we can
|
||||
// keep the bottom "waiting" message visible until all of them complete.
|
||||
const activeExecToolCallsRef = useRef<Set<string>>(new Set());
|
||||
// Use ref to store callbacks to avoid useEffect dependency issues
|
||||
const handlersRef = useRef({
|
||||
sessionManagement,
|
||||
@@ -260,6 +263,10 @@ export const useWebViewMessages = ({
|
||||
// no-op: stream might not have been started
|
||||
console.warn('[PanelManager] Failed to end streaming:', err);
|
||||
}
|
||||
// Important: Do NOT blindly clear the waiting message if there are
|
||||
// still active tool calls running. We keep the waiting indicator
|
||||
// tied to tool-call lifecycle instead.
|
||||
if (activeExecToolCallsRef.current.size === 0) {
|
||||
try {
|
||||
handlers.messageHandling.clearWaitingForResponse();
|
||||
} catch (err) {
|
||||
@@ -270,6 +277,7 @@ export const useWebViewMessages = ({
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -293,6 +301,11 @@ export const useWebViewMessages = ({
|
||||
case 'streamEnd':
|
||||
handlers.messageHandling.endStreaming();
|
||||
handlers.messageHandling.clearThinking();
|
||||
// Clear the generic waiting indicator only if there are no active
|
||||
// long-running tool calls. Otherwise, keep it visible.
|
||||
if (activeExecToolCallsRef.current.size === 0) {
|
||||
handlers.messageHandling.clearWaitingForResponse();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
@@ -439,7 +452,15 @@ export const useWebViewMessages = ({
|
||||
const kind = (toolCallData.kind || '').toString().toLowerCase();
|
||||
const isExec =
|
||||
kind === 'execute' || kind === 'bash' || kind === 'command';
|
||||
if (isExec && (status === 'pending' || status === 'in_progress')) {
|
||||
|
||||
if (isExec) {
|
||||
const id = (toolCallData.toolCallId || '').toString();
|
||||
|
||||
// Maintain the active set by status
|
||||
if (status === 'pending' || status === 'in_progress') {
|
||||
activeExecToolCallsRef.current.add(id);
|
||||
|
||||
// Build a helpful hint from rawInput
|
||||
const rawInput = toolCallData.rawInput;
|
||||
let cmd = '';
|
||||
if (typeof rawInput === 'string') {
|
||||
@@ -450,10 +471,15 @@ export const useWebViewMessages = ({
|
||||
}
|
||||
const hint = cmd ? `Running: ${cmd}` : 'Running command...';
|
||||
handlers.messageHandling.setWaitingForResponse(hint);
|
||||
} else if (status === 'completed' || status === 'failed') {
|
||||
activeExecToolCallsRef.current.delete(id);
|
||||
}
|
||||
if (status === 'completed' || status === 'failed') {
|
||||
|
||||
// If no active exec tool remains, clear the waiting message.
|
||||
if (activeExecToolCallsRef.current.size === 0) {
|
||||
handlers.messageHandling.clearWaitingForResponse();
|
||||
}
|
||||
}
|
||||
} catch (_err) {
|
||||
// Best-effort UI hint; ignore errors
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
export function isDevelopmentMode(): boolean {
|
||||
// TODO: 调试用
|
||||
return false;
|
||||
// return (
|
||||
// process.env.NODE_ENV === 'development' ||
|
||||
// process.env.DEBUG === 'true' ||
|
||||
// process.env.NODE_ENV !== 'production'
|
||||
// );
|
||||
// return false;
|
||||
return (
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.DEBUG === 'true' ||
|
||||
process.env.NODE_ENV !== 'production'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,10 +34,16 @@ export default {
|
||||
'0%, 100%': { opacity: '1' },
|
||||
'50%': { opacity: '0.5' },
|
||||
},
|
||||
// PermissionDrawer enter animation: slide up from bottom
|
||||
'slide-up': {
|
||||
'0%': { transform: 'translateY(100%)', opacity: '0' },
|
||||
'100%': { transform: 'translateY(0)', opacity: '1' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'completion-menu-enter': 'completion-menu-enter 150ms ease-out both',
|
||||
'pulse-slow': 'pulse-slow 1.5s ease-in-out infinite',
|
||||
'slide-up': 'slide-up 200ms ease-out both',
|
||||
},
|
||||
colors: {
|
||||
qwen: {
|
||||
|
||||
Reference in New Issue
Block a user