diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index 31a751c..69e18f3 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -13,12 +13,12 @@ import { VCDViewerPanel } from "./VCDViewerPanel"; /** * 创建并显示 IC 助手面板 */ -export function showICHelperPanel(context: vscode.ExtensionContext) { +export function showICHelperPanel(context: vscode.ExtensionContext, viewColumn?: vscode.ViewColumn) { // 创建WebView面板 const panel = vscode.window.createWebviewPanel( "icCoder", // 面板ID "IC Coder", // 面板标题 - vscode.ViewColumn.Beside, // 显示在旁边 + viewColumn || vscode.ViewColumn.Beside, // 默认显示在旁边,但可以指定 { enableScripts: true, retainContextWhenHidden: true, @@ -74,6 +74,20 @@ export function showICHelperPanel(context: vscode.ExtensionContext) { getVCDFileInfo(panel, message.vcdFilePath, message.containerId); } break; + case "createNewConversation": + // 创建新会话 - 在当前编辑器组中打开新标签页 + showICHelperPanel(context, panel.viewColumn); + break; + case "loadConversationHistory": + // 加载会话历史(暂未实现) + panel.webview.postMessage({ + command: 'conversationHistory', + history: [] + }); + break; + case "selectConversation": + // 选择会话(暂未实现) + break; } }, undefined, diff --git a/src/views/conversationHistoryBar.ts b/src/views/conversationHistoryBar.ts new file mode 100644 index 0000000..c4d82b3 --- /dev/null +++ b/src/views/conversationHistoryBar.ts @@ -0,0 +1,308 @@ +/** + * 获取会话历史栏的 HTML 内容 + */ +export function getConversationHistoryBarContent(): string { + return ` +
+
+ + +
+
+ +
+
+
+ + +
+ `; +} + +/** + * 获取会话历史栏的 CSS 样式 + */ +export function getConversationHistoryBarStyles(): string { + return ` + .conversation-history-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; + background: var(--vscode-tab-activeBackground); + border-bottom: 1px solid var(--vscode-panel-border); + flex-shrink: 0; + min-height: 35px; + } + + .history-dropdown-container { + position: relative; + flex: 1; + } + + .history-dropdown-button { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + background: transparent; + color: var(--vscode-input-foreground); + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.2s ease; + } + + .history-dropdown-button:hover { + opacity: 0.8; + } + + .dropdown-label { + white-space: nowrap; + } + + .dropdown-icon { + width: 12px; + height: 12px; + transition: transform 0.2s ease; + flex-shrink: 0; + } + + .history-dropdown-button.active .dropdown-icon { + transform: rotate(180deg); + } + + .history-dropdown-menu { + position: absolute; + top: calc(100% + 4px); + left: 0; + min-width: 300px; + max-height: 400px; + background: var(--vscode-dropdown-background); + border: 1px solid var(--vscode-dropdown-border); + border-radius: 4px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + max-height: 400px; + overflow-y: auto; + z-index: 1000; + display: none; + } + + .history-dropdown-menu.active { + display: block; + } + + .history-list { + padding: 4px 0; + } + + .history-item { + padding: 10px 16px; + cursor: pointer; + transition: background 0.2s ease; + border-bottom: 1px solid var(--vscode-panel-border); + } + + .history-item:last-child { + border-bottom: none; + } + + .history-item:hover { + background: var(--vscode-list-hoverBackground); + } + + .history-item-title { + font-size: 14px; + font-weight: 500; + margin-bottom: 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .history-item-time { + font-size: 12px; + opacity: 0.7; + } + + .history-empty { + padding: 20px; + text-align: center; + color: var(--vscode-descriptionForeground); + font-size: 14px; + } + + .new-conversation-button { + width: 36px; + height: 36px; + padding: 0; + background: transparent; + color: var(--vscode-foreground); + border: none; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + flex-shrink: 0; + } + + .new-conversation-button:hover { + opacity: 0.7; + } + + .new-conversation-button:active { + opacity: 0.5; + } + + .new-conversation-button svg { + width: 20px; + height: 20px; + } + + /* 滚动条样式 */ + .history-dropdown-menu::-webkit-scrollbar { + width: 8px; + } + + .history-dropdown-menu::-webkit-scrollbar-track { + background: transparent; + } + + .history-dropdown-menu::-webkit-scrollbar-thumb { + background: rgba(128, 128, 128, 0.5); + border-radius: 4px; + } + + .history-dropdown-menu::-webkit-scrollbar-thumb:hover { + background: rgba(128, 128, 128, 0.7); + } + `; +} + +/** + * 获取会话历史栏的 JavaScript 脚本 + */ +export function getConversationHistoryBarScript(): string { + return ` + // 会话历史相关变量 + let conversationHistory = []; + let currentConversationId = null; + + // 切换历史记录下拉菜单 + function toggleHistoryDropdown() { + const menu = document.getElementById('historyDropdownMenu'); + const button = document.querySelector('.history-dropdown-button'); + + if (menu.classList.contains('active')) { + menu.classList.remove('active'); + button.classList.remove('active'); + } else { + menu.classList.add('active'); + button.classList.add('active'); + // 加载会话历史 + loadConversationHistory(); + } + } + + // 加载会话历史 + function loadConversationHistory() { + vscode.postMessage({ command: 'loadConversationHistory' }); + } + + // 渲染会话历史列表 + function renderConversationHistory(history) { + conversationHistory = history; + const historyList = document.getElementById('historyList'); + + if (!history || history.length === 0) { + historyList.innerHTML = '
暂无会话历史
'; + return; + } + + historyList.innerHTML = history.map(item => \` +
+
\${item.title || '未命名会话'}
+
\${formatTime(item.timestamp)}
+
+ \`).join(''); + } + + // 选择会话 + function selectConversation(conversationId) { + currentConversationId = conversationId; + vscode.postMessage({ + command: 'selectConversation', + conversationId: conversationId + }); + + // 关闭下拉菜单 + const menu = document.getElementById('historyDropdownMenu'); + const button = document.querySelector('.history-dropdown-button'); + menu.classList.remove('active'); + button.classList.remove('active'); + } + + // 创建新会话 + function createNewConversation() { + vscode.postMessage({ command: 'createNewConversation' }); + } + + // 格式化时间 + function formatTime(timestamp) { + const date = new Date(timestamp); + const now = new Date(); + const diff = now - date; + + // 小于1分钟 + if (diff < 60000) { + return '刚刚'; + } + // 小于1小时 + if (diff < 3600000) { + return Math.floor(diff / 60000) + '分钟前'; + } + // 小于1天 + if (diff < 86400000) { + return Math.floor(diff / 3600000) + '小时前'; + } + // 小于7天 + if (diff < 604800000) { + return Math.floor(diff / 86400000) + '天前'; + } + + // 超过7天显示具体日期 + return date.toLocaleDateString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); + } + + // 点击外部关闭下拉菜单 + document.addEventListener('click', (event) => { + const container = document.querySelector('.history-dropdown-container'); + const menu = document.getElementById('historyDropdownMenu'); + const button = document.querySelector('.history-dropdown-button'); + + if (menu && menu.classList.contains('active')) { + if (!container.contains(event.target)) { + menu.classList.remove('active'); + button.classList.remove('active'); + } + } + }); + `; +} diff --git a/src/views/webviewContent.ts b/src/views/webviewContent.ts index a6b7327..271f4d0 100644 --- a/src/views/webviewContent.ts +++ b/src/views/webviewContent.ts @@ -1,4 +1,12 @@ -import { getWaveformPreviewContent, getWaveformPreviewScript } from './waveformPreviewContent'; +import { + getWaveformPreviewContent, + getWaveformPreviewScript, +} from "./waveformPreviewContent"; +import { + getConversationHistoryBarContent, + getConversationHistoryBarStyles, + getConversationHistoryBarScript, +} from "./conversationHistoryBar"; /** * 获取 WebView 面板的 HTML 内容 @@ -16,7 +24,7 @@ export function getWebviewContent(iconUri?: string): string { background: var(--vscode-editor-background); color: var(--vscode-foreground); margin: 0; - padding: 20px; + padding: 0; height: 100vh; box-sizing: border-box; display: flex; @@ -24,9 +32,16 @@ export function getWebviewContent(iconUri?: string): string { } .header { text-align: center; - margin-bottom: 20px; - padding-bottom: 15px; - border-bottom: 1px solid var(--vscode-panel-border); + padding: 20px; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + flex: 1; + } + .header.hidden { + display: none; } .header h1 { color: var(--vscode-button-background); @@ -38,6 +53,7 @@ export function getWebviewContent(iconUri?: string): string { flex-direction: column; min-height: 0; overflow: hidden; + padding: 0 20px 20px 20px; } .messages { flex: 1; @@ -384,6 +400,7 @@ export function getWebviewContent(iconUri?: string): string { margin-top: 10px; } ${getWaveformPreviewContent()} + ${getConversationHistoryBarStyles()} .file-editor-section { margin-bottom: 15px; padding: 15px; @@ -544,6 +561,7 @@ export function getWebviewContent(iconUri?: string): string { + ${getConversationHistoryBarContent()}
IC Coder @@ -553,35 +571,7 @@ export function getWebviewContent(iconUri?: string): string {
-
-

📁 文件读取

-
- - -
-
- 文件内容将在这里显示... -
- -
- -
-

✏️ 编辑文件:

- -
- - -
-
-
-
- 👋 你好!我是 IC Coder 助手,可以帮你生成代码、回答问题。 -
-
+
@@ -823,10 +813,20 @@ export function getWebviewContent(iconUri?: string): string { div.appendChild(actionsDiv); } else { div.textContent = text; + // 当添加用户消息时,隐藏 header + hideHeaderIfNeeded(); } messagesEl.appendChild(div); messagesEl.scrollTop = messagesEl.scrollHeight; + + // 添加消息后检查 header 显示状态 + checkHeaderVisibility(); + } + + // 检查是否需要隐藏 header + function hideHeaderIfNeeded() { + checkHeaderVisibility(); } function copyMessage(text, button) { @@ -995,6 +995,32 @@ export function getWebviewContent(iconUri?: string): string { renderWaveformInfo(message.containerId, message.vcdInfo); } break; + case 'conversationHistory': + // 接收到会话历史数据 + if (message.history) { + renderConversationHistory(message.history); + } + break; + case 'conversationLoaded': + // 会话加载成功,清空当前消息并显示历史消息 + messagesEl.innerHTML = ''; + if (message.messages && message.messages.length > 0) { + message.messages.forEach(msg => { + addMessage(msg.text, msg.sender); + }); + } + currentConversationId = message.conversationId; + break; + case 'newConversationCreated': + // 新会话创建成功,清空消息区域 + messagesEl.innerHTML = ''; + currentConversationId = message.conversationId; + // 显示 header + const header = document.querySelector('.header'); + if (header) { + header.classList.remove('hidden'); + } + break; } }); @@ -1017,9 +1043,24 @@ export function getWebviewContent(iconUri?: string): string { // 初始化时调整一次高度 autoResizeTextarea(); + // 初始化时检查是否需要显示 header + checkHeaderVisibility(); + messageInput.focus(); + // 检查 header 显示状态 + function checkHeaderVisibility() { + const allMessages = messagesEl.querySelectorAll('.message'); + const header = document.querySelector('.header'); + if (allMessages.length > 0 && header) { + header.classList.add('hidden'); + } else if (header) { + header.classList.remove('hidden'); + } + } + ${getWaveformPreviewScript()} + ${getConversationHistoryBarScript()} `;