diff --git a/media/webview/index.html b/media/webview/index.html
index d274c5a..3b18dfa 100644
--- a/media/webview/index.html
+++ b/media/webview/index.html
@@ -72,7 +72,7 @@
AI 正在思考...
diff --git a/media/webview/script.js b/media/webview/script.js
index fd849e9..d609d29 100644
--- a/media/webview/script.js
+++ b/media/webview/script.js
@@ -1,5 +1,11 @@
+import {detectLanguage, getFileExtension} from "../../src/utils/common"
+
let selectedCodeForContext = '';
let contextFilePath = ''; // 存储当前上下文文件路径
+let streamingMessageElement = null; // 用于存储当前流式消息的DOM元素
+let streamingMessageContent = ''; // 用于存储当前流式消息的内容
+let currentReader = null; // 用于存储当前流的reader,以便可以取消
+let isRequestInProgress = false; // 标记是否有请求正在进行
const vscode = acquireVsCodeApi();
document.addEventListener('DOMContentLoaded', () => {
@@ -7,18 +13,33 @@ document.addEventListener('DOMContentLoaded', () => {
const userInput = document.getElementById('user-input');
const chatBox = document.getElementById('chat-box');
const loading = document.getElementById('loading');
-
+ const sendBtn = document.getElementById('send-btn'); // 获取发送按钮
+
// 初始化隐藏工作区
const workspaceContainer = document.querySelector('.workspace-container');
if (workspaceContainer) {
workspaceContainer.style.display = 'none';
}
- chatForm.addEventListener('submit', (e) => {
+ chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
+
+ // 如果有请求正在进行,点击按钮将中断请求
+ if (isRequestInProgress) {
+ // 发送取消请求的消息到扩展
+ vscode.postMessage({
+ command: 'cancelRequest'
+ });
+
+ resetSendButton();
+ hideLoading();
+ return;
+ }
+
const text = userInput.value.trim();
if (!text) return;
+ // 发送消息并禁用按钮
vscode.postMessage({
command: 'ask',
text,
@@ -29,8 +50,36 @@ document.addEventListener('DOMContentLoaded', () => {
// addMessage('user', text);
showLoading();
userInput.value = '';
+
+ // 切换按钮为停止按钮
+ isRequestInProgress = true;
+ if (sendBtn) {
+ sendBtn.textContent = '停止';
+ sendBtn.classList.add('stop-button');
+ }
});
+ // 重置发送按钮为初始状态
+ function resetSendButton() {
+ isRequestInProgress = false;
+ const sendBtn = document.getElementById('send-btn');
+ if (sendBtn) {
+ sendBtn.textContent = '发送';
+ sendBtn.classList.remove('stop-button');
+ }
+ }
+
+ // 显示加载状态
+ function showLoading() {
+ loading.classList.remove('hidden');
+ chatBox.scrollTop = chatBox.scrollHeight;
+ }
+
+ // 隐藏加载状态
+ function hideLoading() {
+ loading.classList.add('hidden');
+ }
+
// 判断内容是否为 Markdown(简单判断)
function isMarkdown(content) {
if (!content || content.trim() === '') return false;
@@ -49,7 +98,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
// 处理添加消息
- function addMessage(role, content, codeDiff) {
+ function addMessage(role, content, codeDiffs) {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${role}`;
@@ -64,24 +113,29 @@ document.addEventListener('DOMContentLoaded', () => {
msgDiv.appendChild(div);
}
- // 如果有代码差异,添加查看差异按钮
- if (codeDiff) {
- const diffButton = document.createElement('button');
- diffButton.className = 'diff-button';
- diffButton.textContent = '查看代码差异';
- diffButton.onclick = () => showCodeDiff(codeDiff);
- msgDiv.appendChild(diffButton);
- }
-
- // 如果是AI消息且包含代码块,添加"创建文件"按钮
+ // 如果是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);
+
+ // 将按钮插入到代码块后面
+ block.parentNode.after(createFileButton);
+
+ // 如果有代码差异,也为每个代码块添加查看差异按钮
+ // codeDiffs是一个数组,每个元素对应一个代码块的差异信息
+ if (codeDiffs && codeDiffs[index]) {
+ const diffButton = document.createElement('button');
+ diffButton.className = 'diff-button';
+ diffButton.textContent = `查看代码差异`;
+ diffButton.onclick = () => showCodeDiff(codeDiffs[index]);
+ // 将差异按钮插入到创建文件按钮后面
+ createFileButton.after(diffButton);
+ }
});
}
@@ -97,6 +151,147 @@ document.addEventListener('DOMContentLoaded', () => {
// 滚动到底部
chatBox.scrollTop = chatBox.scrollHeight;
+
+ return msgDiv; // 返回创建的消息元素
+ }
+
+ // 开始流式传输
+ function startStreaming() {
+ // 创建一个新的AI消息元素
+ streamingMessageElement = document.createElement('div');
+ streamingMessageElement.className = 'message ai streaming';
+
+ // 添加流式消息内容容器
+ const contentDiv = document.createElement('div');
+ contentDiv.className = 'streaming-content';
+ streamingMessageElement.appendChild(contentDiv);
+
+ // 添加光标元素
+ const cursor = document.createElement('span');
+ cursor.className = 'streaming-cursor';
+ cursor.textContent = '▋';
+ streamingMessageElement.appendChild(cursor);
+
+ chatBox.appendChild(streamingMessageElement);
+ streamingMessageContent = '';
+
+ // 滚动到底部
+ chatBox.scrollTop = chatBox.scrollHeight;
+ }
+
+ // 处理流式数据
+ function handleStreamData(data) {
+ if (!streamingMessageElement) return;
+
+ let processedData = data;
+ if (data.startsWith('data:')) {
+ // 移除data:前缀
+ processedData = data.replace(/^data:/, '');
+ // 逐个匹配换行符,当出现两个或更多连续换行符时删除多余的
+ processedData = processedData.replace(/^(\n{2,})/, (match) => {
+ // 删除所有连续的换行符
+ return '';
+ });
+ }
+
+ // 更新内容
+ streamingMessageContent += processedData;
+
+ // 更新显示
+ const contentDiv = streamingMessageElement.querySelector('.streaming-content');
+ if (contentDiv) {
+ // 如果是Markdown内容,我们需要特殊处理
+ if (isMarkdown(streamingMessageContent)) {
+ contentDiv.innerHTML = marked.parse(streamingMessageContent);
+ } else {
+ contentDiv.textContent = streamingMessageContent;
+ }
+
+ // 高亮代码块
+ const codeBlocks = contentDiv.querySelectorAll('pre code');
+ codeBlocks.forEach(block => {
+ if (hljs && !block.dataset.highlighted) {
+ hljs.highlightElement(block);
+ block.dataset.highlighted = 'true';
+ }
+ });
+ }
+
+ // 滚动到底部
+ chatBox.scrollTop = chatBox.scrollHeight;
+ }
+
+ // 结束流式传输
+ function endStreaming(content, codeDiffs = null) {
+ if (streamingMessageElement) {
+ // 移除流式传输类和光标
+ streamingMessageElement.classList.remove('streaming');
+ const cursor = streamingMessageElement.querySelector('.streaming-cursor');
+ if (cursor) {
+ cursor.remove();
+ }
+
+ // 如果是AI消息且包含代码块,为每个代码块添加按钮
+ if (streamingMessageElement.classList.contains('ai')) {
+ const codeBlocks = streamingMessageElement.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);
+
+ // 将按钮插入到代码块后面
+ block.parentNode.after(createFileButton);
+
+ // 如果有代码差异,也为每个代码块添加查看差异按钮
+ // codeDiffs是一个数组,每个元素对应一个代码块的差异信息
+ if (codeDiffs && codeDiffs[index]) {
+ const diffButton = document.createElement('button');
+ diffButton.className = 'diff-button';
+ diffButton.textContent = `查看代码差异`;
+ diffButton.onclick = () => showCodeDiff(codeDiffs[index]);
+ // 将差异按钮插入到创建文件按钮后面
+ createFileButton.after(diffButton);
+ }
+ });
+ }
+
+ // 高亮代码块
+ const codeBlocks = streamingMessageElement.querySelectorAll('pre code');
+ codeBlocks.forEach(block => {
+ if (hljs) {
+ hljs.highlightElement(block);
+ }
+ });
+
+ streamingMessageElement = null;
+ streamingMessageContent = '';
+ }
+
+ // 保存消息到历史记录
+ currentSessionHistory.push({role: 'assistant', content: content});
+
+ hideLoading();
+ }
+
+ // 处理请求取消
+ function handleRequestCancelled() {
+ // 重置发送按钮
+ resetSendButton();
+
+ // 如果有正在流式传输的消息,结束它
+ if (streamingMessageElement) {
+ streamingMessageElement.classList.remove('streaming');
+ const cursor = streamingMessageElement.querySelector('.streaming-cursor');
+ if (cursor) {
+ cursor.remove();
+ }
+ streamingMessageElement = null;
+ streamingMessageContent = '';
+ }
+
+ hideLoading();
}
// 显示代码差异
@@ -111,16 +306,16 @@ document.addEventListener('DOMContentLoaded', () => {
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);
@@ -132,16 +327,16 @@ document.addEventListener('DOMContentLoaded', () => {
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);
@@ -164,7 +359,7 @@ document.addEventListener('DOMContentLoaded', () => {
filePath: contextFilePath,
modifiedContent: modifiedCode
});
-
+
// 关闭模态框
document.getElementById('diff-modal').classList.add('hidden');
}
@@ -178,25 +373,17 @@ document.addEventListener('DOMContentLoaded', () => {
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';
@@ -206,19 +393,19 @@ document.addEventListener('DOMContentLoaded', () => {
// 显示工作区
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 = '✓ 已接受';
@@ -232,13 +419,13 @@ document.addEventListener('DOMContentLoaded', () => {
statusText = '待处理';
statusClass = 'status-pending';
}
-
+
fileItem.innerHTML = `
📄
${fileName}
${statusText}
`;
-
+
fileItem.addEventListener('click', () => {
// 发送消息到插件以打开文件
vscode.postMessage({
@@ -246,7 +433,7 @@ document.addEventListener('DOMContentLoaded', () => {
filePath: filePath
});
});
-
+
workspaceFilesContainer.appendChild(fileItem);
});
}
@@ -255,7 +442,19 @@ document.addEventListener('DOMContentLoaded', () => {
const message = event.data;
console.log(message)
if (message.command === 'addMessage') {
- addMessage(message.role, message.content,message.codeDiff);
+ addMessage(message.role, message.content, message.codeDiff);
+ } else if (message.command === 'startStream') {
+ // 开始流式传输
+ startStreaming();
+ } else if (message.command === 'streamData') {
+ // 处理流式数据
+ handleStreamData(message.data);
+ } else if (message.command === 'endStream') {
+ // 结束流式传输
+ endStreaming(message.content, message.codeDiffs);
+ } else if (message.command === 'requestCancelled') {
+ // 请求被取消
+ handleRequestCancelled();
} else if (message.command === 'hideLoading') {
hideLoading();
} else if (message.command === 'addToInput') {
@@ -392,6 +591,7 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('diff-modal').classList.add('hidden');
}
});
+
// 从代码创建新文件
function createNewFileFromCode(codeContent, index) {
// 简单的语言检测
@@ -407,41 +607,4 @@ document.addEventListener('DOMContentLoaded', () => {
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(' {
+ this._messageHandler.handleMessage(message);
+ });
+ }
+
+ public sendSelectedCode(code: string) {
+ if (this._view) {
+ this._view.show?.(true);
+ this._postMessage({
+ command: 'addToInput',
+ role: 'user',
+ content: code
+ });
+ }
+ }
+
+ private _getHtmlForWebview(webview: vscode.Webview) {
+ const styleUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'style.css'))
+ );
+
+ const scriptUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'script.js'))
+ );
+
+ const highlightScriptUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'highlight.min.js'))
+ );
+
+ const highlightStyleUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'styles', 'atom-one-dark.css'))
+ );
+
+ const markedScriptUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'marked.min.js'))
+ );
+
+ return getWebviewContent(styleUri, scriptUri, highlightScriptUri, highlightStyleUri, markedScriptUri);
+ }
+
+ public _postMessage(message: any) {
+ if (this._view) {
+ this._view.webview.postMessage(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MessageHandler.ts b/src/MessageHandler.ts
new file mode 100644
index 0000000..3b0c55c
--- /dev/null
+++ b/src/MessageHandler.ts
@@ -0,0 +1,489 @@
+import vscode from "vscode";
+import path from "path";
+import {callQwenAPI} from "./utils/modelApi";
+import {generateCodeDiff, getFileExtension} from "./utils/common";
+
+// 定义全局变量来存储面板状态
+let currentSessionHistory: { role: string; content: string }[] = [];
+// 存储工作区变更的文件,包含状态信息
+let workspaceChanges: {
+ [key: string]: {
+ original: string;
+ modified: string;
+ status: 'pending' | 'accepted' | 'rejected'
+ }
+} = {};
+
+export class MessageHandler {
+ private provider: any; // 这里应该使用AISidebarViewProvider类型,但由于循环依赖问题,暂时使用any
+ private context: vscode.ExtensionContext;
+ private currentReader: ReadableStreamDefaultReader | null = null; // 添加当前reader引用
+
+ constructor(provider: any, context: vscode.ExtensionContext) {
+ this.provider = provider;
+ this.context = context;
+ this.currentReader = null;
+ }
+
+ public async handleMessage(message: any) {
+ switch (message.command) {
+ case 'ask':
+ await this.handleAsk(message);
+ break;
+ case 'cancelRequest':
+ await this.handleCancelRequest();
+ break;
+ case 'selectContextFile':
+ await this.handleSelectContextFile();
+ break;
+ case 'getFileList':
+ await this.handleGetFileList();
+ break;
+ case 'selectFileByPath':
+ await this.handleSelectFileByPath(message);
+ break;
+ case 'showCodeDiffInEditor':
+ await this.handleShowCodeDiffInEditor(message);
+ break;
+ case 'acceptChanges':
+ await this.handleAcceptChanges(message);
+ break;
+ case 'rejectChanges':
+ await this.handleRejectChanges(message);
+ break;
+ case 'openWorkspaceFile':
+ await this.handleOpenWorkspaceFile(message);
+ break;
+ case 'getCodeChanges':
+ this.handleGetCodeChanges();
+ break;
+ case 'createNewFile':
+ await this.handleCreateNewFile(message);
+ break;
+ case 'restoreState':
+ this.handleRestoreState(message);
+ break;
+ }
+ }
+
+ private async handleAsk(message: any) {
+ const question = message.text;
+ const fileContent = message.fileContent;
+ const fileName = message.fileContentPath ? path.basename(message.fileContentPath) : "current_file.txt";
+ const history = currentSessionHistory;
+
+ currentSessionHistory = [...history, {role: 'user', content: question}];
+
+ this.provider._postMessage({command: 'addMessage', role: 'user', content: question});
+
+ try {
+ const response = await callQwenAPI(question, history, fileContent, this.context, fileName);
+
+ // 发送开始流式传输的消息
+ this.provider._postMessage({command: 'startStream', role: 'assistant'});
+
+ // 处理流式响应
+ const reader = response.body?.getReader();
+ this.currentReader = reader; // 保存reader引用
+ const decoder = new TextDecoder('utf-8');
+ let aiMessage = '';
+
+ if (reader) {
+ try {
+ while (true) {
+ const {done, value} = await reader.read();
+ if (done) break;
+
+ const chunk = decoder.decode(value, {stream: true});
+
+ // 检查是否包含event: end事件
+ let processedChunk = chunk;
+ if (chunk.includes('event: end')) {
+ // 只处理event: end之前的内容
+ const endIndex = chunk.indexOf('event: end');
+ processedChunk = chunk.substring(0, endIndex);
+
+ // 处理截取后的内容
+ if (processedChunk.startsWith('data:')) {
+ // 移除data:前缀
+ processedChunk = processedChunk.replace(/^data:/, '');
+ // 逐个匹配换行符,当出现两个或更多连续换行符时删除多余的
+ processedChunk = processedChunk.replace(/^(\n{2,})/, (match) => {
+ // 删除所有连续的换行符
+ return '';
+ });
+ }
+
+ if (processedChunk) {
+ aiMessage += processedChunk;
+
+ // 发送流式数据到前端
+ this.provider._postMessage({
+ command: 'streamData',
+ data: processedChunk
+ });
+ }
+
+ // 停止继续处理
+ break;
+ }
+
+ if (chunk.startsWith('data:')) {
+ // 移除data:前缀
+ processedChunk = chunk.replace(/^data:/, '');
+ // 逐个匹配换行符,当出现两个或更多连续换行符时删除多余的
+ processedChunk = processedChunk.replace(/^(\n{2,})/, (match) => {
+ // 删除所有连续的换行符
+ return '';
+ });
+ }
+
+ aiMessage += processedChunk;
+
+ // 发送流式数据到前端
+ this.provider._postMessage({
+ command: 'streamData',
+ data: processedChunk
+ });
+ }
+ } catch (readError: any) {
+ // 检查是否是由于取消请求导致的中断
+ if (readError.name === 'AbortError' || readError.message.includes('cancel')) {
+ console.log('请求已被用户中断');
+ // 通知前端请求已中断
+ this.provider._postMessage({command: 'requestCancelled'});
+ // 重置状态但不抛出错误
+ this.provider._postMessage({command: 'hideLoading'});
+ return;
+ }
+ // 如果是其他错误,重新抛出
+ throw readError;
+ } finally {
+ // 清理reader引用
+ this.currentReader = null;
+ }
+
+ // 流传输完成后,更新会话历史
+ currentSessionHistory = [...currentSessionHistory, {role: 'assistant', content: aiMessage}];
+
+ // 解析AI响应中的多个代码块并生成独立的codeDiff信息
+ const codeBlocks = aiMessage.match(/```[\s\S]*?```/g) || [];
+
+ // 存储所有生成的codeDiff
+ const allCodeDiffs = [];
+
+ for (const block of codeBlocks) {
+ // 生成codeDiff
+ const codeDiff = generateCodeDiff(fileContent, block);
+
+ if (codeDiff) {
+ // @ts-ignore
+ allCodeDiffs.push(codeDiff);
+ }
+ }
+
+ console.log("allCodeDiffs:",allCodeDiffs)
+
+ // 通知前端流传输完成
+ this.provider._postMessage({
+ command: 'endStream',
+ content: aiMessage,
+ codeDiffs: allCodeDiffs // 传递多个codeDiff信息
+ });
+
+ // 如果有代码差异,保存到工作区变更中
+ if (allCodeDiffs.length > 0 && fileContent && message.fileContentPath) {
+ // 为每个codeDiff创建独立的workspaceChange
+ for (let i = 0; i < allCodeDiffs.length; i++) {
+ const codeDiff = allCodeDiffs[i];
+ const filePath = message.fileContentPath;
+
+ // 创建唯一的工作区变更键
+ const changeKey = `${filePath}`;
+
+
+ workspaceChanges[changeKey] = {
+ original: fileContent,
+ // @ts-ignore
+ modified: codeDiff.modifiedCode,
+ status: 'pending'
+ };
+ }
+
+ // 通知 WebView 更新工作区文件列表
+ this.provider._postMessage({
+ command: 'updateWorkspaceFiles',
+ files: workspaceChanges
+ });
+ }
+ }
+ } catch (error: any) {
+ // 检查是否是由于取消请求导致的中断
+ if (error.name === 'AbortError' || error.message.includes('cancel') || error.message.includes('中断')) {
+ console.log('请求已被用户中断');
+ this.provider._postMessage({command: 'requestCancelled'});
+ } else {
+ this.provider._postMessage({command: 'addMessage', role: 'ai', content: '调用失败:' + error.message});
+ }
+ }
+
+ this.provider._postMessage({command: 'hideLoading'});
+ }
+
+ private async handleSelectContextFile() {
+ // 执行文件选择逻辑
+ const uris = await vscode.window.showOpenDialog({
+ canSelectFiles: true,
+ canSelectFolders: false,
+ canSelectMany: false,
+ title: "选择一个文件作为上下文"
+ });
+
+ if (uris && uris.length > 0) {
+ const selectedFileUri = uris[0];
+ const fileContent = await vscode.workspace.fs.readFile(selectedFileUri);
+ const decodedContent = new TextDecoder("utf-8").decode(fileContent);
+
+ this.provider._postMessage({
+ command: 'setContextFile',
+ fileName: selectedFileUri.fsPath,
+ fileContent: decodedContent
+ });
+ }
+ }
+
+ private async handleGetFileList() {
+ // 执行获取文件列表逻辑
+ const workspaceFolders = vscode.workspace.workspaceFolders;
+ if (workspaceFolders && workspaceFolders.length > 0) {
+ const fileUris = await vscode.workspace.findFiles('**/*', '**/node_modules/**');
+
+ const files = fileUris.map(uri => {
+ const relativePath = vscode.workspace.asRelativePath(uri);
+ return {
+ path: uri.fsPath,
+ relativePath: relativePath,
+ name: path.basename(uri.fsPath),
+ isDirectory: false // 简化处理,实际可以根据扩展名判断
+ };
+ });
+
+ this.provider._postMessage({
+ command: 'fileList',
+ files: files
+ });
+ }
+ }
+
+ private async handleSelectFileByPath(message: any) {
+ // 根据路径选择文件
+ try {
+ const fileUri = vscode.Uri.file(message.filePath);
+ const fileContent = await vscode.workspace.fs.readFile(fileUri);
+ const decodedContent = new TextDecoder("utf-8").decode(fileContent);
+
+ this.provider._postMessage({
+ command: 'setContextFile',
+ fileName: fileUri.fsPath,
+ fileContent: decodedContent
+ });
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`无法读取文件: ${error.message}`);
+ }
+ }
+
+ private async handleShowCodeDiffInEditor(message: any) {
+ // 在编辑器中显示代码差异
+ try {
+ const originalCode = message.originalCode || '';
+ const modifiedCode = message.modifiedCode || '';
+
+ // 创建原始代码的虚拟文档
+ const originalUri = vscode.Uri.parse('untitled:original-code.txt');
+ const originalDoc = await vscode.workspace.openTextDocument(originalUri);
+ const originalEditor = await vscode.window.showTextDocument(originalDoc, vscode.ViewColumn.One, true);
+ await originalEditor.edit(edit => {
+ edit.insert(new vscode.Position(0, 0), originalCode);
+ });
+
+ // 创建修改后代码的虚拟文档
+ const modifiedUri = vscode.Uri.parse('untitled:modified-code.txt');
+ const modifiedDoc = await vscode.workspace.openTextDocument(modifiedUri);
+ const modifiedEditor = await vscode.window.showTextDocument(modifiedDoc, vscode.ViewColumn.Two, true);
+ await modifiedEditor.edit(edit => {
+ edit.insert(new vscode.Position(0, 0), modifiedCode);
+ });
+
+ // 使用 VS Code 的 diff 命令显示差异
+ await vscode.commands.executeCommand(
+ 'vscode.diff',
+ originalUri,
+ modifiedUri,
+ 'Code Diff - Original ↔ Modified'
+ );
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`显示代码差异失败: ${error.message}`);
+ }
+ }
+
+ private async handleAcceptChanges(message: any) {
+ // 接受代码变更
+ try {
+ const {filePath, modifiedContent} = message;
+ const fileUri = vscode.Uri.file(filePath);
+
+ // 将修改后的内容写入文件
+ const encodedContent = new TextEncoder().encode(modifiedContent);
+ await vscode.workspace.fs.writeFile(fileUri, encodedContent);
+
+ // 更新工作区变更状态
+ if (workspaceChanges[filePath]) {
+ workspaceChanges[filePath].status = 'accepted';
+ }
+
+ // 通知 WebView 更新工作区文件列表
+ this.provider._postMessage({
+ command: 'updateWorkspaceFiles',
+ files: workspaceChanges
+ });
+
+ vscode.window.showInformationMessage('代码变更已应用');
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`应用代码变更失败: ${error.message}`);
+ }
+ }
+
+ private async handleRejectChanges(message: any) {
+ // 拒绝代码变更
+ try {
+ const {filePath} = message;
+
+ // 更新工作区变更状态
+ if (workspaceChanges[filePath]) {
+ workspaceChanges[filePath].status = 'rejected';
+ }
+
+ // 通知 WebView 更新工作区文件列表
+ this.provider._postMessage({
+ command: 'updateWorkspaceFiles',
+ files: workspaceChanges
+ });
+
+ vscode.window.showInformationMessage('代码变更已拒绝');
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`拒绝代码变更失败: ${error.message}`);
+ }
+ }
+
+ private async handleOpenWorkspaceFile(message: any) {
+ // 打开工作区文件
+ try {
+ const {filePath} = message;
+ const fileUri = vscode.Uri.file(filePath);
+
+ // 检查文件是否已经打开
+ let fileAlreadyOpen = false;
+ const openedTextDocuments = vscode.workspace.textDocuments;
+
+ for (const doc of openedTextDocuments) {
+ if (doc.uri.fsPath === fileUri.fsPath) {
+ fileAlreadyOpen = true;
+ // 激活已打开的文件
+ await vscode.window.showTextDocument(doc, {
+ viewColumn: vscode.ViewColumn.One // 切换到已打开的文件
+ });
+ break;
+ }
+ }
+
+ // 如果文件未打开,则在新窗口打开
+ if (!fileAlreadyOpen) {
+ await vscode.window.showTextDocument(fileUri, {
+ viewColumn: vscode.ViewColumn.One // 在第一列打开文件
+ });
+ }
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`打开文件失败: ${error.message}`);
+ }
+ }
+
+ private handleGetCodeChanges() {
+ // 获取代码变更
+ this.provider._postMessage({
+ command: 'codeChanges',
+ changes: workspaceChanges
+ });
+ }
+
+ private async handleCreateNewFile(message: any) {
+ // 创建新文件功能
+ try {
+ const {fileName, content, language} = message;
+
+ // 获取根工作区路径
+ const workspaceFolders = vscode.workspace.workspaceFolders;
+ if (!workspaceFolders || workspaceFolders.length === 0) {
+ vscode.window.showErrorMessage('未找到工作区');
+ return;
+ }
+
+ // 根据语言确定文件扩展名
+ const fileExtension = getFileExtension(language);
+ const fullFileName = fileName.endsWith(fileExtension) ? fileName : `${fileName}${fileExtension}`;
+
+ // 创建文件路径(在根目录下)
+ const filePath = path.join(workspaceFolders[0].uri.fsPath, fullFileName);
+ const fileUri = vscode.Uri.file(filePath);
+
+ // 将内容写入文件
+ const encodedContent = new TextEncoder().encode(content);
+ await vscode.workspace.fs.writeFile(fileUri, encodedContent);
+
+ // 在编辑器中打开新创建的文件
+ await vscode.window.showTextDocument(fileUri, {
+ viewColumn: vscode.ViewColumn.One
+ });
+
+ vscode.window.showInformationMessage(`文件 ${fullFileName} 已创建`);
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`生成代码文件失败: ${error.message}`);
+ }
+ }
+
+ private handleRestoreState(message: any) {
+ // 恢复面板状态
+ currentSessionHistory = message.history || [];
+
+ // 恢复显示历史消息
+ currentSessionHistory.forEach(msg => {
+ this.provider._postMessage({
+ command: 'addMessage',
+ role: msg.role,
+ content: msg.content,
+ codeDiff: null
+ });
+ });
+
+ // 恢复工作区变更
+ if (message.workspaceChanges) {
+ this.provider._postMessage({
+ command: 'updateWorkspaceFiles',
+ files: message.workspaceChanges
+ });
+ }
+ }
+
+ // 添加处理取消请求的方法
+ private async handleCancelRequest() {
+ console.log('收到取消请求');
+ if (this.currentReader) {
+ try {
+ await this.currentReader.cancel();
+ this.currentReader = null;
+ console.log('请求已取消');
+ } catch (error) {
+ console.error('取消请求时出错:', error);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index e3d244b..d3726a1 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,18 +1,5 @@
import * as vscode from 'vscode';
-import * as path from 'path';
-import {callQwenAPI} from "./utils/modelApi";
-import {generateCodeDiff,getFileExtension} from "./utils/common";
-
-// 定义全局变量来存储面板状态
-let currentSessionHistory: { role: string; content: string }[] = [];
-// 存储工作区变更的文件,包含状态信息
-let workspaceChanges: {
- [key: string]: {
- original: string;
- modified: string;
- status: 'pending' | 'accepted' | 'rejected'
- }
-} = {};
+import {AISidebarViewProvider} from "./AISidebarViewProvider"
export function activate(context: vscode.ExtensionContext) {
console.log('Extension activated');
@@ -30,7 +17,7 @@ export function activate(context: vscode.ExtensionContext) {
vscode.window.registerWebviewViewProvider(
'ai-chat-sidebar-view',
provider,
- { webviewOptions: { retainContextWhenHidden: true } }
+ {webviewOptions: {retainContextWhenHidden: true}}
)
);
@@ -64,448 +51,3 @@ export function activate(context: vscode.ExtensionContext) {
// 添加到 subscriptions
context.subscriptions.push(statusBarItem, openWebviewCommand, addToChatCommand);
}
-
-class AISidebarViewProvider implements vscode.WebviewViewProvider {
- public static readonly viewType = 'ai-chat-sidebar-view';
-
- private _view?: vscode.WebviewView;
- private _extensionPath: string;
- private _context: vscode.ExtensionContext;
-
- constructor(extensionPath: string, context: vscode.ExtensionContext) {
- this._extensionPath = extensionPath;
- this._context = context;
- }
-
- public resolveWebviewView(
- webviewView: vscode.WebviewView,
- context: vscode.WebviewViewResolveContext,
- _token: vscode.CancellationToken,
- ) {
- this._view = webviewView;
-
- webviewView.webview.options = {
- enableScripts: true,
- localResourceRoots: [
- vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview'))
- ]
- };
-
- webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
-
- webviewView.webview.onDidReceiveMessage(async (message) => {
- switch (message.command) {
- case 'ask':
- const question = message.text;
- const fileContent = message.fileContent;
- const history = currentSessionHistory;
-
- currentSessionHistory = [...history, { role: 'user', content: question }];
-
- this._postMessage({ command: 'addMessage', role: 'user', content: question });
-
- try {
- const response = await callQwenAPI(question, history, fileContent, this._context);
- console.log("response:", response);
- const aiMessage = response.choices[0]?.message?.content || '未获取到回复';
-
- // 检查是否包含代码修改
- const codeDiff = generateCodeDiff(fileContent, aiMessage);
- console.log("codeDiff:",codeDiff)
-
- // 如果有代码差异,保存到工作区变更中
- if (codeDiff && fileContent && message.fileContentPath) {
- workspaceChanges[message.fileContentPath] = {
- original: fileContent,
- modified: codeDiff.modifiedCode,
- status: 'pending'
- };
-
- // 通知 WebView 更新工作区文件列表
- this._postMessage({
- command: 'updateWorkspaceFiles',
- files: workspaceChanges
- });
- }
-
- // 更新当前会话历史
- currentSessionHistory = [...currentSessionHistory, { role: 'assistant', content: aiMessage }];
-
- this._postMessage({
- command: 'addMessage',
- role: 'ai',
- content: aiMessage,
- codeDiff: codeDiff // 发送代码差异信息
- });
- } catch (error: any) {
- this._postMessage({ command: 'addMessage', role: 'ai', content: '调用失败:' + error.message });
- }
-
- this._postMessage({ command: 'hideLoading' });
- break;
- case 'selectContextFile':
- // 执行文件选择逻辑
- const uris = await vscode.window.showOpenDialog({
- canSelectFiles: true,
- canSelectFolders: false,
- canSelectMany: false,
- title: "选择一个文件作为上下文"
- });
-
- if (uris && uris.length > 0) {
- const selectedFileUri = uris[0];
- const fileContent = await vscode.workspace.fs.readFile(selectedFileUri);
- const decodedContent = new TextDecoder("utf-8").decode(fileContent);
-
- this._postMessage({
- command: 'setContextFile',
- fileName: selectedFileUri.fsPath,
- fileContent: decodedContent
- });
- }
- break;
- case 'getFileList':
- // 执行获取文件列表逻辑
- const workspaceFolders = vscode.workspace.workspaceFolders;
- if (workspaceFolders && workspaceFolders.length > 0) {
- const fileUris = await vscode.workspace.findFiles('**/*', '**/node_modules/**');
-
- const files = fileUris.map(uri => {
- const relativePath = vscode.workspace.asRelativePath(uri);
- return {
- path: uri.fsPath,
- relativePath: relativePath,
- name: path.basename(uri.fsPath),
- isDirectory: false // 简化处理,实际可以根据扩展名判断
- };
- });
-
- this._postMessage({
- command: 'fileList',
- files: files
- });
- }
- break;
-
- case 'selectFileByPath':
- // 根据路径选择文件
- try {
- const fileUri = vscode.Uri.file(message.filePath);
- const fileContent = await vscode.workspace.fs.readFile(fileUri);
- const decodedContent = new TextDecoder("utf-8").decode(fileContent);
-
- this._postMessage({
- command: 'setContextFile',
- fileName: fileUri.fsPath,
- fileContent: decodedContent
- });
- } catch (error: any) {
- vscode.window.showErrorMessage(`无法读取文件: ${error.message}`);
- }
- break;
- case 'showCodeDiffInEditor':
- // 在编辑器中显示代码差异
- try {
- const originalCode = message.originalCode || '';
- const modifiedCode = message.modifiedCode || '';
-
- // 创建原始代码的虚拟文档
- const originalUri = vscode.Uri.parse('untitled:original-code.txt');
- const originalDoc = await vscode.workspace.openTextDocument(originalUri);
- const originalEditor = await vscode.window.showTextDocument(originalDoc, vscode.ViewColumn.One, true);
- await originalEditor.edit(edit => {
- edit.insert(new vscode.Position(0, 0), originalCode);
- });
-
- // 创建修改后代码的虚拟文档
- const modifiedUri = vscode.Uri.parse('untitled:modified-code.txt');
- const modifiedDoc = await vscode.workspace.openTextDocument(modifiedUri);
- const modifiedEditor = await vscode.window.showTextDocument(modifiedDoc, vscode.ViewColumn.Two, true);
- await modifiedEditor.edit(edit => {
- edit.insert(new vscode.Position(0, 0), modifiedCode);
- });
-
- // 使用 VS Code 的 diff 命令显示差异
- await vscode.commands.executeCommand(
- 'vscode.diff',
- originalUri,
- modifiedUri,
- 'Code Diff - Original ↔ Modified'
- );
- } catch (error: any) {
- vscode.window.showErrorMessage(`显示代码差异失败: ${error.message}`);
- }
- break;
- case 'acceptChanges':
- // 接受代码变更
- try {
- const { filePath, modifiedContent } = message;
- const fileUri = vscode.Uri.file(filePath);
-
- // 将修改后的内容写入文件
- const encodedContent = new TextEncoder().encode(modifiedContent);
- await vscode.workspace.fs.writeFile(fileUri, encodedContent);
-
- // 更新工作区变更状态
- if (workspaceChanges[filePath]) {
- workspaceChanges[filePath].status = 'accepted';
- }
-
- // 通知 WebView 更新工作区文件列表
- this._postMessage({
- command: 'updateWorkspaceFiles',
- files: workspaceChanges
- });
-
- vscode.window.showInformationMessage('代码变更已应用');
- } catch (error: any) {
- vscode.window.showErrorMessage(`应用代码变更失败: ${error.message}`);
- }
- break;
- case 'rejectChanges':
- // 拒绝代码变更
- try {
- const { filePath } = message;
-
- // 更新工作区变更状态
- if (workspaceChanges[filePath]) {
- workspaceChanges[filePath].status = 'rejected';
- }
-
- // 通知 WebView 更新工作区文件列表
- this._postMessage({
- command: 'updateWorkspaceFiles',
- files: workspaceChanges
- });
-
- vscode.window.showInformationMessage('代码变更已拒绝');
- } catch (error: any) {
- vscode.window.showErrorMessage(`拒绝代码变更失败: ${error.message}`);
- }
- break;
- case 'openWorkspaceFile':
- // 打开工作区文件
- try {
- const { filePath } = message;
- const fileUri = vscode.Uri.file(filePath);
-
- // 检查文件是否已经打开
- let fileAlreadyOpen = false;
- const openedTextDocuments = vscode.workspace.textDocuments;
-
- for (const doc of openedTextDocuments) {
- if (doc.uri.fsPath === fileUri.fsPath) {
- fileAlreadyOpen = true;
- // 激活已打开的文件
- await vscode.window.showTextDocument(doc, {
- viewColumn: vscode.ViewColumn.One // 切换到已打开的文件
- });
- break;
- }
- }
-
- // 如果文件未打开,则在新窗口打开
- if (!fileAlreadyOpen) {
- await vscode.window.showTextDocument(fileUri, {
- viewColumn: vscode.ViewColumn.One // 在第一列打开文件
- });
- }
- } catch (error: any) {
- vscode.window.showErrorMessage(`打开文件失败: ${error.message}`);
- }
- break;
- case 'getCodeChanges':
- // 获取代码变更
- this._postMessage({
- command: 'codeChanges',
- changes: workspaceChanges
- });
- break;
- case 'createNewFile':
- // 创建新文件功能
- try {
- const { fileName, content, language } = message;
-
- // 获取根工作区路径
- const workspaceFolders = vscode.workspace.workspaceFolders;
- if (!workspaceFolders || workspaceFolders.length === 0) {
- vscode.window.showErrorMessage('未找到工作区');
- return;
- }
-
- // 根据语言确定文件扩展名
- const fileExtension = getFileExtension(language);
- const fullFileName = fileName.endsWith(fileExtension) ? fileName : `${fileName}${fileExtension}`;
-
- // 创建文件路径(在根目录下)
- const filePath = path.join(workspaceFolders[0].uri.fsPath, fullFileName);
- const fileUri = vscode.Uri.file(filePath);
-
- // 将内容写入文件
- const encodedContent = new TextEncoder().encode(content);
- await vscode.workspace.fs.writeFile(fileUri, encodedContent);
-
- // 在编辑器中打开新创建的文件
- await vscode.window.showTextDocument(fileUri, {
- viewColumn: vscode.ViewColumn.One
- });
-
- vscode.window.showInformationMessage(`文件 ${fullFileName} 已创建`);
- } catch (error: any) {
- vscode.window.showErrorMessage(`生成代码文件失败: ${error.message}`);
- }
- break;
- case 'restoreState':
- // 恢复面板状态
- currentSessionHistory = message.history || [];
-
- // 恢复显示历史消息
- currentSessionHistory.forEach(msg => {
- this._postMessage({
- command: 'addMessage',
- role: msg.role,
- content: msg.content,
- codeDiff: null
- });
- });
-
- // 恢复工作区变更
- if (message.workspaceChanges) {
- this._postMessage({
- command: 'updateWorkspaceFiles',
- files: message.workspaceChanges
- });
- }
- break;
- }
- });
- }
-
- public sendSelectedCode(code: string) {
- if (this._view) {
- this._view.show?.(true);
- this._postMessage({
- command: 'addToInput',
- role: 'user',
- content: code
- });
- }
- }
-
- private _getHtmlForWebview(webview: vscode.Webview) {
- const styleUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'style.css'))
- );
-
- const scriptUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'script.js'))
- );
-
- const highlightScriptUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'highlight.min.js'))
- );
-
- const highlightStyleUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'styles', 'atom-one-dark.css'))
- );
-
- const markedScriptUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._extensionPath, 'media', 'webview', 'marked.min.js'))
- );
-
- return getWebviewContent(styleUri, scriptUri, highlightScriptUri, highlightStyleUri, markedScriptUri);
- }
-
- private _postMessage(message: any) {
- if (this._view) {
- this._view.webview.postMessage(message);
- }
- }
-}
-
-function getWebviewContent(styleUri: vscode.Uri, scriptUri: vscode.Uri,highlightScriptUri:vscode.Uri,highlightStyleUri:vscode.Uri,markedScriptUri:vscode.Uri): string {
- return `
-
-
-
-
- AI Chat
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 添加上下文
-
-
-
- 添加上下文
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
AI 正在思考...
-
-
-
-
-
- `;
-}
diff --git a/src/utils/common.ts b/src/utils/common.ts
index bf568fa..1256328 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -81,3 +81,24 @@ export function getFileExtension(language: string): string {
return languageMap[language?.toLowerCase()] || '.txt';
}
+// 简单的语言检测
+export 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('('url');
+ const userBaseUrl = config.get('baseUrl');
+ const userApiKey = config.get('key');
+
+ // 如果用户配置了完整URL,优先使用
+ if (userUrl) {
+ return {
+ url: userUrl,
+ apiKey: userApiKey || 'dev-token'
+ };
+ }
+
+ // 如果用户配置了基础URL,使用基础URL加上默认路径
+ if (userBaseUrl) {
+ return {
+ url: `${userBaseUrl}/comp/api/v1/chat/completions-stream`,
+ apiKey: userApiKey || 'dev-token'
+ };
+ }
+
+ // 检测code-server环境
+ const codeServerUrl = process.env.CODE_SERVER_URL;
+ if (codeServerUrl) {
+ // 在code-server环境中,尝试使用相对路径或者基于当前地址的API地址
+ try {
+ const baseUrl = new URL(codeServerUrl);
+ return {
+ url: `${baseUrl.origin}/comp/api/v1/chat/completions-stream`,
+ apiKey: userApiKey || 'dev-token'
+ };
+ } catch (e) {
+ // 如果解析失败,使用默认地址
+ }
+ }
+
+ // 检查CODE_SERVER_CONFIG环境变量
+ const codeServerConfig = process.env.CODE_SERVER_CONFIG;
+ if (codeServerConfig) {
+ try {
+ // 尝试从配置中解析bindAddr
+ const configObj = JSON.parse(codeServerConfig);
+ if (configObj.bindAddr) {
+ const [host, port] = configObj.bindAddr.split(':');
+ if (host && port) {
+ return {
+ url: `http://${host}:${port}/comp/api/v1/chat/completions-stream`,
+ apiKey: userApiKey || 'dev-token'
+ };
+ }
+ }
+ } catch (e) {
+ // 解析失败则继续使用默认配置
+ }
+ }
+
+ // 默认配置
+ return {
+ url: 'https://p13-ai.ngsk.tech:7001/comp/api/v1/chat/completions-stream',
+ apiKey: userApiKey || 'dev-token'
+ };
+}
export async function callQwenAPI(
question: string,
history: any[],
fileContent: string,
- context: vscode.ExtensionContext
+ context: vscode.ExtensionContext,
+ filename: string = ""
): Promise {
- const apiKey = 'dev-token';
- const url = 'https://aicomp.ngsk.tech:7001/api/v1/chat/completions';
+ const { url, apiKey } = getApiConfig(context);
const messages = [
{
@@ -17,34 +84,41 @@ export async function callQwenAPI(
...history,
{
role: 'user',
- content: `【当前文件内容】:
- \`\`\`${fileContent}\`\`\`
-
- 【用户提问】:
- ${question}`
+ content: question
}
];
- console.log("messages:",messages)
+ console.log("messages:", messages)
try {
- const response = await fetch(url, {
+ const params = {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
+ // 'Accept': 'text/event-stream'
},
body: JSON.stringify({
- model: 'qwen2.5-local',
- messages
+ model: 'Qwen2_5_Coder',
+ messages,
+ context_file: {
+ filename: filename,
+ section_type: "code",
+ content: fileContent
+ }
})
- });
+ }
+ console.log("params:", JSON.stringify(params))
+
+ // @ts-ignore
+ const response = await fetch(url, params);
if (!response.ok) {
+ console.log("请求失败:", response)
throw new Error('API 请求失败');
}
- return await response.json();
+ return response;
} catch (error: any) {
console.error(error);
throw error;
diff --git a/src/utils/webView.ts b/src/utils/webView.ts
new file mode 100644
index 0000000..4abc6e1
--- /dev/null
+++ b/src/utils/webView.ts
@@ -0,0 +1,88 @@
+import vscode from "vscode";
+
+export function getWebviewContent(styleUri: vscode.Uri, scriptUri: vscode.Uri,highlightScriptUri:vscode.Uri,highlightStyleUri:vscode.Uri,markedScriptUri:vscode.Uri): string {
+ return `
+
+
+
+
+ AI Chat
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加上下文
+
+
+
+ 添加上下文
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
AI 正在思考...
+
+
+
+
+
+ `;
+}
\ No newline at end of file