feat: Implement CodeMirror 6 file editor with tab support
Implement Phase 1 of the file editor & chat UI redesign: - CodeMirror 6 integration with syntax highlighting - Multi-file tab support with dirty state tracking - Custom dark theme matching GitHub's color scheme - Keyboard shortcuts (Ctrl+S to save, Ctrl+W to close tab) - Mobile-responsive design with proper touch targets - Fallback to basic textarea if CodeMirror fails to load Technical details: - Import map for ESM modules from node_modules - Language support for JS, Python, HTML, CSS, JSON, Markdown - Auto-initialization on DOM ready - Global window.fileEditor instance for integration - Serve node_modules at /claude/node_modules for import map Files added: - public/claude-ide/components/file-editor.js (main component) - public/claude-ide/components/file-editor.css (responsive styles) Files modified: - public/claude-ide/index.html (import map, script tags) - public/claude-ide/ide.js (updated loadFile function) - server.js (serve node_modules for CodeMirror) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -855,60 +855,74 @@ async function loadFile(filePath) {
|
||||
const res = await fetch(`/claude/api/file/${encodeURIComponent(filePath)}`);
|
||||
const data = await res.json();
|
||||
|
||||
const isHtmlFile = filePath.toLowerCase().endsWith('.html') || filePath.toLowerCase().endsWith('.htm');
|
||||
|
||||
const editorEl = document.getElementById('file-editor');
|
||||
|
||||
if (isHtmlFile) {
|
||||
// HTML file - show with preview option
|
||||
editorEl.innerHTML = `
|
||||
<div class="file-header">
|
||||
<h2>${filePath}</h2>
|
||||
<div class="file-actions">
|
||||
<button class="btn-secondary btn-sm" onclick="editFile('${filePath}')">Edit</button>
|
||||
<button class="btn-primary btn-sm" onclick="showHtmlPreview('${filePath}')">👁️ Preview</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-content" id="file-content-view">
|
||||
<div class="view-toggle">
|
||||
<button class="toggle-btn active" data-view="code" onclick="switchFileView('code')">Code</button>
|
||||
<button class="toggle-btn" data-view="preview" onclick="switchFileView('preview')">Preview</button>
|
||||
</div>
|
||||
<div class="code-view">
|
||||
<pre><code class="language-html">${escapeHtml(data.content)}</code></pre>
|
||||
</div>
|
||||
<div class="preview-view" style="display: none;">
|
||||
<iframe id="html-preview-frame" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Store file content for preview
|
||||
window.currentFileContent = data.content;
|
||||
window.currentFilePath = filePath;
|
||||
|
||||
// Highlight code
|
||||
if (window.hljs) {
|
||||
document.querySelectorAll('#file-content-view pre code').forEach((block) => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
}
|
||||
// Check if FileEditor component is available
|
||||
if (window.fileEditor) {
|
||||
// Use the new CodeMirror-based editor
|
||||
await window.fileEditor.openFile(filePath, data.content || '');
|
||||
} else {
|
||||
// Non-HTML file - show as before
|
||||
editorEl.innerHTML = `
|
||||
<div class="file-header">
|
||||
<h2>${filePath}</h2>
|
||||
<div class="file-actions">
|
||||
<button class="btn-secondary btn-sm" onclick="editFile('${filePath}')">Edit</button>
|
||||
// Fallback to the old view if FileEditor is not loaded yet
|
||||
console.warn('[loadFile] FileEditor not available, using fallback');
|
||||
const editorEl = document.getElementById('file-editor');
|
||||
|
||||
const isHtmlFile = filePath.toLowerCase().endsWith('.html') || filePath.toLowerCase().endsWith('.htm');
|
||||
|
||||
if (isHtmlFile) {
|
||||
// HTML file - show with preview option
|
||||
editorEl.innerHTML = `
|
||||
<div class="file-header">
|
||||
<h2>${filePath}</h2>
|
||||
<div class="file-actions">
|
||||
<button class="btn-secondary btn-sm" onclick="editFile('${filePath}')">Edit</button>
|
||||
<button class="btn-primary btn-sm" onclick="showHtmlPreview('${filePath}')">👁️ Preview</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-content">
|
||||
<div class="markdown-body">${data.html}</div>
|
||||
</div>
|
||||
`;
|
||||
<div class="file-content" id="file-content-view">
|
||||
<div class="view-toggle">
|
||||
<button class="toggle-btn active" data-view="code" onclick="switchFileView('code')">Code</button>
|
||||
<button class="toggle-btn" data-view="preview" onclick="switchFileView('preview')">Preview</button>
|
||||
</div>
|
||||
<div class="code-view">
|
||||
<pre><code class="language-html">${escapeHtml(data.content)}</code></pre>
|
||||
</div>
|
||||
<div class="preview-view" style="display: none;">
|
||||
<iframe id="html-preview-frame" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Store file content for preview
|
||||
window.currentFileContent = data.content;
|
||||
window.currentFilePath = filePath;
|
||||
|
||||
// Highlight code
|
||||
if (window.hljs) {
|
||||
document.querySelectorAll('#file-content-view pre code').forEach((block) => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Non-HTML file - show content
|
||||
editorEl.innerHTML = `
|
||||
<div class="file-header">
|
||||
<h2>${filePath}</h2>
|
||||
</div>
|
||||
<div class="file-content">
|
||||
<pre><code>${escapeHtml(data.content || '')}</code></pre>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading file:', error);
|
||||
const editorEl = document.getElementById('file-editor');
|
||||
if (editorEl) {
|
||||
editorEl.innerHTML = `
|
||||
<div class="file-error">
|
||||
<h2>Error loading file</h2>
|
||||
<p>${error.message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user