feat: CodeMirror markdown editor for identity/memory file editor
Replace plain textarea with CodeMirror 5 + markdown mode loaded from jsDelivr CDN. Editor fills the modal body via flex layout, theme-aware via CSS vars (cursor, selection, headings, bold/em/links/code all mapped to Cortex dark/light palette). Lazy init on first file open; history cleared per-file so undo doesn't bleed across files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1253,18 +1253,32 @@
|
||||
inputEl.addEventListener('input', syncHeight);
|
||||
|
||||
// ── File editor ──────────────────────────────────────────────
|
||||
const fileModal = document.getElementById('file-modal');
|
||||
const fileSidebar = document.getElementById('file-sidebar');
|
||||
const fileEditor = document.getElementById('file-editor');
|
||||
const filePreview = document.getElementById('file-preview');
|
||||
const fileRawBtn = document.getElementById('file-raw-btn');
|
||||
const filePreviewBtn = document.getElementById('file-preview-btn');
|
||||
const fileSaveBtn = document.getElementById('file-save-btn');
|
||||
const fileCloseBtn = document.getElementById('file-close-btn');
|
||||
const filesBtn = document.getElementById('files-btn');
|
||||
const fileModal = document.getElementById('file-modal');
|
||||
const fileSidebar = document.getElementById('file-sidebar');
|
||||
const fileEditorWrap = document.getElementById('file-editor-wrap');
|
||||
const filePreview = document.getElementById('file-preview');
|
||||
const fileRawBtn = document.getElementById('file-raw-btn');
|
||||
const filePreviewBtn = document.getElementById('file-preview-btn');
|
||||
const fileSaveBtn = document.getElementById('file-save-btn');
|
||||
const fileCloseBtn = document.getElementById('file-close-btn');
|
||||
const filesBtn = document.getElementById('files-btn');
|
||||
|
||||
let fileMode = 'preview'; // 'edit' or 'preview'
|
||||
let activeFileName = null;
|
||||
let fileMode = 'preview'; // 'edit' or 'preview'
|
||||
let activeFileName = null;
|
||||
let mdEditor = null;
|
||||
|
||||
function initMdEditor() {
|
||||
if (mdEditor) return;
|
||||
mdEditor = CodeMirror(fileEditorWrap, {
|
||||
mode: 'markdown',
|
||||
lineWrapping: true,
|
||||
lineNumbers: false,
|
||||
autofocus: false,
|
||||
tabSize: 2,
|
||||
indentWithTabs: false,
|
||||
extraKeys: { 'Ctrl-S': () => { fileSaveBtn.click(); return false; } },
|
||||
});
|
||||
}
|
||||
|
||||
// File groups — controls sidebar order and section labels
|
||||
const FILE_GROUPS = [
|
||||
@@ -1346,17 +1360,19 @@
|
||||
function setFileMode(mode) {
|
||||
fileMode = mode;
|
||||
if (mode === 'edit') {
|
||||
fileEditor.classList.remove('hidden');
|
||||
fileEditorWrap.classList.remove('hidden');
|
||||
filePreview.classList.remove('active');
|
||||
fileRawBtn.classList.add('active');
|
||||
filePreviewBtn.classList.remove('active');
|
||||
mdEditor.refresh();
|
||||
mdEditor.focus();
|
||||
} else {
|
||||
fileEditor.classList.add('hidden');
|
||||
fileEditorWrap.classList.add('hidden');
|
||||
filePreview.classList.add('active');
|
||||
fileRawBtn.classList.remove('active');
|
||||
filePreviewBtn.classList.add('active');
|
||||
if (typeof marked !== 'undefined') {
|
||||
filePreview.innerHTML = marked.parse(fileEditor.value);
|
||||
filePreview.innerHTML = marked.parse(mdEditor.getValue());
|
||||
filePreview.querySelectorAll('a').forEach(a => {
|
||||
a.target = '_blank'; a.rel = 'noopener noreferrer';
|
||||
});
|
||||
@@ -1366,10 +1382,12 @@
|
||||
|
||||
async function loadFile(name) {
|
||||
setActiveFile(name);
|
||||
initMdEditor();
|
||||
const res = await fetch(`/files/${encodeURIComponent(name)}?${_fileParams}`);
|
||||
if (!res.ok) { fileEditor.value = `Error loading ${name}`; return; }
|
||||
if (!res.ok) { mdEditor.setValue(`Error loading ${name}`); return; }
|
||||
const data = await res.json();
|
||||
fileEditor.value = data.content;
|
||||
mdEditor.setValue(data.content);
|
||||
mdEditor.clearHistory();
|
||||
setFileMode(fileMode);
|
||||
}
|
||||
|
||||
@@ -1396,7 +1414,7 @@
|
||||
const res = await fetch(`/files/${encodeURIComponent(activeFileName)}?${_fileParams}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content: fileEditor.value }),
|
||||
body: JSON.stringify({ content: mdEditor.getValue() }),
|
||||
});
|
||||
if (res.ok) {
|
||||
showToast('File saved', 'success');
|
||||
@@ -1421,13 +1439,13 @@
|
||||
const sessionSearchResults = document.getElementById('session-search-results');
|
||||
|
||||
function _showFileView() {
|
||||
fileEditor.style.display = '';
|
||||
setFileMode(fileMode);
|
||||
filePreview.style.display = '';
|
||||
sessionSearchResults.style.display = 'none';
|
||||
}
|
||||
|
||||
function _showSearchResults(html) {
|
||||
fileEditor.style.display = 'none';
|
||||
fileEditorWrap.classList.add('hidden');
|
||||
filePreview.style.display = 'none';
|
||||
sessionSearchResults.style.display = '';
|
||||
sessionSearchResults.innerHTML = html;
|
||||
|
||||
Reference in New Issue
Block a user