diff --git a/packages/vscode-ide-companion/src/webview/App.scss b/packages/vscode-ide-companion/src/webview/App.scss
index 64abceed..d4f0cde1 100644
--- a/packages/vscode-ide-companion/src/webview/App.scss
+++ b/packages/vscode-ide-companion/src/webview/App.scss
@@ -532,76 +532,6 @@ button {
flex: 1;
}
-/* ===========================
- Info Banner (at bottom)
- =========================== */
-.info-banner {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
- padding: 12px 16px;
- background-color: var(--app-input-secondary-background);
- border-top: 1px solid var(--app-primary-border-color);
-}
-
-.banner-content {
- display: flex;
- align-items: center;
- gap: 12px;
- flex: 1;
- min-width: 0;
-}
-
-.banner-icon {
- flex-shrink: 0;
- width: 16px;
- height: 16px;
- fill: var(--app-primary-foreground);
-}
-
-.banner-content label {
- font-size: 13px;
- color: var(--app-primary-foreground);
- margin: 0;
- line-height: 1.4;
-}
-
-.banner-link {
- color: var(--app-qwen-orange);
- text-decoration: none;
- cursor: pointer;
-}
-
-.banner-link:hover {
- text-decoration: underline;
-}
-
-.banner-close {
- flex-shrink: 0;
- width: 20px;
- height: 20px;
- padding: 0;
- background: transparent;
- border: none;
- border-radius: var(--corner-radius-small);
- color: var(--app-primary-foreground);
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: background-color 0.2s;
-}
-
-.banner-close:hover {
- background-color: var(--app-ghost-button-hover-background);
-}
-
-.banner-close svg {
- width: 10px;
- height: 10px;
-}
-
/* ===========================
Claude Code Style Input Form (.Me > .u)
=========================== */
diff --git a/packages/vscode-ide-companion/src/webview/App.tsx b/packages/vscode-ide-companion/src/webview/App.tsx
index 506a6f16..92e7ee76 100644
--- a/packages/vscode-ide-companion/src/webview/App.tsx
+++ b/packages/vscode-ide-companion/src/webview/App.tsx
@@ -23,6 +23,7 @@ import {
} from './components/CompletionMenu.js';
import { useCompletionTrigger } from './hooks/useCompletionTrigger.js';
import { SaveSessionDialog } from './components/SaveSessionDialog.js';
+import { InfoBanner } from './components/InfoBanner.js';
interface ToolCallUpdate {
type: 'tool_call' | 'tool_call_update';
@@ -1402,53 +1403,14 @@ export const App: React.FC = () => {
{/* Info Banner */}
- {showBanner && (
-
-
-
-
- )}
+ setShowBanner(false)}
+ onLinkClick={(e) => {
+ e.preventDefault();
+ vscode.postMessage({ type: 'openSettings', data: {} });
+ }}
+ />
diff --git a/packages/vscode-ide-companion/src/webview/MessageHandler.ts b/packages/vscode-ide-companion/src/webview/MessageHandler.ts
index 5c2e39f2..8bb937a1 100644
--- a/packages/vscode-ide-companion/src/webview/MessageHandler.ts
+++ b/packages/vscode-ide-companion/src/webview/MessageHandler.ts
@@ -207,6 +207,10 @@ export class MessageHandler {
await this.handleResumeSession(data?.sessionId as string);
break;
+ case 'openSettings':
+ await this.handleOpenSettings();
+ break;
+
default:
console.warn('[MessageHandler] Unknown message type:', message.type);
break;
@@ -922,4 +926,24 @@ export class MessageHandler {
});
}
}
+
+ /**
+ * 处理打开设置请求
+ * 打开 VSCode 设置页面并定位到扩展配置
+ */
+ private async handleOpenSettings(): Promise
{
+ try {
+ console.log('[MessageHandler] Opening settings');
+ await vscode.commands.executeCommand(
+ 'workbench.action.openSettings',
+ 'qwenCode',
+ );
+ } catch (error) {
+ console.error('[MessageHandler] Failed to open settings:', error);
+ this.sendToWebView({
+ type: 'error',
+ data: { message: `Failed to open settings: ${error}` },
+ });
+ }
+ }
}
diff --git a/packages/vscode-ide-companion/src/webview/components/InfoBanner.tsx b/packages/vscode-ide-companion/src/webview/components/InfoBanner.tsx
new file mode 100644
index 00000000..c51377b0
--- /dev/null
+++ b/packages/vscode-ide-companion/src/webview/components/InfoBanner.tsx
@@ -0,0 +1,135 @@
+/**
+ * @license
+ * Copyright 2025 Qwen Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import type React from 'react';
+
+interface InfoBannerProps {
+ /**
+ * Whether the banner is visible
+ */
+ visible: boolean;
+
+ /**
+ * Callback when the banner is dismissed
+ */
+ onDismiss: () => void;
+
+ /**
+ * Optional: Custom message content (if not provided, uses default)
+ */
+ message?: React.ReactNode;
+
+ /**
+ * Optional: Custom link text
+ */
+ linkText?: string;
+
+ /**
+ * Optional: Callback when the link is clicked
+ */
+ onLinkClick?: (e: React.MouseEvent) => void;
+}
+
+export const InfoBanner: React.FC = ({
+ visible,
+ onDismiss,
+ message,
+ linkText = 'Switch back in Settings.',
+ onLinkClick,
+}) => {
+ if (!visible) {
+ return null;
+ }
+
+ return (
+
+
+ {/* Icon */}
+
+
+ {/* Message */}
+
+
+
+ {/* Close button */}
+
+
+ );
+};