📜
save.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
// ===== Save & Export Module ===== // Wait for editor to be ready document.addEventListener('DOMContentLoaded', () => { // Get editor reference from global API const { editor, getValue } = window.editorAPI || {}; if (!editor) { console.warn('Editor not found, save functionality may be limited'); return; } // DOM elements const saveBtn = document.getElementById("saveBtn"); const saveMenu = document.getElementById("saveMenu"); const doSave = document.getElementById("doSave"); const doArtifact = document.getElementById("doArtifact"); // ===== Save Menu Management ===== saveBtn.addEventListener("click", (e) => { const open = saveMenu.classList.toggle("open"); saveBtn.setAttribute("aria-expanded", String(open)); e.stopPropagation(); }); document.addEventListener("click", (e) => { if (!saveMenu.contains(e.target) && !saveBtn.contains(e.target)) { saveMenu.classList.remove("open"); saveBtn.setAttribute("aria-expanded", "false"); } }); // ===== Save Functions ===== // Get current file info function getFileInfo() { const content = getValue ? getValue() : editor.getValue(); const mode = editor.session.getMode().$id; // Determine file extension based on mode let extension = '.txt'; let mimeType = 'text/plain'; switch (mode) { case 'ace/mode/html': extension = '.html'; mimeType = 'text/html'; break; case 'ace/mode/javascript': extension = '.js'; mimeType = 'text/javascript'; break; case 'ace/mode/css': extension = '.css'; mimeType = 'text/css'; break; case 'ace/mode/php': extension = '.php'; mimeType = 'text/plain'; break; case 'ace/mode/python': extension = '.py'; mimeType = 'text/plain'; break; case 'ace/mode/json': extension = '.json'; mimeType = 'application/json'; break; } // Try to extract filename from content (for HTML files) let filename = 'untitled' + extension; if (mode === 'ace/mode/html') { const titleMatch = content.match(/<title[^>]*>([^<]+)<\/title>/i); if (titleMatch) { // Clean title for filename const cleanTitle = titleMatch[1] .trim() .replace(/[^\w\s-]/g, '') // Remove special chars .replace(/\s+/g, '-') // Replace spaces with dashes .toLowerCase(); if (cleanTitle && cleanTitle.length > 0) { filename = cleanTitle + extension; } } } return { content, filename, extension, mimeType, mode }; } // Download file function downloadFile(content, filename, mimeType = 'text/plain') { try { const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); // Clean up the URL object setTimeout(() => URL.revokeObjectURL(url), 100); return true; } catch (error) { console.error('Download failed:', error); return false; } } // Copy to clipboard async function copyToClipboard(text) { try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); return true; } else { // Fallback for older browsers or non-HTTPS const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); const result = document.execCommand('copy'); document.body.removeChild(textArea); return result; } } catch (error) { console.error('Copy to clipboard failed:', error); return false; } } // Show temporary notification function showNotification(message, type = 'success') { // Create notification element const notification = document.createElement('div'); notification.textContent = message; notification.style.cssText = ` position: fixed; top: 80px; right: 20px; padding: 12px 20px; background: ${type === 'success' ? '#1b4332' : '#722f37'}; color: ${type === 'success' ? '#90ee90' : '#ff9999'}; border: 1px solid ${type === 'success' ? '#2d6a4f' : '#c9184a'}; border-radius: 8px; font-size: 14px; font-family: system-ui, sans-serif; z-index: 1000; opacity: 0; transform: translateX(100%); transition: all 0.3s ease; `; document.body.appendChild(notification); // Animate in setTimeout(() => { notification.style.opacity = '1'; notification.style.transform = 'translateX(0)'; }, 10); // Animate out and remove setTimeout(() => { notification.style.opacity = '0'; notification.style.transform = 'translateX(100%)'; setTimeout(() => document.body.removeChild(notification), 300); }, 3000); } // ===== Save Actions ===== // Regular save (download file) function handleSave() { const fileInfo = getFileInfo(); const success = downloadFile(fileInfo.content, fileInfo.filename, fileInfo.mimeType); if (success) { showNotification(`Downloaded: ${fileInfo.filename}`); } else { showNotification('Save failed - please try again', 'error'); } saveMenu.classList.remove("open"); saveBtn.setAttribute("aria-expanded", "false"); } // Save as artifact (copy to clipboard with metadata) async function handleArtifact() { const fileInfo = getFileInfo(); // Create artifact-style content const artifactContent = `# ${fileInfo.filename} \`\`\`${fileInfo.mode.replace('ace/mode/', '')} ${fileInfo.content} \`\`\` <!-- File Info: - Mode: ${fileInfo.mode} - Extension: ${fileInfo.extension} - Generated: ${new Date().toISOString()} -->`; const success = await copyToClipboard(artifactContent); if (success) { showNotification('Code copied to clipboard as artifact!'); } else { showNotification('Copy failed - please try manually', 'error'); } saveMenu.classList.remove("open"); saveBtn.setAttribute("aria-expanded", "false"); } // ===== Event Listeners ===== doSave.addEventListener("click", handleSave); doArtifact.addEventListener("click", handleArtifact); // Keyboard shortcuts document.addEventListener('keydown', (e) => { // Ctrl/Cmd + S for save if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); handleSave(); } // Ctrl/Cmd + Shift + S for artifact if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'S') { e.preventDefault(); handleArtifact(); } }); // ===== Export Save API ===== window.saveAPI = { handleSave, handleArtifact, downloadFile, copyToClipboard, getFileInfo, showNotification }; console.log("Save module loaded successfully"); });