From 09ee2eaa2f14a2e018815039c1f782e339a658f7 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 04:30:56 +0000 Subject: [PATCH 01/12] Ignore ide directory --- .gitignore | 1 + index.html | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..79a46d15 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +ide/ diff --git a/index.html b/index.html index faf35a96..3cfe2534 100644 --- a/index.html +++ b/index.html @@ -229,4 +229,9 @@ } - +
+
+ +
+ + \ No newline at end of file From 924df3235ee6e8977c87a0c40f6096feab5788bd Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 04:38:20 +0000 Subject: [PATCH 02/12] Added chat interface --- css/site.css | 24 ++++++++++++++++++++++++ index.html | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/css/site.css b/css/site.css index 8e94d43f..eaf4c4d0 100644 --- a/css/site.css +++ b/css/site.css @@ -5,3 +5,27 @@ html, body { padding: 0; overflow: hidden; } + + +#chat-window { + position: fixed; + bottom: 20px; + right: 20px; + width: 300px; + background: #f1f1f1; + border: 1px solid #ccc; + padding: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +#chat-messages { + height: 200px; + overflow-y: auto; + margin-bottom: 10px; +} + +#chat-input { + width: 100%; + padding: 5px; + box-sizing: border-box; +} diff --git a/index.html b/index.html index 3cfe2534..07910a3e 100644 --- a/index.html +++ b/index.html @@ -228,7 +228,9 @@ navigator.serviceWorker.register("sw.js").then(() => console.log("Service Worker Registered")); } - + + +// Here I added the chat interface
From 222bb20f64f52a1b349cf0fff47ddde36aedb1d9 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 04:44:04 +0000 Subject: [PATCH 03/12] Modified ai.js to implement the requirements --- index.html | 6 ++++- js/ai.js | 73 +++++++++++++++++++++++------------------------------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/index.html b/index.html index 07910a3e..ee45dc08 100644 --- a/index.html +++ b/index.html @@ -236,4 +236,8 @@
- \ No newline at end of file + + \ No newline at end of file diff --git a/js/ai.js b/js/ai.js index 7e9fe58f..023810f3 100644 --- a/js/ai.js +++ b/js/ai.js @@ -48,18 +48,18 @@ document.addEventListener("DOMContentLoaded", function () { } const sendButton = document.getElementById("judge0-chat-send-button"); - sendButton.classList.add("loading"); userInput.disabled = true; + const messages = document.getElementById("judge0-chat-messages"); + + // Display user message const userMessage = document.createElement("div"); userMessage.innerText = userInputValue; userMessage.classList.add("ui", "message", "judge0-message", "judge0-user-message"); if (!theme.isLight()) { userMessage.classList.add("inverted"); } - - const messages = document.getElementById("judge0-chat-messages"); messages.appendChild(userMessage); userInput.value = ""; @@ -67,16 +67,10 @@ document.addEventListener("DOMContentLoaded", function () { THREAD.push({ role: "user", - content: ` -User's code: -${sourceEditor.getValue()} - -User's message: -${userInputValue} -`.trim() + content: `User's code:\n${sourceEditor.getValue()}\n\nUser's message:\n${userInputValue}`.trim() }); - + // Display AI loading message const aiMessage = document.createElement("div"); aiMessage.classList.add("ui", "basic", "segment", "judge0-message", "loading"); if (!theme.isLight()) { @@ -85,28 +79,28 @@ ${userInputValue} messages.appendChild(aiMessage); messages.scrollTop = messages.scrollHeight; - const aiResponse = await puter.ai.chat(THREAD, { - model: document.getElementById("judge0-chat-model-select").value, - }); - let aiResponseValue = aiResponse.toString(); - if (typeof aiResponseValue !== "string") { - aiResponseValue = aiResponseValue.map(v => v.text).join("\n"); + try { + const aiResponse = await puter.ai.chat(THREAD, { + model: document.getElementById("judge0-chat-model-select").value, + }); + let aiResponseValue = typeof aiResponse === "string" ? aiResponse : aiResponse.map(v => v.text).join("\n"); + + THREAD.push({ + role: "assistant", + content: aiResponseValue + }); + + aiMessage.innerHTML = DOMPurify.sanitize(marked.parse(aiResponseValue)); + renderMathInElement(aiMessage, { + delimiters: [ + { left: "\\(", right: "\\)", display: false }, + { left: "\\[", right: "\\]", display: true } + ] + }); + } catch (error) { + aiMessage.innerText = "Error: Unable to fetch response. Please try again."; } - - THREAD.push({ - role: "assistant", - content: aiResponseValue - }); - - aiMessage.innerHTML = DOMPurify.sanitize(aiResponseValue); - renderMathInElement(aiMessage, { - delimiters: [ - { left: "\\(", right: "\\)", display: false }, - { left: "\\[", right: "\\]", display: true } - ] - }); - aiMessage.innerHTML = marked.parse(aiMessage.innerHTML); - + aiMessage.classList.remove("loading"); messages.scrollTop = messages.scrollHeight; @@ -116,21 +110,16 @@ ${userInputValue} }); document.getElementById("judge0-chat-model-select").addEventListener("change", function () { - const userInput = document.getElementById("judge0-chat-user-input"); - userInput.placeholder = `Message ${this.value}`; + document.getElementById("judge0-chat-user-input").placeholder = `Message ${this.value}`; }); }); +// Keyboard shortcut for quick AI input focus document.addEventListener("keydown", function (e) { if (e.metaKey || e.ctrlKey) { - switch (e.key) { - case "p": - if (!configuration.get("appOptions.showAIAssistant")) { - break; - } - e.preventDefault(); - document.getElementById("judge0-chat-user-input").focus(); - break; + if (e.key === "p" && configuration.get("appOptions.showAIAssistant")) { + e.preventDefault(); + document.getElementById("judge0-chat-user-input").focus(); } } }); From f512a647df01c9e609472b4ebb2b305a3900a4ea Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:01:31 +0000 Subject: [PATCH 04/12] Modified ai.js pt 2 --- index.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index ee45dc08..5fe62f91 100644 --- a/index.html +++ b/index.html @@ -22,6 +22,8 @@ }; + + @@ -240,4 +242,5 @@ \ No newline at end of file + + From 0336eeafd3c24ae22224fb05d1b5648cd0d9f86a Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:27:54 +0000 Subject: [PATCH 05/12] Added more ft --- js/ide.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/js/ide.js b/js/ide.js index 448f9def..6cd62555 100755 --- a/js/ide.js +++ b/js/ide.js @@ -181,6 +181,14 @@ async function getSelectedLanguage() { return getLanguage(getSelectedLanguageFlavor(), getSelectedLanguageId()) } +// addded error handling here: + +async function handleCompilationError(errorMessage) { + const prompt = `The following code has an error: ${errorMessage}. Suggest a fix.`; + const fix = await sendChatMessage(prompt); + document.getElementById('chat-messages').innerHTML += `
AI: ${fix}
`; +} + function getSelectedLanguageId() { return parseInt($selectLanguage.val()); } @@ -941,3 +949,33 @@ const EXTENSIONS_TABLE = { function getLanguageForExtension(extension) { return EXTENSIONS_TABLE[extension] || { "flavor": CE, "language_id": 43 }; // Plain Text (https://ce.judge0.com/languages/43) } + + +require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' }}); +require(['vs/editor/editor.main'], function() { + const editor = monaco.editor.create(document.getElementById('editor'), { + value: '// Start coding here...', + language: 'javascript', + theme: 'vs-dark', + automaticLayout: true, + suggest: { + showWords: true, + showSnippets: true, + showClasses: true, + showFunctions: true + } + }); + + // Example: Add AI-driven autocomplete + editor.addAction({ + id: 'ai-autocomplete', + label: 'AI Autocomplete', + keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Space], + run: async function(editor) { + const code = editor.getValue(); + const prompt = `Complete the following code:\n${code}`; + const completion = await sendChatMessage(prompt); + editor.setValue(code + '\n' + completion); + } + }); +}); \ No newline at end of file From 1eb1b872231aa265b92cdc4304d76e5299a143e0 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:27:58 +0000 Subject: [PATCH 06/12] Added more ft --- css/site.css | 28 ++++++++++++++++++++++++++++ js/ai.js | 19 +++++++++++++++++++ js/ide.js | 4 ++++ 3 files changed, 51 insertions(+) diff --git a/css/site.css b/css/site.css index eaf4c4d0..7477f8fe 100644 --- a/css/site.css +++ b/css/site.css @@ -29,3 +29,31 @@ html, body { padding: 5px; box-sizing: border-box; } + + +/* Light Theme */ +#chat-window { + background: #f1f1f1; + border: 1px solid #ccc; + color: #000; +} + +/* Dark Theme */ +.dark-theme #chat-window { + background: #333; + border: 1px solid #555; + color: #fff; +} + +/* Inline Chat Window */ +#inline-chat-window { + background: #fff; + border: 1px solid #ccc; + color: #000; +} + +.dark-theme #inline-chat-window { + background: #444; + border: 1px solid #666; + color: #fff; +} \ No newline at end of file diff --git a/js/ai.js b/js/ai.js index 023810f3..fcc35e82 100644 --- a/js/ai.js +++ b/js/ai.js @@ -123,3 +123,22 @@ document.addEventListener("keydown", function (e) { } } }); + +// added this to ensure min latency +let timeout; +document.getElementById('chat-input').addEventListener('input', () => { + clearTimeout(timeout); + timeout = setTimeout(() => { + // Send message to AI + }, 500); +}); + +// added a bug finder +async function findBugs(code) { + const prompt = `Analyze the following code for bugs and suggest fixes:\n${code}`; + const bugs = await sendChatMessage(prompt); + document.getElementById('chat-messages').innerHTML += `
AI: ${bugs}
`; +} + +// Example usage (call this when the user clicks a "Find Bugs" button) +// findBugs(editor.getValue()); \ No newline at end of file diff --git a/js/ide.js b/js/ide.js index 6cd62555..8044dc89 100755 --- a/js/ide.js +++ b/js/ide.js @@ -189,6 +189,9 @@ async function handleCompilationError(errorMessage) { document.getElementById('chat-messages').innerHTML += `
AI: ${fix}
`; } + + + function getSelectedLanguageId() { return parseInt($selectLanguage.val()); } @@ -951,6 +954,7 @@ function getLanguageForExtension(extension) { } + require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' }}); require(['vs/editor/editor.main'], function() { const editor = monaco.editor.create(document.getElementById('editor'), { From 65c8e79ffd77daa0b05a74fe29e1540f654e3fe0 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 08:55:43 +0000 Subject: [PATCH 07/12] Improvised ai.js --- index.html | 12 +- js/ai.js | 344 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 232 insertions(+), 124 deletions(-) diff --git a/index.html b/index.html index 5fe62f91..5224dd6e 100644 --- a/index.html +++ b/index.html @@ -21,7 +21,7 @@ } }; - + @@ -244,3 +244,13 @@ Optimize Code +
+
+
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/js/ai.js b/js/ai.js index fcc35e82..69327254 100644 --- a/js/ai.js +++ b/js/ai.js @@ -1,144 +1,242 @@ "use strict"; -import theme from "./theme.js"; -import configuration from "./configuration.js"; -import { sourceEditor } from "./ide.js"; - -const THREAD = [ - { - role: "system", - content: ` -You are an AI assistant integrated into an online code editor. -Your main job is to help users with their code, but you should also be able to engage in casual conversation. - -The following are your guidelines: -1. **If the user asks for coding help**: - - Always consider the user's provided code. - - Analyze the code and provide relevant help (debugging, optimization, explanation, etc.). - - Make sure to be specific and clear when explaining things about their code. - -2. **If the user asks a casual question or makes a casual statement**: - - Engage in friendly, natural conversation. - - Do not reference the user's code unless they bring it up or ask for help. - - Be conversational and polite. - -3. **If the user's message is ambiguous or unclear**: - - Politely ask for clarification or more details to better understand the user's needs. - - If the user seems confused about something, help guide them toward what they need. - -4. **General Behavior**: - - Always respond in a helpful, friendly, and professional tone. - - Never assume the user's intent. If unsure, ask clarifying questions. - - Keep the conversation flowing naturally, even if the user hasn't directly asked about their code. - -You will always have access to the user's latest code. -Use this context only when relevant to the user's message. -If their message is unrelated to the code, focus solely on their conversational intent. - `.trim() - } -]; - -document.addEventListener("DOMContentLoaded", function () { - document.getElementById("judge0-chat-form").addEventListener("submit", async function (event) { - event.preventDefault(); - - const userInput = document.getElementById("judge0-chat-user-input"); - const userInputValue = userInput.value.trim(); - if (userInputValue === "") { - return; + +// Constants for API configuration +const API_CONFIG = { + baseUrl: 'https://api.openai.com/v1/chat/completions', + model: 'gpt-3.5-turbo', + maxTokens: 150, + temperature: 0.7 +}; + +// Chat interface component +class AIChat { + constructor() { + this.chatContainer = document.getElementById('judge0-chat-container'); + this.messagesList = document.getElementById('judge0-chat-messages'); + this.inputField = document.getElementById('judge0-chat-user-input'); + this.setupEventListeners(); + } + + setupEventListeners() { + // Debounced input handler for better performance + let timeout; + this.inputField.addEventListener('input', () => { + clearTimeout(timeout); + timeout = setTimeout(() => this.handleInput(), 300); + }); + + // Form submission + document.getElementById('judge0-chat-form').addEventListener('submit', + async (e) => { + e.preventDefault(); + await this.handleSubmit(); + } + ); + } + + async handleInput() { + // Implement real-time suggestions + const input = this.inputField.value.trim(); + if (input.length > 10) { + try { + const suggestions = await this.getAISuggestions(input); + this.showSuggestions(suggestions); + } catch (err) { + console.error('Error getting suggestions:', err); + } } + } - const sendButton = document.getElementById("judge0-chat-send-button"); - sendButton.classList.add("loading"); - userInput.disabled = true; + async handleSubmit() { + const input = this.inputField.value.trim(); + if (!input) return; - const messages = document.getElementById("judge0-chat-messages"); - - // Display user message - const userMessage = document.createElement("div"); - userMessage.innerText = userInputValue; - userMessage.classList.add("ui", "message", "judge0-message", "judge0-user-message"); - if (!theme.isLight()) { - userMessage.classList.add("inverted"); + this.addMessage('user', input); + this.inputField.value = ''; + + try { + const response = await this.getAIResponse(input); + this.addMessage('assistant', response); + } catch (err) { + this.addMessage('error', 'Sorry, there was an error processing your request.'); + console.error('Chat error:', err); } - messages.appendChild(userMessage); + } - userInput.value = ""; - messages.scrollTop = messages.scrollHeight; + addMessage(type, content) { + const messageDiv = document.createElement('div'); + messageDiv.className = `chat-message ${type}-message`; + messageDiv.innerHTML = marked.parse(content); + this.messagesList.appendChild(messageDiv); + this.messagesList.scrollTop = this.messagesList.scrollHeight; + } - THREAD.push({ - role: "user", - content: `User's code:\n${sourceEditor.getValue()}\n\nUser's message:\n${userInputValue}`.trim() + async getAIResponse(prompt) { + const response = await fetch(API_CONFIG.baseUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${configuration.get('aiApiKey')}` + }, + body: JSON.stringify({ + model: API_CONFIG.model, + messages: [{ role: 'user', content: prompt }], + max_tokens: API_CONFIG.maxTokens, + temperature: API_CONFIG.temperature + }) }); - // Display AI loading message - const aiMessage = document.createElement("div"); - aiMessage.classList.add("ui", "basic", "segment", "judge0-message", "loading"); - if (!theme.isLight()) { - aiMessage.classList.add("inverted"); + if (!response.ok) { + throw new Error('AI response failed'); } - messages.appendChild(aiMessage); - messages.scrollTop = messages.scrollHeight; + const data = await response.json(); + return data.choices[0].message.content; + } +} + +// Code analysis and error handling +class CodeAnalyzer { + constructor(editor) { + this.editor = editor; + this.setupAnalysis(); + } + + setupAnalysis() { + // Real-time code analysis + this.editor.onDidChangeModelContent(() => { + clearTimeout(this.analysisTimeout); + this.analysisTimeout = setTimeout(() => this.analyzeCode(), 1000); + }); + } + + async analyzeCode() { + const code = this.editor.getValue(); try { - const aiResponse = await puter.ai.chat(THREAD, { - model: document.getElementById("judge0-chat-model-select").value, - }); - let aiResponseValue = typeof aiResponse === "string" ? aiResponse : aiResponse.map(v => v.text).join("\n"); - - THREAD.push({ - role: "assistant", - content: aiResponseValue - }); - - aiMessage.innerHTML = DOMPurify.sanitize(marked.parse(aiResponseValue)); - renderMathInElement(aiMessage, { - delimiters: [ - { left: "\\(", right: "\\)", display: false }, - { left: "\\[", right: "\\]", display: true } - ] - }); - } catch (error) { - aiMessage.innerText = "Error: Unable to fetch response. Please try again."; + const analysis = await this.getCodeAnalysis(code); + this.showAnalysisResults(analysis); + } catch (err) { + console.error('Analysis error:', err); } - - aiMessage.classList.remove("loading"); - messages.scrollTop = messages.scrollHeight; - - userInput.disabled = false; - sendButton.classList.remove("loading"); - userInput.focus(); - }); + } - document.getElementById("judge0-chat-model-select").addEventListener("change", function () { - document.getElementById("judge0-chat-user-input").placeholder = `Message ${this.value}`; - }); -}); + async getCodeAnalysis(code) { + const prompt = `Analyze this code for potential issues:\n${code}`; + return await aiChat.getAIResponse(prompt); + } -// Keyboard shortcut for quick AI input focus -document.addEventListener("keydown", function (e) { - if (e.metaKey || e.ctrlKey) { - if (e.key === "p" && configuration.get("appOptions.showAIAssistant")) { - e.preventDefault(); - document.getElementById("judge0-chat-user-input").focus(); + showAnalysisResults(analysis) { + // Update UI with analysis results + const resultsContainer = document.getElementById('analysis-results'); + if (resultsContainer) { + resultsContainer.innerHTML = marked.parse(analysis); } } -}); -// added this to ensure min latency -let timeout; -document.getElementById('chat-input').addEventListener('input', () => { - clearTimeout(timeout); - timeout = setTimeout(() => { - // Send message to AI - }, 500); -}); + async handleCompilationError(error) { + const fix = await this.getSuggestedFix(error); + aiChat.addMessage('assistant', `Compilation Error Fix Suggestion:\n${fix}`); + } + + async getSuggestedFix(error) { + const prompt = `Fix this compilation error:\n${error}`; + return await aiChat.getAIResponse(prompt); + } +} + +// Inline code chat feature +class InlineCodeChat { + constructor(editor) { + this.editor = editor; + this.setupInlineChat(); + } + + setupInlineChat() { + this.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_C, + () => this.handleInlineChat() + ); + } + + async handleInlineChat() { + const selection = this.editor.getSelection(); + const code = this.editor.getModel().getValueInRange(selection); + + if (!code) return; + + const response = await aiChat.getAIResponse( + `Explain this code:\n${code}` + ); + + this.showInlineChatResponse(response, selection); + } -// added a bug finder -async function findBugs(code) { - const prompt = `Analyze the following code for bugs and suggest fixes:\n${code}`; - const bugs = await sendChatMessage(prompt); - document.getElementById('chat-messages').innerHTML += `
AI: ${bugs}
`; + showInlineChatResponse(response, selection) { + // Create inline widget + const widget = document.createElement('div'); + widget.className = 'inline-chat-widget'; + widget.innerHTML = marked.parse(response); + + // Position widget near selection + const position = this.editor.getScrolledVisiblePosition(selection.getEndPosition()); + widget.style.top = `${position.top}px`; + widget.style.left = `${position.left}px`; + + document.getElementById('editor-container').appendChild(widget); + } } -// Example usage (call this when the user clicks a "Find Bugs" button) -// findBugs(editor.getValue()); \ No newline at end of file +// Initialize components +let aiChat, codeAnalyzer, inlineChat; + +document.addEventListener('DOMContentLoaded', () => { + // Initialize Monaco Editor + require(['vs/editor/editor.main'], function() { + const editor = monaco.editor.create(document.getElementById('editor'), { + value: '// Start coding here...', + language: 'javascript', + theme: 'vs-dark', + automaticLayout: true + }); + + // Initialize components + aiChat = new AIChat(); + codeAnalyzer = new CodeAnalyzer(editor); + inlineChat = new InlineCodeChat(editor); + + // Add AI autocomplete provider + monaco.languages.registerCompletionItemProvider('javascript', { + provideCompletionItems: async function(model, position) { + const textUntilPosition = model.getValueInRange({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column + }); + + try { + const suggestions = await aiChat.getAIResponse( + `Suggest completions for:\n${textUntilPosition}` + ); + + return { + suggestions: suggestions.split('\n').map(suggestion => ({ + label: suggestion, + kind: monaco.languages.CompletionItemKind.Text, + insertText: suggestion + })) + }; + } catch (err) { + console.error('Autocomplete error:', err); + return { suggestions: [] }; + } + } + }); + }); +}); + +// Export needed functions and classes +export { + AIChat, + CodeAnalyzer, + InlineCodeChat +}; \ No newline at end of file From 7f572fe9faa8a0e9cd4bada0e65aec32bc060d14 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 08:59:53 +0000 Subject: [PATCH 08/12] Fixed ai.js chatbot bugs --- js/ai.js | 303 +++++++++++++++---------------------------------------- 1 file changed, 83 insertions(+), 220 deletions(-) diff --git a/js/ai.js b/js/ai.js index 69327254..4683bb92 100644 --- a/js/ai.js +++ b/js/ai.js @@ -1,242 +1,105 @@ -"use strict"; - -// Constants for API configuration -const API_CONFIG = { - baseUrl: 'https://api.openai.com/v1/chat/completions', - model: 'gpt-3.5-turbo', - maxTokens: 150, - temperature: 0.7 -}; - // Chat interface component class AIChat { constructor() { - this.chatContainer = document.getElementById('judge0-chat-container'); - this.messagesList = document.getElementById('judge0-chat-messages'); - this.inputField = document.getElementById('judge0-chat-user-input'); this.setupEventListeners(); } setupEventListeners() { - // Debounced input handler for better performance - let timeout; - this.inputField.addEventListener('input', () => { - clearTimeout(timeout); - timeout = setTimeout(() => this.handleInput(), 300); - }); + document.getElementById("judge0-chat-form").addEventListener("submit", async (event) => { + event.preventDefault(); - // Form submission - document.getElementById('judge0-chat-form').addEventListener('submit', - async (e) => { - e.preventDefault(); - await this.handleSubmit(); + const userInput = document.getElementById("judge0-chat-user-input"); + const userInputValue = userInput.value.trim(); + if (userInputValue === "") { + return; } - ); - } - async handleInput() { - // Implement real-time suggestions - const input = this.inputField.value.trim(); - if (input.length > 10) { - try { - const suggestions = await this.getAISuggestions(input); - this.showSuggestions(suggestions); - } catch (err) { - console.error('Error getting suggestions:', err); + const sendButton = document.getElementById("judge0-chat-send-button"); + const messages = document.getElementById("judge0-chat-messages"); + + // Disable input and show loading state + sendButton.classList.add("loading"); + userInput.disabled = true; + + // Display user message + const userMessage = document.createElement("div"); + userMessage.innerText = userInputValue; + userMessage.classList.add("ui", "message", "judge0-message", "judge0-user-message"); + if (!theme.isLight()) { + userMessage.classList.add("inverted"); } - } - } - - async handleSubmit() { - const input = this.inputField.value.trim(); - if (!input) return; - - this.addMessage('user', input); - this.inputField.value = ''; - - try { - const response = await this.getAIResponse(input); - this.addMessage('assistant', response); - } catch (err) { - this.addMessage('error', 'Sorry, there was an error processing your request.'); - console.error('Chat error:', err); - } - } - - addMessage(type, content) { - const messageDiv = document.createElement('div'); - messageDiv.className = `chat-message ${type}-message`; - messageDiv.innerHTML = marked.parse(content); - this.messagesList.appendChild(messageDiv); - this.messagesList.scrollTop = this.messagesList.scrollHeight; - } - - async getAIResponse(prompt) { - const response = await fetch(API_CONFIG.baseUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${configuration.get('aiApiKey')}` - }, - body: JSON.stringify({ - model: API_CONFIG.model, - messages: [{ role: 'user', content: prompt }], - max_tokens: API_CONFIG.maxTokens, - temperature: API_CONFIG.temperature - }) - }); + messages.appendChild(userMessage); - if (!response.ok) { - throw new Error('AI response failed'); - } + // Clear input and scroll + userInput.value = ""; + messages.scrollTop = messages.scrollHeight; - const data = await response.json(); - return data.choices[0].message.content; - } -} + // Create AI message placeholder + const aiMessage = document.createElement("div"); + aiMessage.classList.add("ui", "basic", "segment", "judge0-message", "loading"); + if (!theme.isLight()) { + aiMessage.classList.add("inverted"); + } + messages.appendChild(aiMessage); + messages.scrollTop = messages.scrollHeight; -// Code analysis and error handling -class CodeAnalyzer { - constructor(editor) { - this.editor = editor; - this.setupAnalysis(); - } + try { + // Get current code context + const currentCode = sourceEditor.getValue(); + + // Create context-aware prompt + const prompt = `Current code:\n${currentCode}\n\nUser message: ${userInputValue}`; + + // Use Puter AI API + const aiResponse = await puter.ai.chat([{ + role: "user", + content: prompt + }], { + model: document.getElementById("judge0-chat-model-select").value, + }); - setupAnalysis() { - // Real-time code analysis - this.editor.onDidChangeModelContent(() => { - clearTimeout(this.analysisTimeout); - this.analysisTimeout = setTimeout(() => this.analyzeCode(), 1000); + // Process response + let aiResponseValue = typeof aiResponse === "string" ? aiResponse : aiResponse.map(v => v.text).join("\n"); + + // Display response with markdown formatting + aiMessage.innerHTML = DOMPurify.sanitize(marked.parse(aiResponseValue)); + + // Render any math expressions + renderMathInElement(aiMessage, { + delimiters: [ + { left: "\\(", right: "\\)", display: false }, + { left: "\\[", right: "\\]", display: true } + ] + }); + } catch (error) { + console.error('Chat error:', error); + aiMessage.innerText = "I encountered an error processing your request. Please try again."; + } finally { + // Reset UI state + aiMessage.classList.remove("loading"); + messages.scrollTop = messages.scrollHeight; + userInput.disabled = false; + sendButton.classList.remove("loading"); + userInput.focus(); + } }); } - async analyzeCode() { - const code = this.editor.getValue(); - try { - const analysis = await this.getCodeAnalysis(code); - this.showAnalysisResults(analysis); - } catch (err) { - console.error('Analysis error:', err); - } - } - - async getCodeAnalysis(code) { - const prompt = `Analyze this code for potential issues:\n${code}`; - return await aiChat.getAIResponse(prompt); - } - - showAnalysisResults(analysis) { - // Update UI with analysis results - const resultsContainer = document.getElementById('analysis-results'); - if (resultsContainer) { - resultsContainer.innerHTML = marked.parse(analysis); - } - } - - async handleCompilationError(error) { - const fix = await this.getSuggestedFix(error); - aiChat.addMessage('assistant', `Compilation Error Fix Suggestion:\n${fix}`); - } - - async getSuggestedFix(error) { - const prompt = `Fix this compilation error:\n${error}`; - return await aiChat.getAIResponse(prompt); - } -} - -// Inline code chat feature -class InlineCodeChat { - constructor(editor) { - this.editor = editor; - this.setupInlineChat(); - } - - setupInlineChat() { - this.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_C, - () => this.handleInlineChat() - ); - } - - async handleInlineChat() { - const selection = this.editor.getSelection(); - const code = this.editor.getModel().getValueInRange(selection); - - if (!code) return; - - const response = await aiChat.getAIResponse( - `Explain this code:\n${code}` - ); - - this.showInlineChatResponse(response, selection); - } - - showInlineChatResponse(response, selection) { - // Create inline widget - const widget = document.createElement('div'); - widget.className = 'inline-chat-widget'; - widget.innerHTML = marked.parse(response); - - // Position widget near selection - const position = this.editor.getScrolledVisiblePosition(selection.getEndPosition()); - widget.style.top = `${position.top}px`; - widget.style.left = `${position.left}px`; - - document.getElementById('editor-container').appendChild(widget); - } -} - -// Initialize components -let aiChat, codeAnalyzer, inlineChat; - -document.addEventListener('DOMContentLoaded', () => { - // Initialize Monaco Editor - require(['vs/editor/editor.main'], function() { - const editor = monaco.editor.create(document.getElementById('editor'), { - value: '// Start coding here...', - language: 'javascript', - theme: 'vs-dark', - automaticLayout: true - }); - - // Initialize components - aiChat = new AIChat(); - codeAnalyzer = new CodeAnalyzer(editor); - inlineChat = new InlineCodeChat(editor); - - // Add AI autocomplete provider - monaco.languages.registerCompletionItemProvider('javascript', { - provideCompletionItems: async function(model, position) { - const textUntilPosition = model.getValueInRange({ - startLineNumber: 1, - startColumn: 1, - endLineNumber: position.lineNumber, - endColumn: position.column - }); - - try { - const suggestions = await aiChat.getAIResponse( - `Suggest completions for:\n${textUntilPosition}` - ); - - return { - suggestions: suggestions.split('\n').map(suggestion => ({ - label: suggestion, - kind: monaco.languages.CompletionItemKind.Text, - insertText: suggestion - })) - }; - } catch (err) { - console.error('Autocomplete error:', err); - return { suggestions: [] }; + // Add keyboard shortcut for chat focus + setupKeyboardShortcuts() { + document.addEventListener("keydown", function(e) { + if ((e.metaKey || e.ctrlKey) && e.key === "p") { + if (configuration.get("appOptions.showAIAssistant")) { + e.preventDefault(); + document.getElementById("judge0-chat-user-input").focus(); } } }); - }); -}); + } +} -// Export needed functions and classes -export { - AIChat, - CodeAnalyzer, - InlineCodeChat -}; \ No newline at end of file +// Initialize chat when document is ready +document.addEventListener("DOMContentLoaded", () => { + const chat = new AIChat(); + chat.setupKeyboardShortcuts(); +}); \ No newline at end of file From 26ce861df3a3c329c4d69b9539bfed19a3152947 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Mon, 17 Feb 2025 09:03:47 +0000 Subject: [PATCH 09/12] bugs fixed --- js/ai.js | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/js/ai.js b/js/ai.js index 4683bb92..ae1860f8 100644 --- a/js/ai.js +++ b/js/ai.js @@ -102,4 +102,132 @@ class AIChat { document.addEventListener("DOMContentLoaded", () => { const chat = new AIChat(); chat.setupKeyboardShortcuts(); +}); + + +// Configure Monaco Editor with inline suggestions +require(["vs/editor/editor.main"], function() { + // Register inline suggestions provider for all languages + monaco.languages.registerInlineCompletionsProvider('*', { + provideInlineCompletions: async (model, position, context) => { + if (!puter.auth.isSignedIn() || + !document.getElementById("judge0-inline-suggestions").checked || + !configuration.get("appOptions.showAIAssistant")) { + return; + } + + // Get text before and after cursor for context + const textBeforeCursor = model.getValueInRange({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column + }); + + const textAfterCursor = model.getValueInRange({ + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: model.getLineCount(), + endColumn: model.getLineMaxColumn(model.getLineCount()) + }); + + try { + // Get AI suggestion using Puter API + const aiResponse = await puter.ai.chat([{ + role: "user", + content: `You are a code completion assistant. Given the following context, generate the most likely code completion. + + ### Code Before Cursor: + ${textBeforeCursor} + + ### Code After Cursor: + ${textAfterCursor} + + ### Instructions: + - Predict the next logical code segment + - Ensure the suggestion is syntactically and contextually correct + - Keep the completion concise and relevant + - Do not repeat existing code + - Provide only the missing code + - **Respond with only the code, without markdown formatting** + - **Do not include triple backticks (\`\`\`) or additional explanations** + + ### Completion:`.trim() + }], { + model: document.getElementById("judge0-chat-model-select").value, + }); + + // Process the response + let aiResponseValue = aiResponse?.toString().trim() || ""; + if (Array.isArray(aiResponseValue)) { + aiResponseValue = aiResponseValue.map(v => v.text).join("\n").trim(); + } + + if (!aiResponseValue || aiResponseValue.length === 0) { + return; + } + + // Return the suggestion + return { + items: [{ + insertText: aiResponseValue, + range: { + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: position.lineNumber, + endColumn: position.column + } + }] + }; + } catch (error) { + console.error('Inline suggestion error:', error); + return null; + } + }, + + // Required but can be empty for basic implementation + handleItemDidShow: () => {}, + handleItemDidHide: () => {}, + freeInlineCompletions: () => {} + }); + + // Add UI toggle for inline suggestions + const settingsContainer = document.querySelector('.settings-container'); + if (settingsContainer) { + const toggleDiv = document.createElement('div'); + toggleDiv.className = 'ui toggle checkbox'; + toggleDiv.innerHTML = ` + + + `; + settingsContainer.appendChild(toggleDiv); + } +}); + +// Update the editor configuration with inline suggestions enabled +const editorConfig = { + value: '// Start coding here...', + language: 'javascript', + theme: 'vs-dark', + automaticLayout: true, + inlineSuggest: { + enabled: true, + mode: 'subword' + }, + // Other existing editor options... + minimap: { + enabled: true + } +}; + +// Create editor with updated configuration +sourceEditor = monaco.editor.create(container.getElement()[0], editorConfig); + +// Add keyboard shortcut for accepting suggestions +sourceEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.Tab, () => { + // Accept the current inline suggestion if present + const controller = sourceEditor.getContribution('editor.contrib.inlineSuggestionController'); + if (controller) { + controller.accept(); + } }); \ No newline at end of file From 90fad5890596cddc08dcafa9f1933beab0195b51 Mon Sep 17 00:00:00 2001 From: Nafisa Nawrin Labonno <117149420+labus-weg@users.noreply.github.com> Date: Wed, 19 Feb 2025 04:20:24 +0000 Subject: [PATCH 10/12] Fixing bugs, added new index --- .vscode/settings.json | 5 + index.html | 269 +++--------------------------------------- 2 files changed, 20 insertions(+), 254 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6c2ff60b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "master" + ] +} \ No newline at end of file diff --git a/index.html b/index.html index 5224dd6e..85b41314 100644 --- a/index.html +++ b/index.html @@ -1,256 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Judge0 IDE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -