From 4918399325254eb2bf62eebd2eab236acbce3cfa Mon Sep 17 00:00:00 2001 From: Roe-xin Date: Tue, 16 Dec 2025 16:22:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E5=A4=8D=E5=88=B6?= =?UTF-8?q?=E7=82=B9=E8=B5=9E=E7=82=B9=E8=B8=A9=E5=8A=9F=E8=83=BD=20-=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BE=93=E5=85=A5=E6=A1=86=E5=8D=A0=E6=8D=AE?= =?UTF-8?q?=E4=B8=8D=E6=BB=A1=E6=95=B4=E4=B8=AA=E5=A4=A7=E6=A1=86=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20-=20=E4=BC=98=E5=8C=96=E7=82=B9=E8=B5=9E?= =?UTF-8?q?=E7=82=B9=E8=B8=A9=E5=A4=8D=E5=88=B6=E7=9A=84tooltip=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E8=A2=AB=E9=81=AE=E6=8C=A1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/webviewContent.ts | 338 +++++++++++++++++++++++++++++------- 1 file changed, 271 insertions(+), 67 deletions(-) diff --git a/src/views/webviewContent.ts b/src/views/webviewContent.ts index b9b9982..6551c7c 100644 --- a/src/views/webviewContent.ts +++ b/src/views/webviewContent.ts @@ -45,18 +45,103 @@ export function getWebviewContent(iconUri?: string): string { } .message { margin-bottom: 12px; - padding: 10px 15px; - border-radius: 8px; - max-width: 80%; } .user-message { + padding: 10px 15px; + border-radius: 8px; background: var(--vscode-button-secondaryBackground); + border: 1px solid var(--vscode-input-border); margin-left: auto; + width: fit-content; + max-width: 80%; } .bot-message { - background: var(--vscode-button-background); - color: var(--vscode-button-foreground); - margin-right: auto; + padding: 0; + text-align: left; + color: var(--vscode-foreground); + max-width: 100%; + position: relative; + } + .message-actions { + display: flex; + gap: 8px; + margin-top: 12px; + margin-left: 10px; + opacity: 0.85; + transition: opacity 0.2s ease; + } + .message-actions:hover { + opacity: 1; + } + .action-btn { + background: transparent; + border: none; + cursor: pointer; + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + color: var(--vscode-foreground); + opacity: 0.9; + transition: opacity 0.2s ease; + position: relative; + } + .action-btn:hover { + opacity: 1; + } + .action-btn svg { + width: 14px; + height: 14px; + } + .action-btn.active { + color: var(--vscode-button-background); + opacity: 1; + } + .action-btn .action-tooltip { + visibility: hidden; + width: auto; + background: #1e1e1e; + color: #ffffff; + text-align: center; + border-radius: 4px; + border: 1px solid rgba(255, 255, 255, 0.2); + padding: 4px 8px; + position: absolute; + z-index: 1000; + bottom: 125%; + left: 50%; + transform: translateX(-50%) translateY(5px); + opacity: 0; + transition: all 0.2s ease; + font-size: 12px; + white-space: nowrap; + pointer-events: none; + } + .action-btn .action-tooltip::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #1e1e1e transparent transparent transparent; + } + .action-btn .action-tooltip::before { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -6px; + border-width: 6px; + border-style: solid; + border-color: rgba(255, 255, 255, 0.2) transparent transparent transparent; + z-index: -1; + } + .action-btn:hover .action-tooltip { + visibility: visible; + opacity: 1; + transform: translateX(-50%) translateY(0); } .input-area { border-top: 1px solid var(--vscode-panel-border); @@ -65,8 +150,8 @@ export function getWebviewContent(iconUri?: string): string { } .input-group { display: flex; + flex-direction: column; gap: 10px; - align-items: flex-end; background: var(--vscode-input-background); border: 1px solid var(--vscode-input-border); border-radius: 8px; @@ -82,16 +167,27 @@ export function getWebviewContent(iconUri?: string): string { box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25), 0 3px 10px rgba(0, 0, 0, 0.2); } .input-wrapper { - flex: 1; display: flex; flex-direction: column; gap: 8px; + width: 100%; + } + .input-bottom-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; } .mode-selector { display: flex; align-items: center; position: relative; } + .input-actions { + display: flex; + align-items: center; + gap: 10px; + } .mode-selector select { padding: 2px 4px; background: transparent; @@ -140,7 +236,17 @@ export function getWebviewContent(iconUri?: string): string { border-width: 6px; border-style: solid; border-color: #1e1e1e transparent transparent transparent; - filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.3)); + } + .tooltip .tooltiptext::before { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -7px; + border-width: 7px; + border-style: solid; + border-color: rgba(255, 255, 255, 0.2) transparent transparent transparent; + z-index: -1; } .tooltip:hover .tooltiptext { visibility: visible; @@ -157,8 +263,25 @@ export function getWebviewContent(iconUri?: string): string { font-family: inherit; resize: none; min-height: 40px; + max-height: 200px; outline: none; box-sizing: border-box; + overflow-y: auto; + line-height: 1.5; + } + /* 简洁的滚动条样式 */ + textarea::-webkit-scrollbar { + width: 8px; + } + textarea::-webkit-scrollbar-track { + background: transparent; + } + textarea::-webkit-scrollbar-thumb { + background: rgba(128, 128, 128, 0.5); + border-radius: 4px; + } + textarea::-webkit-scrollbar-button { + display: none; } button { padding: 0 20px; @@ -180,8 +303,6 @@ export function getWebviewContent(iconUri?: string): string { transition: opacity 0.2s ease; width: 32px; height: 32px; - align-self: flex-end; - margin-bottom: -6px; } .optimize-button:hover { opacity: 0.7; @@ -299,8 +420,6 @@ export function getWebviewContent(iconUri?: string): string { flex-direction: column; align-items: center; position: relative; - align-self: flex-end; - margin-bottom: -10px; } .context-info { display: flex; @@ -475,63 +594,66 @@ export function getWebviewContent(iconUri?: string): string { placeholder="输入您的问题..." onkeydown="if(event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); }" > -
-
- - 切换模型 -
-
- - - -
-
-
- - - - - - - - - - - - - - - -
- 0% -
- - -
-
-
- 0k / 200k 已用上下文 +
+
+
+ + 切换模型
- +
+
+ +
+
+
+ + + + + + + + + + + + + + + +
+ 0% +
+ + +
+
+
+ 0k / 200k 已用上下文 +
+ +
+
+
+ + +
+ + 一键优化 +
+ +
- - -
- - 一键优化 -
- -
@@ -560,6 +682,7 @@ export function getWebviewContent(iconUri?: string): string { addMessage(text, 'user'); vscode.postMessage({ command: 'sendMessage', text: text, mode: mode }); messageInput.value = ''; + autoResizeTextarea(); // 重置输入框高度 messageInput.focus(); // 重置优化状态 @@ -661,11 +784,80 @@ export function getWebviewContent(iconUri?: string): string { function addMessage(text, sender) { const div = document.createElement('div'); div.className = \`message \${sender}-message\`; - div.textContent = text; + + if (sender === 'bot') { + // 创建消息内容 + const messageContent = document.createElement('div'); + messageContent.textContent = text; + div.appendChild(messageContent); + + // 创建操作按钮容器 + const actionsDiv = document.createElement('div'); + actionsDiv.className = 'message-actions'; + + // 复制按钮 + const copyBtn = document.createElement('button'); + copyBtn.className = 'action-btn'; + copyBtn.innerHTML = \`复制\`; + copyBtn.onclick = () => copyMessage(text, copyBtn); + + // 点赞按钮 + const likeBtn = document.createElement('button'); + likeBtn.className = 'action-btn'; + likeBtn.innerHTML = \`点赞\`; + likeBtn.onclick = () => toggleLike(likeBtn); + + // 点踩按钮 + const dislikeBtn = document.createElement('button'); + dislikeBtn.className = 'action-btn'; + dislikeBtn.innerHTML = \`点踩\`; + dislikeBtn.onclick = () => toggleDislike(dislikeBtn); + + actionsDiv.appendChild(copyBtn); + actionsDiv.appendChild(likeBtn); + actionsDiv.appendChild(dislikeBtn); + + div.appendChild(actionsDiv); + } else { + div.textContent = text; + } + messagesEl.appendChild(div); messagesEl.scrollTop = messagesEl.scrollHeight; } + function copyMessage(text, button) { + navigator.clipboard.writeText(text).then(() => { + const originalHTML = button.innerHTML; + button.innerHTML = \`\`; + setTimeout(() => { + button.innerHTML = originalHTML; + }, 2000); + }); + } + + function toggleLike(button) { + const isActive = button.classList.contains('active'); + // 移除所有同级按钮的 active 状态 + const parent = button.parentElement; + parent.querySelectorAll('.action-btn').forEach(btn => btn.classList.remove('active')); + + if (!isActive) { + button.classList.add('active'); + } + } + + function toggleDislike(button) { + const isActive = button.classList.contains('active'); + // 移除所有同级按钮的 active 状态 + const parent = button.parentElement; + parent.querySelectorAll('.action-btn').forEach(btn => btn.classList.remove('active')); + + if (!isActive) { + button.classList.add('active'); + } + } + function openFileEditor(filePath, content) { currentEditingFile = filePath; editingFileName.textContent = filePath; @@ -787,6 +979,18 @@ export function getWebviewContent(iconUri?: string): string { } }); + // 自动调整 textarea 高度 + function autoResizeTextarea() { + messageInput.style.height = 'auto'; + messageInput.style.height = messageInput.scrollHeight + 'px'; + } + + // 监听输入事件,自动调整高度 + messageInput.addEventListener('input', autoResizeTextarea); + + // 初始化时调整一次高度 + autoResizeTextarea(); + messageInput.focus();