document.addEventListener("DOMContentLoaded", () => { // Auto-resize textarea const textarea = document.getElementById("user-input") textarea.addEventListener("input", autoResizeTextarea) // Feature button selection const featureButtons = document.querySelectorAll(".feature-btn") const featureOptions = document.getElementById("feature-options") const fileUploadLabel = document.getElementById("file-upload-label") featureButtons.forEach((button) => { button.addEventListener("click", () => { // Remove active class from all buttons featureButtons.forEach((btn) => btn.classList.remove("active")) // Add active class to clicked button button.classList.add("active") // Show/hide file upload button based on feature const feature = button.dataset.feature if (feature === "document" || feature === "image") { fileUploadLabel.classList.remove("hidden") } else { fileUploadLabel.classList.add("hidden") } // Update feature options updateFeatureOptions(feature) // Save active feature to localStorage localStorage.setItem("activeFeature", feature) }) }) // Restore active feature from localStorage const activeFeature = localStorage.getItem("activeFeature") || "chat" document.querySelector(`.feature-btn[data-feature="${activeFeature}"]`)?.click() // File input handling const fileInput = document.getElementById("file-upload") fileInput.addEventListener("change", handleFileSelection) // Chat form submission const chatForm = document.getElementById("chat-form") chatForm.addEventListener("submit", handleChatSubmit) // History panel toggle const historyButton = document.createElement("button") historyButton.id = "historyButton" historyButton.className = "absolute left-4 top-4 p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors" historyButton.innerHTML = ` ` document.querySelector("header .container").appendChild(historyButton) historyButton.addEventListener("click", toggleHistoryPanel) document.getElementById("closeHistory").addEventListener("click", toggleHistoryPanel) // Theme toggle functionality const themeToggle = document.getElementById("theme-toggle") const sunIcon = document.getElementById("sun-icon") const moonIcon = document.getElementById("moon-icon") // Check for saved theme preference or use system preference const savedTheme = localStorage.getItem("theme") const systemPrefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches if (savedTheme === "dark" || (!savedTheme && systemPrefersDark)) { document.documentElement.classList.add("dark") sunIcon.classList.remove("hidden") moonIcon.classList.add("hidden") } // Toggle theme when button is clicked themeToggle.addEventListener("click", () => { const isDark = document.documentElement.classList.toggle("dark") // Toggle icons if (isDark) { sunIcon.classList.remove("hidden") moonIcon.classList.add("hidden") localStorage.setItem("theme", "dark") } else { sunIcon.classList.add("hidden") moonIcon.classList.remove("hidden") localStorage.setItem("theme", "light") } // Show notification showNotification(`Switched to ${isDark ? "dark" : "light"} mode`, "info") }) // Load history from localStorage loadHistory() }) // Auto-resize textarea function autoResizeTextarea() { this.style.height = "auto" this.style.height = this.scrollHeight + "px" } // Update feature options based on selected feature function updateFeatureOptions(feature) { const optionsContainer = document.getElementById("feature-options") // Clear previous options optionsContainer.innerHTML = "" switch (feature) { case "chat": // No special options for chat optionsContainer.classList.add("hidden") break case "translate": optionsContainer.classList.remove("hidden") optionsContainer.innerHTML = `
` break case "qa": optionsContainer.classList.remove("hidden") optionsContainer.innerHTML = `
` // Auto-resize for the new textarea document.getElementById("contextInput").addEventListener("input", autoResizeTextarea) break default: optionsContainer.classList.add("hidden") break } } // Handle file selection function handleFileSelection() { const fileInput = document.getElementById("file-upload") const userInput = document.getElementById("user-input") if (fileInput.files.length > 0) { const file = fileInput.files[0] // Create file preview element const filePreview = document.createElement("div") filePreview.className = "file-preview" filePreview.innerHTML = ` ${file.name} × ` // Add file preview after textarea const inputContainer = userInput.parentElement // Remove any existing file preview const existingPreview = inputContainer.querySelector(".file-preview") if (existingPreview) { existingPreview.remove() } inputContainer.appendChild(filePreview) // Update placeholder text userInput.placeholder = "Add a message about this file..." } } // Remove selected file const removeFile = () => { const fileInput = document.getElementById("file-upload") const userInput = document.getElementById("user-input") const filePreview = document.querySelector(".file-preview") // Reset file input fileInput.value = "" // Remove file preview if (filePreview) { filePreview.remove() } // Reset placeholder userInput.placeholder = "Type your message..." } window.removeFile = removeFile // Handle chat form submission function handleChatSubmit(e) { e.preventDefault() const userInput = document.getElementById("user-input") const fileInput = document.getElementById("file-upload") const activeFeature = document.querySelector(".feature-btn.active").dataset.feature // Get user message const userMessage = userInput.value.trim() // Check if there's a message or file if (!userMessage && fileInput.files.length === 0) { showNotification("Please enter a message or select a file", "warning") return } // Add user message to chat if (userMessage) { addMessageToChat("user", userMessage) } // Process based on active feature processRequest(activeFeature, userMessage, fileInput.files[0]) // Clear input and file userInput.value = "" userInput.style.height = "auto" if (fileInput.files.length > 0) { removeFile() } // Scroll to bottom scrollToBottom() } // Process request based on feature function processRequest(feature, message, file) { // Show loading indicator showLoadingIndicator() // Determine which API endpoint to use let endpoint const formData = new FormData() switch (feature) { case "summarize": endpoint = "summarize" formData.append("text", message) break case "translate": endpoint = "translate" formData.append("text", message) formData.append("source_lang", document.getElementById("sourceLanguage").value) formData.append("target_lang", document.getElementById("targetLanguage").value) break case "qa": endpoint = "qa/" // Use fetch with JSON instead of FormData for QA const context = document.getElementById("contextInput").value.trim() if (!context) { hideLoadingIndicator() showNotification("Please enter reference text", "warning") return } // We'll handle this differently below break case "code": endpoint = "generate_code/" formData.append("prompt", message) break case "document": if (!file) { hideLoadingIndicator() showNotification("Please select a document file", "warning") return } endpoint = "extract_text" formData.append("file", file) break case "image": if (!file) { hideLoadingIndicator() showNotification("Please select an image file", "warning") return } endpoint = "image_caption" formData.append("file", file) break default: // For regular chat, we'll simulate a response setTimeout(() => { const responses = [ "I'm here to help! What would you like to know?", "That's an interesting question. Let me think about it...", "I can assist with summarizing text, translating languages, answering questions, generating code, and analyzing documents and images. What would you like me to help you with?", "I understand. Is there anything specific you'd like me to explain further?", "I'm your AI assistant. I'm designed to be helpful, harmless, and honest.", ] const randomResponse = responses[Math.floor(Math.random() * responses.length)] addMessageToChat("assistant", randomResponse) hideLoadingIndicator() scrollToBottom() // Add to history addToHistory("Chat", message.substring(0, 30) + (message.length > 30 ? "..." : "")) }, 1000) return } // Special handling for QA if (feature === "qa") { const context = document.getElementById("contextInput").value.trim() const question = message fetch("https://soltane777-textgeneration.hf.space/qa/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ context: context, question: question }), }) .then((response) => { if (!response.ok) { throw new Error(`Server responded with status: ${response.status}`) } return response.json() }) .then((data) => { const answer = data.answer || "Sorry, I couldn't find an answer to that question." addMessageToChat("assistant", answer) addToHistory("Question Answered", question.substring(0, 30) + (question.length > 30 ? "..." : "")) }) .catch((error) => { console.error("Error:", error) addMessageToChat("assistant", `Error: ${error.message || "Failed to get an answer"}`) showNotification("Error getting answer: " + (error.message || "Unknown error"), "error") }) .finally(() => { hideLoadingIndicator() scrollToBottom() }) return } // Process other requests fetch(`https://soltane777-textgeneration.hf.space/${endpoint}`, { method: "POST", body: formData, }) .then((response) => { if (!response.ok) { throw new Error(`Server responded with status: ${response.status}`) } return response.json() }) .then((data) => { let result switch (feature) { case "summarize": result = data.summary || "Sorry, I couldn't summarize that text." addToHistory("Summarization", "Text summarized") break case "translate": const sourceLang = getLanguageName(document.getElementById("sourceLanguage").value) const targetLang = getLanguageName(document.getElementById("targetLanguage").value) result = data.translation || "Sorry, I couldn't translate that text." addToHistory("Translation", `${sourceLang} to ${targetLang}`) break case "code": result = formatCodeResponse(data) addToHistory("Code Generation", message.substring(0, 30) + (message.length > 30 ? "..." : "")) break case "document": result = data.text || "Sorry, I couldn't extract text from that document." addToHistory("Text Extraction", "Extracted from file") break case "image": result = data.caption || "Sorry, I couldn't analyze that image." addToHistory("Image Analysis", "Image analyzed") break default: result = JSON.stringify(data) } addMessageToChat("assistant", result) }) .catch((error) => { console.error("Error:", error) addMessageToChat("assistant", `Error: ${error.message || "Failed to process request"}`) showNotification("Error: " + (error.message || "Unknown error"), "error") }) .finally(() => { hideLoadingIndicator() scrollToBottom() }) } // Format code response function formatCodeResponse(data) { if (!data.code && !data[0]) { return "Sorry, I couldn't generate code for that prompt." } const code = data.code || data[0].generated_text || JSON.stringify(data) // Format as code block return "\`\`\`\n" + code + "\n\`\`\`" } // Add message to chat function addMessageToChat(sender, message) { const chatMessages = document.getElementById("chat-messages") const messageDiv = document.createElement("div") if (sender === "user") { messageDiv.className = "flex justify-end mb-4" messageDiv.innerHTML = `

${formatMessage(message)}

` } else { messageDiv.className = "flex items-start mb-4" messageDiv.innerHTML = `
${formatMessage(message)}
` } chatMessages.appendChild(messageDiv) } // Format message with code blocks and links function formatMessage(message) { // Replace code blocks message = message.replace(/\`\`\`([\s\S]*?)\`\`\`/g, (match, code) => { return `
${escapeHtml(code)}
` }) // Replace inline code message = message.replace(/`([^`]+)`/g, (match, code) => { return `${escapeHtml(code)}` }) // Replace URLs with links message = message.replace(/(https?:\/\/[^\s]+)/g, (url) => { return `${url}` }) return message } // Escape HTML function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'") } // Copy to clipboard window.copyToClipboard = (button) => { const messageDiv = button.closest(".assistant-message") const textContent = messageDiv.querySelector(".whitespace-pre-wrap").innerText navigator.clipboard .writeText(textContent) .then(() => { // Change button text temporarily const originalText = button.innerHTML button.innerHTML = ` Copied! ` setTimeout(() => { button.innerHTML = originalText }, 2000) showNotification("Text copied to clipboard!") }) .catch((err) => { console.error("Failed to copy text: ", err) showNotification("Failed to copy text: " + err.message, "error") }) } // Show loading indicator function showLoadingIndicator() { const chatMessages = document.getElementById("chat-messages") // Create loading message const loadingDiv = document.createElement("div") loadingDiv.id = "loading-indicator" loadingDiv.className = "flex items-start mb-4" loadingDiv.innerHTML = `

Thinking...

` chatMessages.appendChild(loadingDiv) scrollToBottom() } // Hide loading indicator function hideLoadingIndicator() { const loadingIndicator = document.getElementById("loading-indicator") if (loadingIndicator) { loadingIndicator.remove() } } // Scroll to bottom of chat function scrollToBottom() { const chatMessages = document.getElementById("chat-messages") chatMessages.scrollTop = chatMessages.scrollHeight } // Show notification function showNotification(message, type = "success") { const container = document.getElementById("notification-container") // Create notification element const notification = document.createElement("div") // Set class based on notification type if (type === "success") { notification.className = "notification mb-2 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded shadow-md" } else if (type === "error") { notification.className = "notification mb-2 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded shadow-md" } else if (type === "warning") { notification.className = "notification mb-2 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded shadow-md" } else if (type === "info") { notification.className = "notification mb-2 bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 rounded shadow-md" } notification.innerHTML = message // Add to container container.appendChild(notification) // Remove after 3 seconds setTimeout(() => { notification.style.opacity = "0" notification.style.transform = "translateY(-20px)" notification.style.transition = "opacity 0.5s, transform 0.5s" setTimeout(() => { notification.remove() }, 500) }, 3000) } // History panel functions function toggleHistoryPanel() { const historyPanel = document.getElementById("historyPanel") if (historyPanel.classList.contains("translate-x-full")) { historyPanel.classList.remove("translate-x-full") } else { historyPanel.classList.add("translate-x-full") } } function addToHistory(action, details) { // Get existing history or initialize empty array const history = JSON.parse(localStorage.getItem("aiAppHistory") || "[]") // Add new item history.unshift({ action, details, timestamp: new Date().toISOString(), }) // Keep only the last 20 items if (history.length > 20) { history.pop() } // Save back to localStorage localStorage.setItem("aiAppHistory", JSON.stringify(history)) // Update UI if history panel exists loadHistory() } function loadHistory() { const historyItemsContainer = document.getElementById("historyItems") if (!historyItemsContainer) return // Clear existing items historyItemsContainer.innerHTML = "" // Get history from localStorage const history = JSON.parse(localStorage.getItem("aiAppHistory") || "[]") if (history.length === 0) { historyItemsContainer.innerHTML = `

No history yet

` return } // Add each history item history.forEach((item) => { const historyItem = document.createElement("div") historyItem.className = "bg-gray-100 p-3 rounded-lg" const date = new Date(item.timestamp) const formattedDate = date.toLocaleDateString() + " " + date.toLocaleTimeString() historyItem.innerHTML = `

${item.action}

${formattedDate}

${item.details}

` historyItemsContainer.appendChild(historyItem) }) } // Helper function to get language name from code function getLanguageName(code) { const languages = { ar: "Arabic", en: "English", fr: "French", es: "Spanish", zh: "Chinese", } return languages[code] || code }