diff --git a/src/assets/QRCode/wx.png b/src/assets/QRCode/wx.png new file mode 100644 index 0000000..d642075 Binary files /dev/null and b/src/assets/QRCode/wx.png differ diff --git a/src/constants/toolIcons.ts b/src/constants/toolIcons.ts index dc24f97..f685412 100644 --- a/src/constants/toolIcons.ts +++ b/src/constants/toolIcons.ts @@ -184,4 +184,9 @@ export const userAvatarIconSvg = ``; +export const updateStageIconSvg = ``; + +/** + * 更多的图标svg + */ +export const moreIconSvg = ``; diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index 03285f8..a0731c3 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -131,13 +131,19 @@ export async function showICHelperPanel( vscode.Uri.joinPath(context.extensionUri, "src", "assets", "model", "Max.png") ); + // 获取二维码图片URI + const qrCodeUri = panel.webview.asWebviewUri( + vscode.Uri.joinPath(context.extensionUri, "src", "assets", "QRCode", "wx.png") + ); + // 设置HTML内容 panel.webview.html = getWebviewContent( iconUri.toString(), autoIconUri.toString(), liteIconUri.toString(), syIconUri.toString(), - maxIconUri.toString() + maxIconUri.toString(), + qrCodeUri.toString() ); // 获取并发送用户信息到 webview @@ -344,6 +350,16 @@ export async function showICHelperPanel( // 退出登录 vscode.commands.executeCommand("ic-coder.logout"); break; + case "openUserManual": + // 打开用户手册 + vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com")); + break; + case "openUserFeedback": + // 打开用户反馈二维码弹窗 + panel.webview.postMessage({ + command: "showFeedbackQRCode" + }); + break; // 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送) case "planAction": if (message.action === "confirm") { diff --git a/src/views/conversationHistoryBar.ts b/src/views/conversationHistoryBar.ts index 80e5785..fb79ecd 100644 --- a/src/views/conversationHistoryBar.ts +++ b/src/views/conversationHistoryBar.ts @@ -3,7 +3,12 @@ import { getUserInfoComponentStyles, getUserInfoComponentScript, } from "./userInfoComponent"; -import { userAvatarIconSvg } from "../constants/toolIcons"; +import { + getMoreOptionsComponentContent, + getMoreOptionsComponentStyles, + getMoreOptionsComponentScript, +} from "./moreOptionsComponent"; +import { userAvatarIconSvg, moreIconSvg } from "../constants/toolIcons"; /** * 获取会话历史栏的 HTML 内容 @@ -39,6 +44,13 @@ export function getConversationHistoryBarContent(): string { ${getUserInfoComponentContent()} + +
+ + ${getMoreOptionsComponentContent()} +
`; @@ -111,6 +123,41 @@ export function getConversationHistoryBarStyles(): string { ${getUserInfoComponentStyles()} + .more-container { + position: relative; + } + + .more-button { + width: 36px; + height: 36px; + padding: 0; + background: transparent; + color: var(--vscode-foreground); + border: none; + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + flex-shrink: 0; + } + + .more-button:hover { + background: var(--vscode-toolbar-hoverBackground); + transform: scale(1.1); + } + + .more-button:active { + transform: scale(0.95); + } + + .more-button.active { + background: var(--vscode-toolbar-hoverBackground); + } + + ${getMoreOptionsComponentStyles()} + .history-dropdown-button { display: inline-flex; align-items: center; @@ -275,6 +322,8 @@ export function getConversationHistoryBarScript(): string { return ` ${getUserInfoComponentScript()} + ${getMoreOptionsComponentScript()} + // 更新用户头像图标按钮显示 function updateUserAvatarIconButton(userInfo) { const userAvatarIconButton = document.getElementById('userAvatarIconButton'); diff --git a/src/views/moreOptionsComponent.ts b/src/views/moreOptionsComponent.ts new file mode 100644 index 0000000..655372b --- /dev/null +++ b/src/views/moreOptionsComponent.ts @@ -0,0 +1,394 @@ +/** + * 更多选项组件 + * 包含用户手册和用户反馈入口 + */ + +/** + * 获取更多选项组件的 HTML 内容 + */ +export function getMoreOptionsComponentContent(): string { + return ` +
+ +
+
+
+ 更多选项 +
+ +
+
+
+ + + +
+
+
用户手册
+
查看使用文档和帮助
+
+
+ +
+
+ + + +
+
+
用户反馈
+
提交问题和建议
+
+
+
+
+
+ + +
+ + +
+
+ `; +} + +/** + * 获取更多选项组件的 CSS 样式 + */ +export function getMoreOptionsComponentStyles(): string { + return ` + .more-options-wrapper { + position: relative; + } + + /* 更多选项下拉面板 */ + .more-options-dropdown { + display: none; + position: absolute; + top: calc(100% + 8px); + right: 0; + z-index: 10000; + min-width: 200px; + } + + .more-options-dropdown.active { + display: block; + animation: dropdownSlideIn 0.15s ease-out; + } + + @keyframes dropdownSlideIn { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + .more-options-content { + background: var(--vscode-dropdown-background); + border: 1px solid var(--vscode-dropdown-border); + border-radius: 6px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25); + overflow: hidden; + } + + .more-options-header { + display: none; + } + + .more-options-body { + padding: 4px; + } + + .more-option-item { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + cursor: pointer; + transition: background 0.15s ease; + border-radius: 4px; + } + + .more-option-item:hover { + background: var(--vscode-list-hoverBackground); + } + + .more-option-item:active { + background: var(--vscode-list-activeSelectionBackground); + } + + .option-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + opacity: 0.8; + } + + .option-icon svg { + width: 16px; + height: 16px; + color: var(--vscode-foreground); + } + + .option-text { + flex: 1; + } + + .option-label { + font-size: 13px; + color: var(--vscode-foreground); + } + + .option-desc { + display: none; + } + + /* 用户反馈二维码弹窗 */ + .feedback-qrcode-modal { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 20000; + align-items: center; + justify-content: center; + } + + .feedback-qrcode-modal.active { + display: flex; + animation: fadeIn 0.2s ease-out; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + .feedback-qrcode-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + cursor: pointer; + } + + .feedback-qrcode-content { + position: relative; + background: var(--vscode-editor-background); + border: 1px solid var(--vscode-widget-border); + border-radius: 8px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + max-width: 400px; + width: 90%; + animation: slideUp 0.2s ease-out; + } + + @keyframes slideUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + .feedback-qrcode-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + border-bottom: 1px solid var(--vscode-widget-border); + } + + .feedback-qrcode-title { + font-size: 14px; + font-weight: 600; + color: var(--vscode-foreground); + } + + .feedback-qrcode-close { + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: none; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.15s ease; + } + + .feedback-qrcode-close:hover { + background: var(--vscode-toolbar-hoverBackground); + } + + .feedback-qrcode-close svg { + width: 16px; + height: 16px; + color: var(--vscode-foreground); + } + + .feedback-qrcode-body { + padding: 24px; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + } + + .feedback-qrcode-image { + width: 200px; + height: 200px; + border: 1px solid var(--vscode-widget-border); + border-radius: 8px; + } + + .feedback-qrcode-text { + margin: 0; + font-size: 13px; + color: var(--vscode-descriptionForeground); + text-align: center; + } + `; +} + +/** + * 获取更多选项组件的 JavaScript 脚本 + */ +export function getMoreOptionsComponentScript(): string { + return ` + // 切换更多选项下拉面板 + function toggleMoreOptionsDropdown() { + const dropdown = document.getElementById('moreOptionsDropdown'); + const moreButton = document.querySelector('.more-button'); + + if (dropdown) { + const isActive = dropdown.classList.contains('active'); + if (isActive) { + dropdown.classList.remove('active'); + if (moreButton) { + moreButton.classList.remove('active'); + } + } else { + dropdown.classList.add('active'); + if (moreButton) { + moreButton.classList.add('active'); + } + } + } + } + + // 关闭更多选项下拉面板 + function closeMoreOptionsDropdown() { + const dropdown = document.getElementById('moreOptionsDropdown'); + const moreButton = document.querySelector('.more-button'); + + if (dropdown) { + dropdown.classList.remove('active'); + } + if (moreButton) { + moreButton.classList.remove('active'); + } + } + + // 打开用户手册 + function openUserManual() { + console.log('打开用户手册'); + vscode.postMessage({ command: 'openUserManual' }); + closeMoreOptionsDropdown(); + } + + // 打开用户反馈 + function openUserFeedback() { + console.log('打开用户反馈'); + vscode.postMessage({ command: 'openUserFeedback' }); + closeMoreOptionsDropdown(); + } + + // 显示用户反馈二维码弹窗 + function showFeedbackQRCode() { + const modal = document.getElementById('feedbackQRCodeModal'); + if (modal) { + modal.classList.add('active'); + } + } + + // 关闭用户反馈二维码弹窗 + function closeFeedbackQRCode() { + const modal = document.getElementById('feedbackQRCodeModal'); + if (modal) { + modal.classList.remove('active'); + } + } + + // 绑定更多选项事件 + document.addEventListener('DOMContentLoaded', () => { + // 绑定用户手册选项 + const userManualOption = document.getElementById('userManualOption'); + if (userManualOption) { + userManualOption.addEventListener('click', openUserManual); + } + + // 绑定用户反馈选项 + const userFeedbackOption = document.getElementById('userFeedbackOption'); + if (userFeedbackOption) { + userFeedbackOption.addEventListener('click', openUserFeedback); + } + + // 点击页面其他地方关闭下拉面板 + document.addEventListener('click', (e) => { + const dropdown = document.getElementById('moreOptionsDropdown'); + const moreButton = document.querySelector('.more-button'); + const moreContainer = document.querySelector('.more-container'); + + if (dropdown && dropdown.classList.contains('active')) { + // 如果点击的不是更多按钮和下拉面板内容,则关闭 + if (!moreContainer?.contains(e.target)) { + closeMoreOptionsDropdown(); + } + } + }); + + // 阻止下拉面板内容点击事件冒泡 + const dropdownContent = document.querySelector('.more-options-content'); + if (dropdownContent) { + dropdownContent.addEventListener('click', (e) => { + e.stopPropagation(); + }); + } + }); + `; +} diff --git a/src/views/webviewContent.ts b/src/views/webviewContent.ts index 83b0bf6..260d72e 100644 --- a/src/views/webviewContent.ts +++ b/src/views/webviewContent.ts @@ -33,7 +33,8 @@ export function getWebviewContent( autoIconUri?: string, liteIconUri?: string, syIconUri?: string, - maxIconUri?: string + maxIconUri?: string, + qrCodeUri?: string ): string { // 获取当前环境,只在 dev 和 test 环境下显示快速操作按钮 const currentEnv = getCurrentEnv(); @@ -439,6 +440,12 @@ export function getWebviewContent( let loadingIndicator = null; let currentSegmentedMessage = null; // 当前分段消息容器 + // 设置二维码图片 + const feedbackQRCodeImage = document.getElementById('feedbackQRCodeImage'); + if (feedbackQRCodeImage && '${qrCodeUri}') { + feedbackQRCodeImage.src = '${qrCodeUri}'; + } + // ========== 模式选择器脚本(直接内联,避免模板字符串嵌套问题)========== let currentMode = 'agent'; @@ -612,6 +619,14 @@ export function getWebviewContent( } break; + case 'showFeedbackQRCode': + // 显示用户反馈二维码弹窗 + console.log('[WebView] 显示用户反馈二维码弹窗'); + if (typeof showFeedbackQRCode === 'function') { + showFeedbackQRCode(); + } + break; + case 'resetSegmentedMessage': // 重置分段消息容器(停止对话时调用) console.log('[WebView] 重置分段消息容器');