You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
17 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

let selectedCodeForContext = '';
let contextFilePath = ''; // 存储当前上下文文件路径
const vscode = acquireVsCodeApi();
document.addEventListener('DOMContentLoaded', () => {
const chatForm = document.getElementById('chat-form');
const userInput = document.getElementById('user-input');
const chatBox = document.getElementById('chat-box');
const loading = document.getElementById('loading');
// 初始化隐藏工作区
const workspaceContainer = document.querySelector('.workspace-container');
if (workspaceContainer) {
workspaceContainer.style.display = 'none';
}
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
const text = userInput.value.trim();
if (!text) return;
vscode.postMessage({
command: 'ask',
text,
fileContent: selectedCodeForContext,
fileContentPath: contextFilePath // 添加文件路径
});
// addMessage('user', text);
showLoading();
userInput.value = '';
});
// 判断内容是否为 Markdown简单判断
function isMarkdown(content) {
if (!content || content.trim() === '') return false;
const markdownPatterns = [
/^#/m, // 标题 #
/\*\*[^*]+\*\*/g, // 加粗 **text**
/__[^_]+__/g, // 加粗 __text__
/^- /gm, // 无序列表
/^\* /gm, // 无序列表
/^\d+\. /gm, // 有序列表
/```/g // 代码块
];
return markdownPatterns.some(pattern => pattern.test(content));
}
// 处理添加消息
function addMessage(role, content, codeDiff) {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${role}`;
// 渲染内容
if (isMarkdown(content)) {
console.log("content:", content);
msgDiv.innerHTML = marked.parse(content);
console.log("msgDiv.innerHTML:", msgDiv.innerHTML);
} else {
const div = document.createElement('div');
div.textContent = content;
msgDiv.appendChild(div);
}
// 如果有代码差异,添加查看差异按钮
if (codeDiff) {
const diffButton = document.createElement('button');
diffButton.className = 'diff-button';
diffButton.textContent = '查看代码差异';
diffButton.onclick = () => showCodeDiff(codeDiff);
msgDiv.appendChild(diffButton);
}
// 如果是AI消息且包含代码块添加"创建文件"按钮
if (role === 'ai') {
const codeBlocks = msgDiv.querySelectorAll('pre code');
codeBlocks.forEach((block, index) => {
const createFileButton = document.createElement('button');
createFileButton.className = 'create-file-button';
createFileButton.textContent = `生成代码文件`;
createFileButton.onclick = () => createNewFileFromCode(block.textContent, index);
msgDiv.appendChild(createFileButton);
});
}
chatBox.appendChild(msgDiv);
// 高亮代码块
const codeBlocks = msgDiv.querySelectorAll('pre code');
codeBlocks.forEach(block => {
if (hljs) {
hljs.highlightElement(block);
}
});
// 滚动到底部
chatBox.scrollTop = chatBox.scrollHeight;
}
// 显示代码差异
function showCodeDiff(codeDiff) {
const diffModal = document.getElementById('diff-modal');
const diffContent = document.getElementById('diff-content');
diffContent.innerHTML = '';
// 显示添加的行
if (codeDiff.added && codeDiff.added.length > 0) {
codeDiff.added.forEach((line, index) => {
const diffRow = document.createElement('div');
diffRow.className = 'diff-row';
// 修复行号显示问题
const lineNumber = document.createElement('div');
lineNumber.className = 'diff-line-number';
lineNumber.textContent = index + 1; // 使用索引+1作为行号
const diffLine = document.createElement('div');
diffLine.className = 'diff-line diff-added';
diffLine.textContent = `+ ${line}`;
diffRow.appendChild(lineNumber);
diffRow.appendChild(diffLine);
diffContent.appendChild(diffRow);
});
}
// 显示删除的行
if (codeDiff.removed && codeDiff.removed.length > 0) {
codeDiff.removed.forEach((line, index) => {
const diffRow = document.createElement('div');
diffRow.className = 'diff-row';
// 修复行号显示问题
const lineNumber = document.createElement('div');
lineNumber.className = 'diff-line-number';
lineNumber.textContent = index + 1; // 使用索引+1作为行号
const diffLine = document.createElement('div');
diffLine.className = 'diff-line diff-removed';
diffLine.textContent = `- ${line}`;
diffRow.appendChild(lineNumber);
diffRow.appendChild(diffLine);
diffContent.appendChild(diffRow);
});
}
// 添加接受和拒绝按钮的事件监听
document.getElementById('accept-changes-btn').onclick = () => acceptChanges(codeDiff.modifiedCode);
document.getElementById('reject-changes-btn').onclick = () => rejectChanges();
diffModal.classList.remove('hidden');
}
// 接受代码变更
function acceptChanges(modifiedCode) {
if (contextFilePath) {
// 发送消息到插件以接受变更
vscode.postMessage({
command: 'acceptChanges',
filePath: contextFilePath,
modifiedContent: modifiedCode
});
// 关闭模态框
document.getElementById('diff-modal').classList.add('hidden');
}
}
// 拒绝代码变更
function rejectChanges() {
if (contextFilePath) {
// 发送消息到插件以拒绝变更
vscode.postMessage({
command: 'rejectChanges',
filePath: contextFilePath
});
// 关闭模态框
document.getElementById('diff-modal').classList.add('hidden');
}
}
function showLoading() {
loading.classList.remove('hidden');
}
function hideLoading() {
loading.classList.add('hidden');
}
// 更新工作区文件列表
function updateWorkspaceFiles(files) {
const workspaceContainer = document.querySelector('.workspace-container');
const workspaceFilesContainer = document.getElementById('workspace-files');
// 如果没有文件变更,则隐藏工作区
if (!files || Object.keys(files).length === 0) {
workspaceContainer.style.display = 'none';
return;
}
// 显示工作区
workspaceContainer.style.display = 'block';
workspaceFilesContainer.innerHTML = '';
Object.keys(files).forEach(filePath => {
const fileChange = files[filePath];
const fileItem = document.createElement('div');
fileItem.className = 'workspace-file-item';
// 获取文件名
const fileName = filePath.split(/[\/\\]/).pop();
// 根据状态设置不同的显示样式
let statusText = '';
let statusClass = '';
switch (fileChange.status) {
case 'accepted':
statusText = '✓ 已接受';
statusClass = 'status-accepted';
break;
case 'rejected':
statusText = '✗ 已拒绝';
statusClass = 'status-rejected';
break;
default:
statusText = '待处理';
statusClass = 'status-pending';
}
fileItem.innerHTML = `
<span class="file-icon">📄</span>
<span class="file-name" title="${filePath}">${fileName}</span>
<span class="file-status ${statusClass}">${statusText}</span>
`;
fileItem.addEventListener('click', () => {
// 发送消息到插件以打开文件
vscode.postMessage({
command: 'openWorkspaceFile',
filePath: filePath
});
});
workspaceFilesContainer.appendChild(fileItem);
});
}
window.addEventListener('message', (event) => {
const message = event.data;
console.log(message)
if (message.command === 'addMessage') {
addMessage(message.role, message.content,message.codeDiff);
} else if (message.command === 'hideLoading') {
hideLoading();
} else if (message.command === 'addToInput') {
userInput.value = message.content; // 插入选中内容到输入框
selectedCodeForContext = message.content;
userInput.focus(); // 自动聚焦输入框
} else if (message.command === 'fileList') {
const fileListContainer = document.getElementById('file-list');
if (message.error) {
fileListContainer.innerHTML = `<div class="loading">错误: ${message.error}</div>`;
return;
}
if (!message.files || message.files.length === 0) {
fileListContainer.innerHTML = '<div class="loading">未找到文件</div>';
return;
}
// 渲染文件列表
fileListContainer.innerHTML = '';
message.files.forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = ` <span class="file-icon">${file.isDirectory ? '📁' : '📄'}</span>
<span class="file-name" title="${file.relativePath}">${file.relativePath}</span>
`;
fileItem.addEventListener('click', () => {
// 选择文件
vscode.postMessage({
command: 'selectFileByPath',
filePath: file.path
});
// 关闭模态框
document.getElementById('file-browser-modal').classList.add('hidden');
// 清空文件搜索输入框
document.getElementById('file-search-input').value = '';
// 保存当前上下文文件路径
contextFilePath = file.path;
});
fileListContainer.appendChild(fileItem);
});
} else if (message.command === 'setContextFile') {
const contextPlaceholder = document.getElementById('context-placeholder');
const contextTab = document.getElementById('context-tab');
const fileNameElement = document.getElementById('context-file-name');
const fullPath = message.fileName;
const fileName = fullPath.split(/[\/\\]/).pop();
fileNameElement.innerText = fileName;
fileNameElement.title = fullPath;
selectedCodeForContext = message.fileContent;
contextFilePath = fullPath; // 保存上下文文件路径
// 隐藏占位符,显示文件标签
contextPlaceholder.classList.add('hidden');
contextTab.classList.remove('hidden');
} else if (message.command === 'updateWorkspaceFiles') {
// 更新工作区文件列表
updateWorkspaceFiles(message.files);
} else if (message.command === 'restoreState') {
// 恢复面板状态
currentSessionHistory = message.history || [];
// 恢复显示历史消息
currentSessionHistory.forEach(msg => {
addMessage(msg.role, msg.content, null);
});
// 恢复工作区变更
if (message.workspaceChanges) {
updateWorkspaceFiles(message.workspaceChanges);
}
}
});
// 打开文件浏览器
document.getElementById('select-context-btn').addEventListener('click', () => {
// 显示模态框
document.getElementById('file-browser-modal').classList.remove('hidden');
// 请求文件列表
vscode.postMessage({
command: 'getFileList'
});
});
// 关闭模态框
document.getElementById('close-modal').addEventListener('click', () => {
document.getElementById('file-browser-modal').classList.add('hidden');
});
// 点击模态框外部关闭
document.getElementById('file-browser-modal').addEventListener('click', (e) => {
if (e.target.id === 'file-browser-modal') {
document.getElementById('file-browser-modal').classList.add('hidden');
}
});
// 文件搜索功能
document.getElementById('file-search-input').addEventListener('input', (e) => {
const searchTerm = e.target.value.toLowerCase();
const fileItems = document.querySelectorAll('.file-item');
fileItems.forEach(item => {
const fileName = item.querySelector('.file-name').textContent.toLowerCase();
if (fileName.includes(searchTerm)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
// 添加关闭按钮事件监听
document.getElementById('close-context-btn').addEventListener('click', () => {
const contextPlaceholder = document.getElementById('context-placeholder');
const contextTab = document.getElementById('context-tab');
contextTab.classList.add('hidden');
contextPlaceholder.classList.remove('hidden');
selectedCodeForContext = '';
contextFilePath = '';
});
// 关闭差异模态框
document.getElementById('close-diff-modal').addEventListener('click', () => {
document.getElementById('diff-modal').classList.add('hidden');
});
// 点击模态框外部关闭
document.getElementById('diff-modal').addEventListener('click', (e) => {
if (e.target.id === 'diff-modal') {
document.getElementById('diff-modal').classList.add('hidden');
}
});
// 从代码创建新文件
function createNewFileFromCode(codeContent, index) {
// 简单的语言检测
const language = detectLanguage(codeContent);
// 请求创建文件
const fileName = `new_file_${index + 1}${getFileExtension(language)}`;
vscode.postMessage({
command: 'createNewFile',
fileName: fileName,
content: codeContent,
language: language
});
}
// 简单的语言检测
function detectLanguage(code) {
// 可以根据代码特征进行简单判断
if (code.includes('import React') || code.includes('from react')) {
return 'javascript'; // React代码
} else if (code.includes('public class') || code.includes('private static')) {
return 'java';
} else if (code.includes('def ') && code.includes(':')) {
return 'python';
} else if (code.includes('function ') || code.includes('const ') || code.includes('let ')) {
return 'javascript';
} else if (code.includes('interface ') && code.includes('export ')) {
return 'typescript';
} else if (code.includes('<?php')) {
return 'php';
} else if (code.includes('using System')) {
return 'csharp';
}
return 'text'; // 默认
}
// 根据语言获取文件扩展名
function getFileExtension(language) {
const extensions = {
'javascript': '.js',
'typescript': '.ts',
'python': '.py',
'java': '.java',
'html': '.html',
'css': '.css',
'php': '.php',
'csharp': '.cs',
'text': '.txt'
};
return extensions[language] || '.txt';
}
});