|
| 1 | +import {Application} from "./application"; |
| 2 | +import * as vscode from 'vscode'; |
| 3 | + |
| 4 | +export class ChatWithAi { |
| 5 | + private app: Application |
| 6 | + private askAiPanel: vscode.WebviewPanel | undefined |
| 7 | + private askAiWithContextPanel: vscode.WebviewPanel | undefined |
| 8 | + private lastActiveEditor: vscode.TextEditor | undefined; |
| 9 | + private sentContextChunks: string[] = [] |
| 10 | + |
| 11 | + constructor(application: Application) { |
| 12 | + this.app = application; |
| 13 | + } |
| 14 | + |
| 15 | + showChatWithAi = (withContext: boolean, context: vscode.ExtensionContext) => { |
| 16 | + const editor = vscode.window.activeTextEditor; |
| 17 | + let webviewIdentifier = 'htmlChatWithAiViewer' |
| 18 | + let panelTitle = 'Chat With AI' |
| 19 | + let aiPanel = this.askAiPanel |
| 20 | + let extraCont = ""; |
| 21 | + if (withContext){ |
| 22 | + aiPanel = this.askAiWithContextPanel |
| 23 | + if (!aiPanel) this.sentContextChunks = [] |
| 24 | + webviewIdentifier = 'htmlChatWithAiWithContextViewer' |
| 25 | + let chunksToSend = this.app.extraContext.chunks.filter((_, index) => !this.sentContextChunks.includes(this.app.extraContext.chunksHash[index])); |
| 26 | + let chunksToSendHash = this.app.extraContext.chunksHash.filter((item) => !this.sentContextChunks.includes(item)); |
| 27 | + if (chunksToSend.length > 0) extraCont = "Here are pieces of code from different files of the project: \n" + chunksToSend.reduce((accumulator, currentValue) => accumulator + "\nFile Name: " + currentValue.filename + "\nText:\n" + currentValue.text + "\n\n" , ""); |
| 28 | + this.sentContextChunks.push(...chunksToSendHash) |
| 29 | + panelTitle = "Chat With AI With Project Context" |
| 30 | + } |
| 31 | + let selectedText = "" |
| 32 | + if (editor) { |
| 33 | + selectedText = editor.document.getText(editor.selection); |
| 34 | + if (selectedText.length > 0) selectedText = "Explain the following source code: " + selectedText |
| 35 | + } |
| 36 | + if (!aiPanel) { |
| 37 | + aiPanel = vscode.window.createWebviewPanel( |
| 38 | + webviewIdentifier, |
| 39 | + panelTitle, |
| 40 | + vscode.ViewColumn.Three, // Editor column to show the Webview |
| 41 | + { |
| 42 | + enableScripts: true, // Allow JavaScript execution |
| 43 | + retainContextWhenHidden: true, |
| 44 | + } |
| 45 | + ); |
| 46 | + if (withContext) this.askAiWithContextPanel = aiPanel; |
| 47 | + else this.askAiPanel = aiPanel; |
| 48 | + |
| 49 | + if (aiPanel) context.subscriptions.push(aiPanel); |
| 50 | + const targetUrl = this.app.extConfig.endpoint_chat + "/"; |
| 51 | + aiPanel.webview.html = this.getWebviewContent(targetUrl); |
| 52 | + aiPanel.onDidDispose(() => { |
| 53 | + if (withContext) this.askAiWithContextPanel = undefined |
| 54 | + else this.askAiPanel = undefined |
| 55 | + }); |
| 56 | + aiPanel.webview.onDidReceiveMessage((message) => { |
| 57 | + if (message.command === 'escapePressed') { |
| 58 | + this.focusEditor(); |
| 59 | + } else if (message.command === 'jsAction') { |
| 60 | + // console.log("onDidReceiveMessage: " + message.text); |
| 61 | + } |
| 62 | + }); |
| 63 | + // Wait for the page to load before sending message |
| 64 | + setTimeout(async () => { |
| 65 | + if (aiPanel) aiPanel.webview.postMessage({ command: 'setText', text: selectedText, context: extraCont }); |
| 66 | + }, 1000); |
| 67 | + } else { |
| 68 | + aiPanel.reveal(); |
| 69 | + this.lastActiveEditor = editor; |
| 70 | + // Wait for the page to load before sending message |
| 71 | + setTimeout(async () => { |
| 72 | + if (aiPanel) aiPanel.webview.postMessage({ command: 'setText', text: selectedText, context: extraCont }); |
| 73 | + }, 500); |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + focusEditor = () => { |
| 78 | + if (this.lastActiveEditor) { |
| 79 | + vscode.window.showTextDocument(this.lastActiveEditor.document, this.lastActiveEditor.viewColumn, false); |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + getWebviewContent = (url: string): string => { |
| 84 | + return ` |
| 85 | + <!DOCTYPE html> |
| 86 | + <html lang="en"> |
| 87 | + <head> |
| 88 | + <meta charset="UTF-8"> |
| 89 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 90 | + <title>llama.cpp server UI</title> |
| 91 | + <script> |
| 92 | + // Initialize the VS Code API |
| 93 | + const vscode = acquireVsCodeApi(); |
| 94 | + vscode.postMessage({ command: 'jsAction', text: 'vscode javascript object created' }); |
| 95 | +
|
| 96 | + // Listen for messages from the extension |
| 97 | + window.addEventListener('message', (event) => { |
| 98 | + vscode.postMessage({ command: 'jsAction', text: 'message received' }); |
| 99 | +
|
| 100 | + const { command, text, context } = event.data; // Extract the command and text from the event |
| 101 | + if (command === 'setText') { |
| 102 | + vscode.postMessage({ command: 'jsAction', text: 'command setText received' }); |
| 103 | +
|
| 104 | + const iframe = document.getElementById('askAiIframe'); |
| 105 | + if (iframe) { |
| 106 | + vscode.postMessage({ command: 'jsAction', text: 'askAiIframe obtained' }); |
| 107 | + iframe.contentWindow.postMessage({ command: 'setText', text: text, context: context }, '*'); |
| 108 | + vscode.postMessage({ command: 'jsAction', text: text }); |
| 109 | + } |
| 110 | + } |
| 111 | + if (command === 'escapePressed') { |
| 112 | + vscode.postMessage({ command: 'jsAction', text: 'command escape pressed' }); |
| 113 | + vscode.postMessage({ command: 'escapePressed' }); |
| 114 | + } |
| 115 | + if (command === 'jsAction') { |
| 116 | + vscode.postMessage({ command: 'jsAction', text: text }); |
| 117 | + } |
| 118 | + }); |
| 119 | +
|
| 120 | + // Listen for key events in the iframe |
| 121 | + window.addEventListener('keydown', (event) => { |
| 122 | + vscode.postMessage({ command: 'jsAction', text: 'keydown event received' }); |
| 123 | + if (event.key === 'Escape') { |
| 124 | + // Send a message to the extension when Escape is pressed |
| 125 | + vscode.postMessage({ command: 'escapePressed', text: "" }); |
| 126 | + vscode.postMessage({ command: 'jsAction', text: "Escabe key pressed..." }); |
| 127 | + } |
| 128 | + }); |
| 129 | + </script> |
| 130 | + <style> |
| 131 | + body, html { |
| 132 | + margin: 0; |
| 133 | + padding: 0; |
| 134 | + width: 100%; |
| 135 | + height: 100%; |
| 136 | + overflow: hidden; |
| 137 | + } |
| 138 | + iframe { |
| 139 | + width: 100%; |
| 140 | + height: 100%; |
| 141 | + border: none; |
| 142 | + } |
| 143 | + </style> |
| 144 | + </head> |
| 145 | + <body> |
| 146 | + <iframe src="${url}" id="askAiIframe"></iframe> |
| 147 | + </body> |
| 148 | + </html> |
| 149 | + `; |
| 150 | + } |
| 151 | + |
| 152 | +} |
0 commit comments