Add textarea fallback for CodeMirror initialization failures and fix module dependency conflicts with import map

This commit is contained in:
Bruno Charest 2026-03-21 12:43:30 -04:00
parent 25c7533409
commit eac1980e62
3 changed files with 70 additions and 39 deletions

View File

@ -16,6 +16,7 @@
let editorView = null; let editorView = null;
let editorVault = null; let editorVault = null;
let editorPath = null; let editorPath = null;
let fallbackEditorEl = null;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// File extension → Lucide icon mapping // File extension → Lucide icon mapping
@ -846,41 +847,48 @@
editorView.destroy(); editorView.destroy();
editorView = null; editorView = null;
} }
fallbackEditorEl = null;
// Wait for CodeMirror to be available try {
await waitForCodeMirror(); await waitForCodeMirror();
const { EditorView, EditorState, basicSetup, markdown, oneDark, keymap } = window.CodeMirror; const { EditorView, EditorState, basicSetup, markdown, oneDark, keymap } = window.CodeMirror;
// Determine theme const currentTheme = document.documentElement.getAttribute("data-theme");
const currentTheme = document.documentElement.getAttribute("data-theme"); const extensions = [
const extensions = [ basicSetup,
basicSetup, markdown(),
markdown(), keymap.of([{
keymap.of([{ key: "Mod-s",
key: "Mod-s", run: () => {
run: () => { saveFile();
saveFile(); return true;
return true; }
} }]),
}]), EditorView.lineWrapping,
EditorView.lineWrapping, ];
];
if (currentTheme === "dark") { if (currentTheme === "dark") {
extensions.push(oneDark); extensions.push(oneDark);
}
const state = EditorState.create({
doc: rawData.raw,
extensions: extensions,
});
editorView = new EditorView({
state: state,
parent: bodyEl,
});
} catch (err) {
console.error("CodeMirror init failed, falling back to textarea:", err);
fallbackEditorEl = document.createElement("textarea");
fallbackEditorEl.className = "fallback-editor";
fallbackEditorEl.value = rawData.raw;
bodyEl.appendChild(fallbackEditorEl);
} }
const state = EditorState.create({
doc: rawData.raw,
extensions: extensions,
});
editorView = new EditorView({
state: state,
parent: bodyEl,
});
modal.classList.add("active"); modal.classList.add("active");
safeCreateIcons(); safeCreateIcons();
} }
@ -903,14 +911,15 @@
editorView.destroy(); editorView.destroy();
editorView = null; editorView = null;
} }
fallbackEditorEl = null;
editorVault = null; editorVault = null;
editorPath = null; editorPath = null;
} }
async function saveFile() { async function saveFile() {
if (!editorView || !editorVault || !editorPath) return; if ((!editorView && !fallbackEditorEl) || !editorVault || !editorPath) return;
const content = editorView.state.doc.toString(); const content = editorView ? editorView.state.doc.toString() : fallbackEditorEl.value;
const saveBtn = document.getElementById("editor-save"); const saveBtn = document.getElementById("editor-save");
const originalText = saveBtn.textContent; const originalText = saveBtn.textContent;

View File

@ -9,17 +9,24 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css" id="hljs-theme-light" disabled> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css" id="hljs-theme-light" disabled>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://unpkg.com/lucide@0.344.0/dist/umd/lucide.min.js"></script> <script src="https://unpkg.com/lucide@0.344.0/dist/umd/lucide.min.js"></script>
<script type="importmap">
{
"imports": {
"@codemirror/state": "https://esm.sh/@codemirror/state@6.2.0"
}
}
</script>
<!-- CodeMirror 6 --> <!-- CodeMirror 6 -->
<script type="module"> <script type="module">
import { EditorView, keymap, lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine } from "https://esm.sh/@codemirror/view@6.9.0"; import { EditorView, keymap, lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine } from "https://esm.sh/@codemirror/view@6.9.0?external=@codemirror/state";
import { EditorState } from "https://esm.sh/@codemirror/state@6.2.0"; import { EditorState } from "@codemirror/state";
import { defaultHighlightStyle, syntaxHighlighting, indentOnInput, bracketMatching, foldGutter, foldKeymap } from "https://esm.sh/@codemirror/language@6.6.0"; import { defaultHighlightStyle, syntaxHighlighting, indentOnInput, bracketMatching, foldGutter, foldKeymap } from "https://esm.sh/@codemirror/language@6.6.0?external=@codemirror/state";
import { defaultKeymap, history, historyKeymap } from "https://esm.sh/@codemirror/commands@6.2.3"; import { defaultKeymap, history, historyKeymap } from "https://esm.sh/@codemirror/commands@6.2.3?external=@codemirror/state";
import { searchKeymap, highlightSelectionMatches } from "https://esm.sh/@codemirror/search@6.3.0"; import { searchKeymap, highlightSelectionMatches } from "https://esm.sh/@codemirror/search@6.3.0?external=@codemirror/state";
import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "https://esm.sh/@codemirror/autocomplete@6.5.0"; import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "https://esm.sh/@codemirror/autocomplete@6.5.0?external=@codemirror/state";
import { markdown } from "https://esm.sh/@codemirror/lang-markdown@6.1.0"; import { markdown } from "https://esm.sh/@codemirror/lang-markdown@6.1.0?external=@codemirror/state";
import { oneDark } from "https://esm.sh/@codemirror/theme-one-dark@6.1.0"; import { oneDark } from "https://esm.sh/@codemirror/theme-one-dark@6.1.0?external=@codemirror/state";
const basicSetup = [ const basicSetup = [
lineNumbers(), lineNumbers(),

View File

@ -937,6 +937,21 @@ a:hover {
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
} }
.fallback-editor {
width: 100%;
height: 100%;
min-height: 420px;
border: none;
outline: none;
resize: none;
padding: 16px;
background: var(--code-bg);
color: var(--text-primary);
font-family: 'JetBrains Mono', monospace;
font-size: 0.9rem;
line-height: 1.55;
}
/* Mobile editor */ /* Mobile editor */
@media (max-width: 768px) { @media (max-width: 768px) {
.editor-modal { .editor-modal {