use codemirror over prism
Hunter
Monday, July 7th, 2025 at 6:45 PM
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>HTML Laboratory</title>
7 + <script type="module">
8 + import {EditorView, keymap} from "https://esm.sh/@codemirror/view@6"
9 + import {EditorState} from "https://esm.sh/@codemirror/state@6"
10 + import {defaultKeymap} from "https://esm.sh/@codemirror/commands@6"
11 + import {html} from "https://esm.sh/@codemirror/lang-html@6"
12 + import {oneDark} from "https://esm.sh/@codemirror/theme-one-dark@6"
13 +
14 + window.CodeMirror = {EditorView, EditorState, keymap, defaultKeymap, html, oneDark};
15 + </script>
16 <style>
17 :root {
18 --bg-color: #2d2d2d;
54 #editor {
55 width: 100%;
56 height: 100%;
- border: none;
- outline: none;
57 font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
58 font-size: 21px;
- padding: 15px;
- overflow: auto;
59 + }
60 +
61 + /* CodeMirror styling */
62 + .cm-editor {
63 + width: 100%;
64 + height: 100%;
65 background: var(--editor-bg);
66 color: var(--text-color);
- tab-size: 4;
- white-space: pre-wrap;
- overflow-x: auto;
- display: block;
67 + }
68 +
69 + .cm-content {
70 + padding: 15px;
71 + font-size: 21px;
72 line-height: 1.5;
73 }
74
- #editor:empty:before {
- content: attr(data-placeholder);
- color: #666;
75 + .cm-focused {
76 + outline: none;
77 }
78
79 #preview {
113 </head>
114 <body>
115 <div class="editor-pane">
- <div id="editor" contenteditable="true" spellcheck="false" data-placeholder="Type your HTML, CSS, and JavaScript here..."></div>
116 + <div id="editor"></div>
117 </div>
118
119 <div class="preview-pane">
120 <iframe id="preview" sandbox="allow-scripts"></iframe>
121 </div>
122
- <script>
123 + <script type="module">
124 let updateTimer;
- const editor = document.getElementById('editor');
125 + let editorView;
126 const preview = document.getElementById('preview');
127 const storageKey = 'html-lab-content';
128
129 function updatePreview() {
- const code = editor.textContent;
130 + const code = editorView.state.doc.toString();
131 const blob = new Blob([code], { type: 'text/html' });
132 const url = URL.createObjectURL(blob);
133 preview.src = url;
138
139 function saveToStorage() {
140 try {
- // Save both the inner HTML (for whitespace preservation) and text content
- const data = {
- html: editor.innerHTML,
- text: editor.textContent
- };
- localStorage.setItem(storageKey, JSON.stringify(data));
141 + const code = editorView.state.doc.toString();
142 + localStorage.setItem(storageKey, code);
143 } catch (e) {
144 // Handle localStorage errors silently
145 console.warn('Could not save to localStorage:', e);
150 try {
151 const saved = localStorage.getItem(storageKey);
152 if (saved !== null) {
153 + // Handle old format that might be JSON
154 try {
- // Try to parse as JSON first (new format)
155 const data = JSON.parse(saved);
- if (data.html) {
- editor.innerHTML = data.html;
- } else if (data.text) {
- editor.textContent = data.text;
- }
156 + return data.text || data.html || saved;
157 } catch (parseError) {
- // Fallback to old format (plain text)
- editor.textContent = saved;
158 + return saved;
159 }
- return true;
160 }
161 } catch (e) {
162 // Handle localStorage errors silently
163 console.warn('Could not load from localStorage:', e);
164 }
- return false;
165 + return '';
166 }
167
-
- // Handle Tab key to insert tab character
- editor.addEventListener('keydown', function(e) {
- if (e.key === 'Tab') {
- e.preventDefault();
- document.execCommand('insertText', false, '\t');
- }
- });
-
- // Update preview after 0.5 seconds of no typing
- editor.addEventListener('input', function() {
- clearTimeout(updateTimer);
- updateTimer = setTimeout(updatePreview, 500);
-
- // Save to localStorage on every change
- saveToStorage();
- });
-
168 // Save functionality with Cmd+S
169 document.addEventListener('keydown', function(e) {
170 if ((e.metaKey || e.ctrlKey) && e.key === 's') {
174 });
175
176 function saveFile() {
- const code = editor.textContent;
177 + const code = editorView.state.doc.toString();
178 const blob = new Blob([code], { type: 'text/html' });
179 const url = URL.createObjectURL(blob);
180
186 URL.revokeObjectURL(url);
187 }
188
- // Initialize the editor content and render
- function initialize() {
- // Always try to load from localStorage regardless of refresh type
- loadFromStorage();
189 + // Wait for CodeMirror to be available
190 + function initializeCodeMirror() {
191 + if (!window.CodeMirror) {
192 + setTimeout(initializeCodeMirror, 100);
193 + return;
194 + }
195 +
196 + const {EditorView, EditorState, keymap, defaultKeymap, html, oneDark} = window.CodeMirror;
197
- // Focus the editor
- editor.focus();
198 + // Load saved content
199 + const savedContent = loadFromStorage();
200
201 + // Create CodeMirror editor
202 + editorView = new EditorView({
203 + state: EditorState.create({
204 + doc: savedContent,
205 + extensions: [
206 + keymap.of(defaultKeymap),
207 + html(),
208 + oneDark,
209 + EditorView.updateListener.of((update) => {
210 + if (update.docChanged) {
211 + clearTimeout(updateTimer);
212 + updateTimer = setTimeout(updatePreview, 500);
213 + saveToStorage();
214 + }
215 + })
216 + ]
217 + }),
218 + parent: document.getElementById('editor')
219 + });
220 +
221 // Initial render
222 updatePreview();
223 }
224
225 // Initialize when page loads
- initialize();
226 + initializeCodeMirror();
227 </script>
228 </body>
229 </html>