From 9bfb4e2d2da07aef45a98c424435db46983a9fa9 Mon Sep 17 00:00:00 2001 From: wadhia-yash Date: Mon, 8 Jan 2024 12:45:59 +0530 Subject: [PATCH 1/2] fix: list style enabled --- media/chat/css/chatpage.css | 14 +++++++++++++- media/chat/scripts/main.js | 6 ++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/media/chat/css/chatpage.css b/media/chat/css/chatpage.css index 0648e989..58f18d02 100644 --- a/media/chat/css/chatpage.css +++ b/media/chat/css/chatpage.css @@ -163,7 +163,19 @@ a { color: var(--vscode-textLink-activeForeground); } +ol li::before, ul li::before { - content: "•"; margin-right: 5px; +} + +ol, ul { + padding-left: 11px; +} + +ol { + list-style-type: decimal; +} + +ul { + list-style-type: disc; } \ No newline at end of file diff --git a/media/chat/scripts/main.js b/media/chat/scripts/main.js index e6f9fd9a..1e37a1cd 100644 --- a/media/chat/scripts/main.js +++ b/media/chat/scripts/main.js @@ -393,8 +393,10 @@ class Mentionify { const modelResponse = document.querySelectorAll("div.user-gemini-pro"); modelResponse.forEach((_modelResponse) => { const pBlocks = _modelResponse.querySelectorAll("p"); - pBlocks.forEach((_pBlock) => { - _pBlock.classList.add("my-3"); + pBlocks.forEach((_pBlock, index) => { + if (index !== 0) { + _pBlock.classList.add("my-3"); + } }); }); } From 0ccc7e70e62a96ae920f9a0a725fb12aeba6f66e Mon Sep 17 00:00:00 2001 From: wadhia-yash Date: Tue, 9 Jan 2024 14:17:44 +0530 Subject: [PATCH 2/2] feat(Workspace Loader): Designed UI for loading screen when workspace query is in process --- media/chat/chat.html | 64 ++++++++++++++++++++++++++--- media/chat/css/chatpage.css | 6 +++ media/chat/scripts/main.js | 44 +++++++++++++++++++- src/providers/chat_view_provider.ts | 5 +-- src/repository/gemini-repository.ts | 29 +++++++++++-- 5 files changed, 135 insertions(+), 13 deletions(-) diff --git a/media/chat/chat.html b/media/chat/chat.html index be13dd50..13a317c9 100644 --- a/media/chat/chat.html +++ b/media/chat/chat.html @@ -7,11 +7,8 @@ - - - + @@ -27,16 +24,73 @@
+ +
- + + + \ No newline at end of file diff --git a/media/chat/css/chatpage.css b/media/chat/css/chatpage.css index 02e01d02..7d29e15a 100644 --- a/media/chat/css/chatpage.css +++ b/media/chat/css/chatpage.css @@ -203,6 +203,12 @@ body { word-break: break-word; } +#workspace-loader { + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + fill: var(--vscode-editor-foreground); +} + .user-gemini-pro p { margin-left: 0px; white-space: pre-wrap; diff --git a/media/chat/scripts/main.js b/media/chat/scripts/main.js index 66c5c8dd..dbd678c8 100644 --- a/media/chat/scripts/main.js +++ b/media/chat/scripts/main.js @@ -242,11 +242,17 @@ class Mentionify { // Define an empty array, which will be loaded through the displayMessages function let conversationHistory = []; let loadingIndicator = document.getElementById('loader'); + let workspaceLoader = document.getElementById('workspace-loader'); + let accessingWorkspaceLoader = document.getElementById('accessing-workspace-loader'); + let accessingWorkspaceDone = document.getElementById('accessing-workspace-done'); + let fetchingFileLoader = document.getElementById('fetching-file-loader'); + let fetchingFileDone = document.getElementById('fetching-file-done'); + let creatingResultLoader = document.getElementById('creating-result-loader'); + let creatingResultDone = document.getElementById('creating-result-done'); // Handle mexssages sent from the extension to the webview window.addEventListener("message", (event) => { const message = event.data; - debugger; switch (message.type) { case "addResponse": { response = message.value; @@ -293,6 +299,42 @@ class Mentionify { showSnackbar(response); break; } + case 'workspaceLoader': { + debugger; + workspaceLoader.style.display = message.value ? 'flex' : 'none'; + if (message.value) { + workspaceLoader.classList.remove("animate__slideOutDown"); + workspaceLoader.classList.add("animate__slideInUp"); + } else { + workspaceLoader.classList.remove("animate__slideInUp"); + workspaceLoader.classList.add("animate__slideOutDown"); + } + break; + } + case 'stepLoader': { + if (message.value?.accessingWorkspaceLoader) { + accessingWorkspaceLoader.style.display = 'none'; + accessingWorkspaceDone.style.display = 'block'; + } + if (message.value?.fetchingFileLoader) { + fetchingFileLoader.style.display = 'none'; + fetchingFileDone.style.display = 'block'; + } + if (message.value?.creatingResultLoader) { + creatingResultLoader.style.display = 'none'; + creatingResultDone.style.display = 'block'; + } + break; + } + case 'stepLoaderCompleted': { + accessingWorkspaceLoader.style.display = 'block'; + accessingWorkspaceDone.style.display = 'none'; + fetchingFileLoader.style.display = 'block'; + fetchingFileDone.style.display = 'none'; + creatingResultLoader.style.display = 'block'; + creatingResultDone.style.display = 'none'; + break; + } } }); diff --git a/src/providers/chat_view_provider.ts b/src/providers/chat_view_provider.ts index 6eaaf18a..1496c209 100644 --- a/src/providers/chat_view_provider.ts +++ b/src/providers/chat_view_provider.ts @@ -3,7 +3,6 @@ import * as fs from 'fs'; import path = require('path'); import { GeminiRepository } from "../repository/gemini-repository"; - export class FlutterGPTViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = "fluttergpt.chatView"; private _view?: vscode.WebviewView; @@ -127,7 +126,6 @@ export class FlutterGPTViewProvider implements vscode.WebviewViewProvider { { role: 'model', parts: "I am a flutter/dart development expert who specializes in providing production-ready well-formatted code. How can I help you?\n\n" } ); } - console.debug('conversation history', this._conversationHistory); // Append the current user prompt to the conversation history this._conversationHistory.push({ role: 'user', parts: prompt }); @@ -139,7 +137,7 @@ export class FlutterGPTViewProvider implements vscode.WebviewViewProvider { try { // Use the stored conversation history for the prompt const isWorkspacePresent = prompt.includes('@workspace'); - const response = await this.aiRepo.getCompletion(this._conversationHistory, isWorkspacePresent); + const response = await this.aiRepo.getCompletion(this._conversationHistory, isWorkspacePresent, this._view); this._conversationHistory.push({ role: 'user', parts: prompt }); this._conversationHistory.push({ role: 'model', parts: response }); this._view?.webview.postMessage({ type: 'displayMessages', value: this._conversationHistory }); @@ -150,6 +148,7 @@ export class FlutterGPTViewProvider implements vscode.WebviewViewProvider { this._view?.webview.postMessage({ type: 'displaySnackbar', value: response }); } finally { this._view?.webview.postMessage({ type: 'hideLoadingIndicator' }); + this._view?.webview.postMessage({type: 'workspaceLoader', value: false}); } } diff --git a/src/repository/gemini-repository.ts b/src/repository/gemini-repository.ts index 1557a389..6e754358 100644 --- a/src/repository/gemini-repository.ts +++ b/src/repository/gemini-repository.ts @@ -13,6 +13,7 @@ function handleError(error: Error, userFriendlyMessage: string): never { export class GeminiRepository { private apiKey?: string; private genAI: GoogleGenerativeAI; + private _view?: vscode.Webview; constructor(apiKey: string) { this.apiKey = apiKey; @@ -34,15 +35,16 @@ export class GeminiRepository { return text; } - public async getCompletion(prompt: { role: string, parts: string }[], isReferenceAdded?: boolean): Promise { + public async getCompletion(prompt: { role: string, parts: string }[], isReferenceAdded?: boolean, view?: vscode.WebviewView): Promise { if (!this.apiKey) { throw new Error('API token not set, please go to extension settings to set it (read README.md for more info)'); } let lastMessage = prompt.pop(); if (lastMessage && isReferenceAdded) { - const dartFiles = await this.findClosestDartFiles(lastMessage.parts); - lastMessage.parts = "You've complete access to the codebase. I'll provide you with top 5 closest files code as context and your job is to read following workspace code end-to-end and answer the prompt initialised by `@workspace` symbol. If you're unable to find answer for the requested prompt, suggest an alternative solution as a dart expert. Be crisp & crystal clear in your answer. Make sure to provide your thinking process in steps along with file name, path & code. Here's the code: \n\n" + dartFiles + "\n\n" + lastMessage.parts; + this.displayWebViewMessage(view, 'workspaceLoader', true); + const dartFiles = await this.findClosestDartFiles(lastMessage.parts, view); + lastMessage.parts = "You're a vscode extension copilot, you've complete access to the codebase. I'll provide you with top 5 closest files code as context and your job is to read following workspace code end-to-end and answer the prompt initialised by `@workspace` symbol. If you're unable to find answer for the requested prompt, suggest an alternative solution as a dart expert. Be crisp & crystal clear in your answer. Make sure to provide your thinking process in steps. Here's the code: \n\n" + dartFiles + "\n\n" + lastMessage.parts; } const chat = this.genAI.getGenerativeModel({ model: "gemini-pro", generationConfig: { temperature: 0.0, topP: 0.2 } }).startChat( { @@ -55,12 +57,29 @@ export class GeminiRepository { const text = response.text(); // Creating a result for you + if (isReferenceAdded) { + this.displayWebViewMessage(view, 'stepLoader', { creatingResultLoader: true }); + await this.sleep(2000); + this.displayWebViewMessage(view, 'workspaceLoader', false); + this.displayWebViewMessage(view, 'stepLoaderCompleted', ''); + } return text; } // Cache structure private codehashCache: { [filePath: string]: { codehash: string, embedding: ContentEmbedding } } = {}; + private displayWebViewMessage(view?: vscode.WebviewView, type?: string, value?: any) { + view?.webview.postMessage({ + type, + value + }); + } + + private async sleep(msec: number) { + return new Promise(resolve => setTimeout(resolve, msec)); + } + // Modify the get cacheFilePath getter to point to a more secure location private get cacheFilePath() { @@ -122,7 +141,7 @@ export class GeminiRepository { } // Find 5 closest dart files for query - public async findClosestDartFiles(query: string): Promise { + public async findClosestDartFiles(query: string, view?: vscode.WebviewView): Promise { try { if (!this.apiKey) { throw new Error('API token not set, please go to extension settings to set it (read README.md for more info)'); @@ -193,6 +212,7 @@ export class GeminiRepository { await this.saveCache(); //Accessing work structure(it can take a while in first time) + this.displayWebViewMessage(view, 'stepLoader', { accessingWorkspaceLoader: true }); // Generate embedding for the query const queryEmbedding = await embeddingModel.embedContent({ @@ -217,6 +237,7 @@ export class GeminiRepository { } // Fetching most relevant files + this.displayWebViewMessage(view, 'stepLoader', { fetchingFileLoader: true }); return resultString.trim(); } catch (error) { console.error("Error finding closest Dart files: ", error);