// ===== AI Overlay & Chat Interface =====
// Wait for editor to be ready
document.addEventListener('DOMContentLoaded', () => {
// Get editor reference from global API
const { editor, getSelectedText, getValue, focus: focusEditor } = window.editorAPI || {};
if (!editor) {
console.warn('Editor not found, AI functionality may be limited');
}
// DOM elements
const aiBtn = document.getElementById("aiBtn");
const aiOverlay = document.getElementById("aiOverlay");
const overlayClose = document.getElementById("overlayClose");
const promptForm = document.getElementById("promptForm");
const prompt = document.getElementById("prompt");
const messages = document.getElementById("messages");
// ===== Overlay Management =====
function openOverlay() {
aiOverlay.classList.add("open");
setTimeout(() => {
prompt.focus();
autoGrow(prompt);
}, 0);
}
function closeOverlay() {
aiOverlay.classList.remove("open");
aiBtn.focus();
}
// Event listeners
aiBtn.addEventListener("click", openOverlay);
overlayClose.addEventListener("click", closeOverlay);
// ESC key to close
window.addEventListener("keydown", (e) => {
if (aiOverlay.classList.contains("open") && e.key === "Escape") {
closeOverlay();
}
});
// ===== Auto-growing Textarea =====
function autoGrow(el) {
el.style.height = "auto";
el.style.height = Math.min(el.scrollHeight, window.innerHeight * 0.4) + "px";
}
prompt.addEventListener("input", () => autoGrow(prompt));
prompt.addEventListener("paste", () => setTimeout(() => autoGrow(prompt), 0));
// ===== Message Management =====
function appendMsg(role, text) {
const div = document.createElement("div");
div.className = `msg ${role === "user" ? "user" : "assistant"}`;
div.textContent = text;
messages.appendChild(div);
// Auto-scroll to bottom
messages.parentElement.scrollTop = messages.parentElement.scrollHeight;
}
function clearMessages() {
messages.innerHTML = "";
}
// ===== AI Chat Handler (Stubbed) =====
promptForm.addEventListener("submit", async (e) => {
e.preventDefault();
const text = prompt.value.trim();
if (!text) return;
// Add user message
appendMsg("user", text);
// Get code context
const selection = getSelectedText ? getSelectedText() : "";
const context = selection || (getValue ? getValue().slice(0, 1500) : "");
// Clear prompt
prompt.value = "";
autoGrow(prompt);
// Simulate AI response (replace with actual API call)
await simulateAIResponse(text, context, selection);
});
async function simulateAIResponse(userQuery, codeContext, isSelection) {
// Add loading indicator
const loadingMsg = document.createElement("div");
loadingMsg.className = "msg assistant";
loadingMsg.textContent = "AI is thinking...";
messages.appendChild(loadingMsg);
// Simulate processing delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Remove loading message
loadingMsg.remove();
// Generate response
const contextType = isSelection ? "the selected code" : "the current file";
const response = `You asked: "${userQuery}"
(This is a stub response. In a real implementation, this would connect to an AI API.)
I would analyze ${contextType} and provide helpful suggestions about:
${codeContext.slice(0, 300)}${codeContext.length > 300 ? "..." : ""}
Some things I could help with:
• Code review and suggestions
• Bug detection and fixes
• Optimization recommendations
• Documentation generation
• Refactoring guidance
• Adding new features
To enable real AI responses, configure your API settings in the Settings panel.`;
appendMsg("assistant", response);
}
// ===== Real AI API Integration (Template) =====
async function callAIAPI(userQuery, codeContext) {
const aiProvider = document.getElementById("ai-provider")?.value;
const aiModel = document.getElementById("ai-model")?.value;
const aiKey = document.getElementById("ai-key")?.value;
const aiTemp = parseFloat(document.getElementById("ai-temp")?.value || "0.4");
if (!aiProvider || aiProvider === "none" || !aiKey) {
throw new Error("AI provider and API key must be configured in Settings");
}
const systemPrompt = `You are a helpful coding assistant. The user is working with code and may ask questions about it, request modifications, or seek explanations. Always provide clear, actionable advice.
Current code context:
${codeContext}`;
const requestBody = {
model: aiModel,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userQuery }
],
temperature: aiTemp,
max_tokens: 1000
};
let apiUrl, headers;
switch (aiProvider) {
case "openai":
apiUrl = "https://api.openai.com/v1/chat/completions";
headers = {
"Content-Type": "application/json",
"Authorization": `Bearer ${aiKey}`
};
break;
case "anthropic":
apiUrl = "https://api.anthropic.com/v1/messages";
headers = {
"Content-Type": "application/json",
"x-api-key": aiKey,
"anthropic-version": "2023-06-01"
};
// Adjust request body for Anthropic's format
requestBody.model = aiModel || "claude-3-sonnet-20240229";
requestBody.max_tokens = 1000;
delete requestBody.messages;
requestBody.messages = [{ role: "user", content: userQuery }];
requestBody.system = systemPrompt;
break;
case "deepseek":
apiUrl = "https://api.deepseek.com/v1/chat/completions";
headers = {
"Content-Type": "application/json",
"Authorization": `Bearer ${aiKey}`
};
break;
default:
throw new Error(`Unsupported AI provider: ${aiProvider}`);
}
const response = await fetch(apiUrl, {
method: "POST",
headers,
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
}
const data = await response.json();
// Extract response based on provider
if (aiProvider === "anthropic") {
return data.content[0].text;
} else {
return data.choices[0].message.content;
}
}
// ===== Keyboard Shortcuts =====
document.addEventListener("keydown", (e) => {
// Ctrl/Cmd + Shift + A to open AI overlay
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "A") {
e.preventDefault();
openOverlay();
}
});
// ===== Export AI API for potential external use =====
window.aiAPI = {
openOverlay,
closeOverlay,
appendMsg,
clearMessages,
callAIAPI
};
console.log("AI module loaded successfully");
});