commit 117aef460e094308403a13517bea636863f67944
parent 736262f56a1ca26882e2cc893bb5d25d3758994a
Author: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Date: Sun, 18 Jan 2026 07:20:00 +0000
Add enhanced code blocks with copy, compiler, and download functionality
Co-authored-by: notamitgamer <199483577+notamitgamer@users.noreply.github.com>
Diffstat:
| M | index.html | | | 106 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | test_backend.py | | | 13 | ++++++++++++- |
2 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/index.html b/index.html
@@ -476,6 +476,112 @@
headerIds: false,
mangle: false
});
+
+ // Custom renderer for code blocks with copy/compiler/download buttons
+ const renderer = new marked.Renderer();
+ renderer.code = function(code, lang) {
+ const language = lang || 'text';
+ const escapedCode = code.replace(/</g, '<').replace(/>/g, '>');
+ const codeId = 'code-' + Math.random().toString(36).substr(2, 9);
+
+ return `
+ <div class="code-block-wrapper">
+ <div class="code-block-header">
+ <span class="code-block-lang">${language}</span>
+ <div class="code-block-actions">
+ <button class="code-block-btn" onclick="copyCodeBlock('${codeId}')">
+ <svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
+ Copy
+ </button>
+ <button class="code-block-btn" onclick="openInCompiler('${codeId}', '${language}')">
+ <svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path></svg>
+ Run
+ </button>
+ <button class="code-block-btn" onclick="downloadCodeBlock('${codeId}', '${language}')">
+ <svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
+ Download
+ </button>
+ </div>
+ </div>
+ <pre><code id="${codeId}" class="language-${language}">${escapedCode}</code></pre>
+ </div>
+ `;
+ };
+ marked.setOptions({ renderer: renderer });
+
+ // Code block utility functions
+ window.copyCodeBlock = async function(codeId) {
+ const codeElement = document.getElementById(codeId);
+ if (!codeElement) return;
+
+ const code = codeElement.textContent;
+ try {
+ await navigator.clipboard.writeText(code);
+ // Show success feedback
+ const btn = event.target.closest('button');
+ const originalText = btn.innerHTML;
+ btn.innerHTML = '<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>Copied!';
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalText;
+ btn.classList.remove('success');
+ }, 2000);
+ } catch (err) {
+ console.error('Failed to copy:', err);
+ }
+ }
+
+ window.openInCompiler = function(codeId, language) {
+ const codeElement = document.getElementById(codeId);
+ if (!codeElement) return;
+
+ const code = codeElement.textContent;
+ const compressed = LZString.compressToEncodedURIComponent(code);
+
+ let urlLang = 'python';
+ if (language === 'javascript' || language === 'js') urlLang = 'nodejs';
+ else if (language === 'c' || language === 'cpp' || language === 'c++') urlLang = 'cpp';
+ else if (language === 'java') urlLang = 'java';
+
+ window.open(`https://compiler.amit.is-a.dev/${urlLang}?code=${compressed}`, '_blank');
+ }
+
+ window.downloadCodeBlock = function(codeId, language) {
+ const codeElement = document.getElementById(codeId);
+ if (!codeElement) return;
+
+ const code = codeElement.textContent;
+ const ext = getFileExtension(language);
+ const filename = `code.${ext}`;
+
+ const blob = new Blob([code], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
+
+ function getFileExtension(language) {
+ const ext = {
+ 'python': 'py',
+ 'javascript': 'js',
+ 'java': 'java',
+ 'c': 'c',
+ 'cpp': 'cpp',
+ 'c++': 'cpp',
+ 'csharp': 'cs',
+ 'go': 'go',
+ 'rust': 'rs',
+ 'typescript': 'ts',
+ 'html': 'html',
+ 'css': 'css'
+ };
+ return ext[language.toLowerCase()] || 'txt';
+ }
// --- AUTH LOGIC ---
onAuthStateChanged(auth, (user) => {
diff --git a/test_backend.py b/test_backend.py
@@ -28,7 +28,18 @@ try:
# Check if routes are defined
routes = [rule.rule for rule in backend.app.url_map.iter_rules()]
- expected_routes = ['/', '/api/chat', '/api/generate-title']
+ expected_routes = [
+ '/',
+ '/health',
+ '/api/chat',
+ '/api/generate-title',
+ '/api/profile',
+ '/api/chats',
+ '/api/chats/<chat_id>',
+ '/api/chats/<chat_id>/rename',
+ '/api/chats/<chat_id>/export',
+ '/api/chats/<chat_id>/pin'
+ ]
for route in expected_routes:
assert route in routes, f"Route {route} not found"