Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a1a8c44824 | ||
|
8261a1291e | ||
|
f96b85d43c | ||
|
1af14cabd3 | ||
|
08a84fbfcd | ||
|
dd67a60fc7 | ||
|
dcbc1ff9de | ||
|
fbf7632a4a | ||
|
4e29badfc3 | ||
|
a22fff22c3 | ||
|
55af7f8c26 | ||
|
95a11f8bf9 | ||
|
d177199470 | ||
|
6977f004d2 | ||
|
3dffeccaa9 | ||
|
384ac4fc80 | ||
|
d8eb37e674 | ||
|
f3fa6a3d0b | ||
|
fd9ac838c4 | ||
|
68a714b80a | ||
|
6994b23aa2 | ||
|
330c18d2ab | ||
|
9122e019b2 | ||
|
9e590955b4 | ||
|
7744e9e49e | ||
|
5350ea703c | ||
|
7af3e2b332 | ||
|
3b793f329c | ||
|
c292129ab8 | ||
|
ac9771b6b7 | ||
|
a299bbeb94 | ||
|
bca9e9e5b2 | ||
|
c692fd359c | ||
|
ad0ca40c5a | ||
|
084ed925c6 | ||
|
b9642e6e5b | ||
|
f9e3e6a3d0 |
46
CHANGELOG.md
@ -4,6 +4,52 @@ Version History
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [Release 0.2.3]
|
||||
|
||||
## What's Changed
|
||||
* fix(display-html): html is now properly displayed by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/38
|
||||
* Release 0.2.3 by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/39
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/Iheuzio/gpt-contextfiles/compare/0.2.2...0.2.3
|
||||
|
||||
## [Release 0.2.2]
|
||||
|
||||
## What's Changed
|
||||
* Fix/organize files by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/32
|
||||
* Fix/style rendered by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/33
|
||||
* fix(style-rendered): padding around response by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/34
|
||||
* update(readme-fix): added new features to readme by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/35
|
||||
* Release 0.2.2 by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/36
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/Iheuzio/gpt-contextfiles/compare/0.2.1...0.2.2
|
||||
|
||||
## [Release 0.2.1]
|
||||
|
||||
## What's Changed
|
||||
* Update/logo by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/28
|
||||
* feature(copy-code): add a copy, button improved ui by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/29
|
||||
* Release 0.2.1 by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/30
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/Iheuzio/gpt-contextfiles/compare/0.2.0...0.2.1
|
||||
|
||||
## [Release 0.2.0]
|
||||
|
||||
- Copy code
|
||||
- Auto response
|
||||
- Formatting
|
||||
|
||||
## What's Changed
|
||||
* Feature/format code by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/23
|
||||
* update(gif): Added new gif to reflect program by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/24
|
||||
* feature(code-box): Can copy code and load responses by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/25
|
||||
* Release 0.2.0 by @Iheuzio in https://github.com/Iheuzio/gpt-contextfiles/pull/26
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/Iheuzio/gpt-contextfiles/compare/0.1.3...0.2.0
|
||||
|
||||
## [Release 0.1.3]
|
||||
|
||||
## What's Changed
|
||||
|
11
README.md
@ -1,5 +1,7 @@
|
||||
# gpt-contextfiles
|
||||
|
||||

|
||||
|
||||
> if you'll like to contribute or provide any feedback check out the [link](https://github.com/Iheuzio/gpt-contextfiles/issues)
|
||||
|
||||
This extension uses the openai api, there are many models avaliable:
|
||||
@ -8,7 +10,7 @@ https://openai.com/pricing
|
||||
|
||||
> However, being orientated with managing files this project defaults to the 16k context with GPT-3.5-turbo-16k
|
||||
|
||||
If you wish to change the model, you must change the model in the extension.js file
|
||||
If you wish to change the model, you must change the model in the `./src/gptContext.js` file
|
||||
|
||||
# Examples
|
||||
|
||||
@ -32,7 +34,12 @@ Refresh -> refreshes the window so that all new files will be available for that
|
||||
- Click on the extension addon to open the context window, refresh to update the files to check.
|
||||
- Select the files uses checkboxes
|
||||
- Wait for response to be returned
|
||||
- Copy the code (orange portion) and paste it into your code editor
|
||||
|
||||
Other features:
|
||||

|
||||
- Click copy to to copy the snippet of code into your file for fast coding
|
||||
|
||||

|
||||
|
||||
# How it works
|
||||
|
||||
|
BIN
images/copy-image.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
images/demo-copying-quotes.gif
Normal file
After Width: | Height: | Size: 12 MiB |
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 13 MiB |
BIN
images/extension.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
images/gpt-banner.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 36 KiB |
BIN
images/gpt-icon.png
Normal file
After Width: | Height: | Size: 97 KiB |
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "gpt-contextfiles",
|
||||
"version": "0.0.1",
|
||||
"version": "0.2.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gpt-contextfiles",
|
||||
"version": "0.0.1",
|
||||
"version": "0.2.3",
|
||||
"dependencies": {
|
||||
"openai": "^3.3.0"
|
||||
},
|
||||
|
15
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "gpt-contextfiles",
|
||||
"displayName": "GPT-ContextFiles",
|
||||
"description": "Choose the files to pass into GPT to provide a question with multiple files (doesn't check context)",
|
||||
"version": "0.2.0",
|
||||
"description": "Choose the files to pass into GPT to provide a question with multiple files",
|
||||
"version": "0.2.3",
|
||||
"engines": {
|
||||
"vscode": "^1.79.0"
|
||||
},
|
||||
@ -22,7 +22,12 @@
|
||||
"testing",
|
||||
"debugging",
|
||||
"files",
|
||||
"api"
|
||||
"api",
|
||||
"snippets",
|
||||
"openai",
|
||||
"vscode",
|
||||
"extension",
|
||||
"chatbot"
|
||||
],
|
||||
"relatedTags": [
|
||||
"Artificial Intelligence",
|
||||
@ -34,7 +39,7 @@
|
||||
"onCommand:extension.openGPTContextPanel",
|
||||
"onCommand:extension.gpt-context-sidebar"
|
||||
],
|
||||
"main": "./extension.js",
|
||||
"main": "./src/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
@ -115,6 +120,6 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/Iheuzio/gpt-contextfiles/"
|
||||
},
|
||||
"icon": "images/gpt-icon.jpg",
|
||||
"icon": "images/gpt-icon.png",
|
||||
"publisher": "Iheuzio"
|
||||
}
|
||||
|
84
src/commands.js
Normal file
@ -0,0 +1,84 @@
|
||||
const vscode = require('vscode');
|
||||
const { selectedFiles, fileDataProvider, handleQuestionSubmission } = require('./gptContext');
|
||||
const FileItem = require('./fileItem');
|
||||
const { getWebviewContent } = require('./webviewPanel');
|
||||
|
||||
const addFilesCommand = vscode.commands.registerCommand('extension.addFilesToGPTContext', () => {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
const uri = editor.document.uri;
|
||||
const existingFileIndex = selectedFiles.findIndex(file => file.uri.fsPath === uri.fsPath);
|
||||
|
||||
if (existingFileIndex !== -1) {
|
||||
// File already exists, remove it from the list
|
||||
selectedFiles.splice(existingFileIndex, 1);
|
||||
} else {
|
||||
// Add the file to the list with selected state
|
||||
selectedFiles.push(new FileItem(uri, true));
|
||||
}
|
||||
|
||||
fileDataProvider.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
const openGPTContextPanelCommand = vscode.commands.registerCommand('extension.openGPTContextPanel', () => {
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'gptContextPanel',
|
||||
'GPT Context',
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true
|
||||
}
|
||||
);
|
||||
|
||||
panel.webview.html = getWebviewContent();
|
||||
|
||||
panel.webview.onDidReceiveMessage(async message => {
|
||||
if (message.command === 'submitQuestion') {
|
||||
await handleQuestionSubmission(panel, message.text, message.selectedUris);
|
||||
} else if (message.command === 'toggleFileSelection') {
|
||||
const uri = message.uri;
|
||||
const file = selectedFiles.find(file => file.uri.fsPath === uri);
|
||||
if (file) {
|
||||
file.toggleSelected();
|
||||
fileDataProvider.refresh();
|
||||
}
|
||||
} else if (message.command === 'clearSelectedFiles') {
|
||||
const clearedFiles = selectedFiles.filter(file => file.selected === false);
|
||||
selectedFiles.length = 0; // Clear the array
|
||||
clearedFiles.forEach(file => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
panel.webview.html = getWebviewContent();
|
||||
} else if (message.command === 'refreshFiles') {
|
||||
fileDataProvider.refresh();
|
||||
panel.webview.html = getWebviewContent();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const refreshSelectedFilesCommand = vscode.commands.registerCommand('extension.refreshSelectedFiles', () => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
|
||||
// Command for clearing the selected files
|
||||
const clearSelectedFilesCommand = vscode.commands.registerCommand('extension.clearSelectedFiles', () => {
|
||||
selectedFiles.forEach(file => {
|
||||
file.selected = false;
|
||||
});
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
|
||||
// Command for refreshing all files
|
||||
const refreshFilesCommand = vscode.commands.registerCommand('extension.refreshFiles', () => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
|
||||
|
||||
module.exports = {
|
||||
addFilesCommand,
|
||||
openGPTContextPanelCommand,
|
||||
refreshSelectedFilesCommand,
|
||||
clearSelectedFilesCommand,
|
||||
refreshFilesCommand
|
||||
};
|
52
src/extension.js
Normal file
@ -0,0 +1,52 @@
|
||||
const vscode = require('vscode');
|
||||
const { addFilesCommand, openGPTContextPanelCommand, refreshSelectedFilesCommand, clearSelectedFilesCommand, refreshFilesCommand } = require('./commands');
|
||||
const { getWebviewContent } = require('./webviewPanel');
|
||||
const { selectedFiles, fileDataProvider, handleQuestionSubmission } = require('./gptContext');
|
||||
|
||||
function activate(context) {
|
||||
context.subscriptions.push(addFilesCommand);
|
||||
context.subscriptions.push(openGPTContextPanelCommand);
|
||||
context.subscriptions.push(refreshSelectedFilesCommand);
|
||||
context.subscriptions.push(clearSelectedFilesCommand);
|
||||
context.subscriptions.push(refreshFilesCommand);
|
||||
vscode.window.registerTreeDataProvider('selectedFiles', fileDataProvider);
|
||||
|
||||
const provider = {
|
||||
resolveWebviewView(webviewView) {
|
||||
webviewView.webview.options = {
|
||||
enableScripts: true
|
||||
};
|
||||
webviewView.webview.html = getWebviewContent();
|
||||
webviewView.webview.onDidReceiveMessage(async message => {
|
||||
if (message.command === 'toggleFileSelection') {
|
||||
const uri = message.uri;
|
||||
const file = selectedFiles.find(file => file.uri.fsPath === uri);
|
||||
if (file) {
|
||||
file.toggleSelected();
|
||||
fileDataProvider.refresh();
|
||||
}
|
||||
} else if (message.command === 'clearSelectedFiles') {
|
||||
const clearedFiles = selectedFiles.filter(file => file.selected === false);
|
||||
selectedFiles.length = 0; // Clear the array
|
||||
clearedFiles.forEach(file => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
webviewView.webview.html = getWebviewContent();
|
||||
} else if (message.command === 'refreshFiles') {
|
||||
fileDataProvider.refresh();
|
||||
webviewView.webview.html = getWebviewContent();
|
||||
} else if (message.command === 'submitQuestion') {
|
||||
await handleQuestionSubmission(webviewView, message.text, message.selectedUris);
|
||||
} else if (message.command === 'codeCopied') {
|
||||
vscode.window.showInformationMessage('Code copied to clipboard');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
context.subscriptions.push(vscode.window.registerWebviewViewProvider('gpt-context-sidebar', provider));
|
||||
|
||||
}
|
||||
|
||||
|
||||
exports.activate = activate;
|
38
src/fileDataProvider.js
Normal file
@ -0,0 +1,38 @@
|
||||
const vscode = require('vscode');
|
||||
const FileItem = require('./fileItem.js');
|
||||
|
||||
// Represents the selected files in the file explorer
|
||||
const selectedFiles = [];
|
||||
|
||||
// Tree data provider for the selected files
|
||||
class FileDataProvider {
|
||||
constructor() {
|
||||
this._onDidChangeTreeData = new vscode.EventEmitter();
|
||||
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
getTreeItem(element) {
|
||||
return {
|
||||
label: element.uri.fsPath,
|
||||
collapsibleState: vscode.TreeItemCollapsibleState.None
|
||||
};
|
||||
}
|
||||
|
||||
getChildren(element) {
|
||||
if (element) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Return only the selected files
|
||||
return selectedFiles.filter(file => file.selected);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
FileDataProvider,
|
||||
selectedFiles
|
||||
};
|
12
src/fileItem.js
Normal file
@ -0,0 +1,12 @@
|
||||
class FileItem {
|
||||
constructor(uri, selected = false) {
|
||||
this.uri = uri;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
toggleSelected() {
|
||||
this.selected = !this.selected;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FileItem;
|
63
src/gptContext.js
Normal file
@ -0,0 +1,63 @@
|
||||
const vscode = require('vscode');
|
||||
const { Configuration, OpenAIApi } = require("openai");
|
||||
const FileDataProvider = require('./fileDataProvider');
|
||||
const { getWebviewContent } = require('./webviewPanel');
|
||||
|
||||
|
||||
const configuration = new Configuration({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
const openai = new OpenAIApi(configuration);
|
||||
|
||||
const selectedFiles = FileDataProvider.selectedFiles;
|
||||
const fileDataProvider = new FileDataProvider.FileDataProvider();
|
||||
|
||||
async function handleQuestionSubmission(panel, question, selectedUris) {
|
||||
// Update the selectedFiles array based on the selectedUris
|
||||
selectedFiles.forEach(file => {
|
||||
file.selected = selectedUris.includes(file.uri.fsPath);
|
||||
});
|
||||
|
||||
fileDataProvider.refresh();
|
||||
|
||||
const fileContents = selectedFiles
|
||||
.filter(file => file.selected)
|
||||
.map(file => {
|
||||
const document = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === file.uri.fsPath);
|
||||
if (document) {
|
||||
const lines = document.getText().split('\n');
|
||||
const formattedLines = lines.map(line => `\t${line}`).join('\n');
|
||||
return `${file.uri.fsPath}:\n\`\`\`\n${formattedLines}\n\`\`\``;
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.join('\n\n');
|
||||
|
||||
// Call OpenAI API with the question and file contents
|
||||
try {
|
||||
const chatCompletion = await openai.createChatCompletion({
|
||||
model: "gpt-3.5-turbo-16k",
|
||||
messages: [
|
||||
{ role: "system", content: "Answer the coding questions, only provide the code and documentation, explaining the solution after providing the code. Put codeblocks inside ``` code ``` with file names above each snippet." },
|
||||
{ role: "user", content: question + "\n" + fileContents},
|
||||
],
|
||||
});
|
||||
|
||||
// Extract the answer from the OpenAI response
|
||||
const answer = chatCompletion.data.choices[0].message.content;
|
||||
|
||||
// Update the webview content to display only the OpenAI response
|
||||
panel.webview.html = getWebviewContent(answer, question);
|
||||
} catch (error) {
|
||||
// Handle any errors from the OpenAI API
|
||||
console.error("Failed to get OpenAI response:", error);
|
||||
panel.webview.html = getWebviewContent(`Failed to get response from OpenAI API. Error: ${error.message}`, question);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleQuestionSubmission,
|
||||
fileDataProvider,
|
||||
selectedFiles
|
||||
};
|
@ -1,179 +1,6 @@
|
||||
const vscode = require('vscode');
|
||||
const { Configuration, OpenAIApi } = require("openai");
|
||||
const { selectedFiles } = require('./fileDataProvider');
|
||||
|
||||
// move these into the script so that instead of echoing the question and the contents,
|
||||
// it will echo the question, followed by the answer from the response when the submit button is pressed.
|
||||
const configuration = new Configuration({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
const openai = new OpenAIApi(configuration);
|
||||
|
||||
// Represents a file item in the file explorer
|
||||
class FileItem {
|
||||
constructor(uri, selected = false) {
|
||||
this.uri = uri;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
toggleSelected() {
|
||||
this.selected = !this.selected;
|
||||
}
|
||||
}
|
||||
|
||||
// Represents the selected files in the file explorer
|
||||
const selectedFiles = [];
|
||||
|
||||
// Tree data provider for the selected files
|
||||
class FileDataProvider {
|
||||
constructor() {
|
||||
this._onDidChangeTreeData = new vscode.EventEmitter();
|
||||
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
getTreeItem(element) {
|
||||
return {
|
||||
label: element.uri.fsPath,
|
||||
collapsibleState: vscode.TreeItemCollapsibleState.None
|
||||
};
|
||||
}
|
||||
|
||||
getChildren(element) {
|
||||
if (element) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Return only the selected files
|
||||
return selectedFiles.filter(file => file.selected);
|
||||
}
|
||||
}
|
||||
|
||||
// Command for adding files to gpt-contextfiles
|
||||
const addFilesCommand = vscode.commands.registerCommand('extension.addFilesToGPTContext', () => {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
const uri = editor.document.uri;
|
||||
const existingFileIndex = selectedFiles.findIndex(file => file.uri.fsPath === uri.fsPath);
|
||||
|
||||
if (existingFileIndex !== -1) {
|
||||
// File already exists, remove it from the list
|
||||
selectedFiles.splice(existingFileIndex, 1);
|
||||
} else {
|
||||
// Add the file to the list with selected state
|
||||
selectedFiles.push(new FileItem(uri, true));
|
||||
}
|
||||
|
||||
fileDataProvider.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
const fileDataProvider = new FileDataProvider();
|
||||
|
||||
// Function to handle question submission
|
||||
async function handleQuestionSubmission(panel, question, selectedUris) {
|
||||
// Update the selectedFiles array based on the selectedUris
|
||||
selectedFiles.forEach(file => {
|
||||
file.selected = selectedUris.includes(file.uri.fsPath);
|
||||
});
|
||||
|
||||
fileDataProvider.refresh();
|
||||
|
||||
const fileContents = selectedFiles
|
||||
.filter(file => file.selected)
|
||||
.map(file => {
|
||||
const document = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === file.uri.fsPath);
|
||||
if (document) {
|
||||
const lines = document.getText().split('\n');
|
||||
const formattedLines = lines.map(line => `\t${line}`).join('\n');
|
||||
return `${file.uri.fsPath}:\n\`\`\`\n${formattedLines}\n\`\`\``;
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.join('\n\n');
|
||||
|
||||
// Call OpenAI API with the question and file contents
|
||||
try {
|
||||
const chatCompletion = await openai.createChatCompletion({
|
||||
model: "gpt-3.5-turbo-16k",
|
||||
messages: [
|
||||
{ role: "system", content: "Answer the coding questions, only provide the code and documentation, explaining the solution after providing the code." },
|
||||
{ role: "user", content: question + "\n" + fileContents},
|
||||
],
|
||||
});
|
||||
|
||||
// Extract the answer from the OpenAI response
|
||||
const answer = chatCompletion.data.choices[0].message.content;
|
||||
|
||||
// Update the webview content to display only the OpenAI response
|
||||
panel.webview.html = getWebviewContent(answer, question);
|
||||
} catch (error) {
|
||||
// Handle any errors from the OpenAI API
|
||||
console.error("Failed to get OpenAI response:", error);
|
||||
panel.webview.html = getWebviewContent(`Failed to get response from OpenAI API. Error: ${error.message}`, question);
|
||||
}
|
||||
}
|
||||
|
||||
// Command for displaying the webview panel
|
||||
const openGPTContextPanelCommand = vscode.commands.registerCommand('extension.openGPTContextPanel', () => {
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'gptContextPanel',
|
||||
'GPT Context',
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true
|
||||
}
|
||||
);
|
||||
|
||||
panel.webview.html = getWebviewContent();
|
||||
|
||||
panel.webview.onDidReceiveMessage(async message => {
|
||||
if (message.command === 'submitQuestion') {
|
||||
await handleQuestionSubmission(panel, message.text, message.selectedUris);
|
||||
} else if (message.command === 'toggleFileSelection') {
|
||||
const uri = message.uri;
|
||||
const file = selectedFiles.find(file => file.uri.fsPath === uri);
|
||||
if (file) {
|
||||
file.toggleSelected();
|
||||
fileDataProvider.refresh();
|
||||
}
|
||||
} else if (message.command === 'clearSelectedFiles') {
|
||||
const clearedFiles = selectedFiles.filter(file => file.selected === false);
|
||||
selectedFiles.length = 0; // Clear the array
|
||||
clearedFiles.forEach(file => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
panel.webview.html = getWebviewContent();
|
||||
} else if (message.command === 'refreshFiles') {
|
||||
fileDataProvider.refresh();
|
||||
panel.webview.html = getWebviewContent();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Command for refreshing the selected files
|
||||
const refreshSelectedFilesCommand = vscode.commands.registerCommand('extension.refreshSelectedFiles', () => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
|
||||
// Command for clearing the selected files
|
||||
const clearSelectedFilesCommand = vscode.commands.registerCommand('extension.clearSelectedFiles', () => {
|
||||
selectedFiles.forEach(file => {
|
||||
file.selected = false;
|
||||
});
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
|
||||
// Command for refreshing all files
|
||||
const refreshFilesCommand = vscode.commands.registerCommand('extension.refreshFiles', () => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
|
||||
// Helper function to generate the HTML content for the webview panel
|
||||
function getWebviewContent(apiResponse = '', question = '') {
|
||||
const fileList = selectedFiles
|
||||
.map(
|
||||
@ -193,27 +20,27 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
width: 90%;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
.textbox {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
resize: both;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
.button {
|
||||
flex-grow: 1;
|
||||
background-color: #007acc;
|
||||
@ -225,28 +52,30 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
#response {
|
||||
white-space: pre-wrap;
|
||||
background: #343434;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
#file-list {
|
||||
margin-top: 20px;
|
||||
border
|
||||
}
|
||||
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.form-group label {
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.form-group input[type="text"] {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
@ -255,17 +84,17 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
background-color: #2d2d2d;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
|
||||
.form-group input[type="text"]::placeholder {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
|
||||
.form-group .button-options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
.form-group .button-options button {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
@ -275,107 +104,107 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
background-color: #007acc;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.form-group .button-options button:hover {
|
||||
background-color: #005f8c;
|
||||
}
|
||||
|
||||
|
||||
.form-group .button-options button:active {
|
||||
background-color: #004d73;
|
||||
}
|
||||
|
||||
|
||||
.form-group .button-options button:focus {
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.5);
|
||||
}
|
||||
|
||||
|
||||
.file-list {
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.file-list h2 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item input[type="checkbox"] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item .file-path {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item .file-path:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item .file-path:active {
|
||||
color: #007acc;
|
||||
}
|
||||
|
||||
|
||||
.file-list .file-item .file-path:focus {
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.5);
|
||||
}
|
||||
|
||||
|
||||
.collapsible {
|
||||
background-color: #2d2d2d;
|
||||
color: #d4d4d4;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
width: 98%;
|
||||
border: none;
|
||||
outline: none;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.collapsible:hover {
|
||||
background-color: #3c3c3c;
|
||||
}
|
||||
|
||||
|
||||
.collapsible:active {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
|
||||
.collapsible:focus {
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.5);
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
padding: 0 10px;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
background-color: #f1f1f1;
|
||||
width: 100%;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
|
||||
.content p {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.active,
|
||||
.collapsible:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
|
||||
.active:after {
|
||||
content: "\\2212";
|
||||
}
|
||||
|
||||
|
||||
.collapsible:after {
|
||||
content: "\\002B";
|
||||
color: #d4d4d4;
|
||||
@ -383,11 +212,11 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.active:after {
|
||||
content: "\\2212";
|
||||
}
|
||||
|
||||
|
||||
.collapsible:after {
|
||||
content: "\\002B";
|
||||
color: #d4d4d4;
|
||||
@ -395,7 +224,7 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
#rendered {
|
||||
background-color: #2d2d2d;
|
||||
word-wrap: wrap;
|
||||
@ -403,32 +232,58 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
border: 1px solid white;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
#question-rep {
|
||||
font-weight: bold;
|
||||
background-color: #2d2d2d;
|
||||
word-wrap: wrap;
|
||||
border: 1px solid white;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
div#api-response.content.active {
|
||||
background-color: #313131;
|
||||
}
|
||||
|
||||
|
||||
#code-block {
|
||||
padding: 0;
|
||||
background: none;
|
||||
padding: 10px 0 10px 10px;
|
||||
border-radius: 5px;
|
||||
background-color: black;
|
||||
border: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
width: 98%;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#copy-button {
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: #007acc;
|
||||
border: none;
|
||||
font: inherit;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
margin: 5px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#copy-button:hover {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@ -449,13 +304,19 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
</div>
|
||||
<div class="content" id="api-response">
|
||||
<div id="question-rep">
|
||||
<p>${question ? '> ' + question : null}</p>
|
||||
<p>${
|
||||
question = question.replace(/</g, '<').replace(/>/g, '>'),
|
||||
question ? '> ' + question : null
|
||||
}</p>
|
||||
</div>
|
||||
${
|
||||
apiResponse ? `
|
||||
<div id="rendered">
|
||||
<p id="responses">
|
||||
<pre id="response">${apiResponse.replace(/```([^```]+)```/g, '<button onclick="copyCode()" id="code-block"><code>$1</code></button>')}</pre>
|
||||
<pre id="response">${
|
||||
apiResponse = apiResponse.replace(/</g, '<').replace(/>/g, '>'),
|
||||
apiResponse.replace(/```([^```]+)```/g, '<div id="code-block"><code>$1</code><button onclick="copyCode(event)" id="copy-button">copy</button></div>')
|
||||
}</pre>
|
||||
</p>
|
||||
</div>
|
||||
` : null
|
||||
@ -476,20 +337,19 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
toggleApiResponse();
|
||||
}
|
||||
|
||||
function copyCode() {
|
||||
function copyCode(event) {
|
||||
event.preventDefault();
|
||||
const codeBlocks = document.getElementsByTagName('code');
|
||||
const selectedCodeBlock = event.target.closest('code');
|
||||
|
||||
if (selectedCodeBlock) {
|
||||
const codeText = selectedCodeBlock.innerText;
|
||||
const codeBlock = event.target.parentNode.querySelector('code');
|
||||
|
||||
if (codeBlock) {
|
||||
const codeText = codeBlock.innerText;
|
||||
const dummyTextArea = document.createElement('textarea');
|
||||
dummyTextArea.value = codeText;
|
||||
document.body.appendChild(dummyTextArea);
|
||||
dummyTextArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(dummyTextArea);
|
||||
|
||||
|
||||
vscode.postMessage({
|
||||
command: 'codeCopied'
|
||||
});
|
||||
@ -561,53 +421,6 @@ function getWebviewContent(apiResponse = '', question = '') {
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Activates the extension
|
||||
function activate(context) {
|
||||
context.subscriptions.push(addFilesCommand);
|
||||
context.subscriptions.push(openGPTContextPanelCommand);
|
||||
context.subscriptions.push(refreshSelectedFilesCommand);
|
||||
context.subscriptions.push(clearSelectedFilesCommand);
|
||||
context.subscriptions.push(refreshFilesCommand);
|
||||
vscode.window.registerTreeDataProvider('selectedFiles', fileDataProvider);
|
||||
|
||||
const provider = {
|
||||
resolveWebviewView(webviewView) {
|
||||
webviewView.webview.options = {
|
||||
enableScripts: true
|
||||
};
|
||||
webviewView.webview.html = getWebviewContent();
|
||||
webviewView.webview.onDidReceiveMessage(async message => {
|
||||
if (message.command === 'toggleFileSelection') {
|
||||
const uri = message.uri;
|
||||
const file = selectedFiles.find(file => file.uri.fsPath === uri);
|
||||
if (file) {
|
||||
file.toggleSelected();
|
||||
fileDataProvider.refresh();
|
||||
}
|
||||
} else if (message.command === 'clearSelectedFiles') {
|
||||
const clearedFiles = selectedFiles.filter(file => file.selected === false);
|
||||
selectedFiles.length = 0; // Clear the array
|
||||
clearedFiles.forEach(file => {
|
||||
fileDataProvider.refresh();
|
||||
});
|
||||
webviewView.webview.html = getWebviewContent();
|
||||
} else if (message.command === 'refreshFiles') {
|
||||
fileDataProvider.refresh();
|
||||
webviewView.webview.html = getWebviewContent();
|
||||
} else if (message.command === 'submitQuestion') {
|
||||
await handleQuestionSubmission(webviewView, message.text, message.selectedUris);
|
||||
} else if (message.command === 'codeCopied') {
|
||||
vscode.window.showInformationMessage('Code copied to clipboard');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
context.subscriptions.push(vscode.window.registerWebviewViewProvider('gpt-context-sidebar', provider));
|
||||
|
||||
}
|
||||
|
||||
|
||||
exports.activate = activate;
|
||||
module.exports = {
|
||||
getWebviewContent
|
||||
};
|
@ -1,41 +0,0 @@
|
||||
# Welcome to your VS Code Extension
|
||||
|
||||
## What's in the folder
|
||||
|
||||
* This folder contains all of the files necessary for your extension.
|
||||
* `package.json` - this is the manifest file in which you declare your extension and command.
|
||||
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
|
||||
* `extension.js` - this is the main file where you will provide the implementation of your command.
|
||||
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
|
||||
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
|
||||
|
||||
## Get up and running straight away
|
||||
|
||||
* Press `F5` to open a new window with your extension loaded.
|
||||
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
|
||||
* Set breakpoints in your code inside `extension.js` to debug your extension.
|
||||
* Find output from your extension in the debug console.
|
||||
|
||||
## Make changes
|
||||
|
||||
* You can relaunch the extension from the debug toolbar after changing code in `extension.js`.
|
||||
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||
|
||||
## Explore the API
|
||||
|
||||
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
|
||||
|
||||
## Run tests
|
||||
|
||||
* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`.
|
||||
* Press `F5` to run the tests in a new window with your extension loaded.
|
||||
* See the output of the test result in the debug console.
|
||||
* Make changes to `src/test/suite/extension.test.js` or create new test files inside the `test/suite` folder.
|
||||
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
|
||||
* You can create folders inside the `test` folder to structure your tests any way you want.
|
||||
|
||||
## Go further
|
||||
|
||||
* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
|
||||
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
|
||||
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
|