|
3 | 3 | <head> |
4 | 4 | <meta charset="UTF-8"> |
5 | 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
6 | | - <title>Editor — C++ Mode for Processing</title> |
| 6 | + <title>Editor - C++ Mode for Processing</title> |
7 | 7 | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css"> |
8 | 8 | <link rel="stylesheet" href="../assets/cppmode-theme.css"> |
9 | 9 | <style> |
10 | 10 | * { margin: 0; padding: 0; box-sizing: border-box; } |
11 | 11 | body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #fff; color: #111; height: 100vh; display: flex; flex-direction: column; overflow: hidden; } |
12 | | - #site-nav { border-bottom: 1px solid #e0e0e0; padding: 0 2rem; display: flex; align-items: center; justify-content: space-between; height: 52px; background: #fff; flex-shrink: 0; } |
13 | | - #site-nav a { color: #111; text-decoration: none; font-size: 14px; } |
14 | | - #site-nav .logo { font-weight: 600; font-size: 15px; } |
| 12 | + |
| 13 | + /* Override nav.js nav to not be sticky in the editor - it already has fixed height */ |
| 14 | + #site-nav { flex-shrink: 0; position: static !important; } |
| 15 | + |
| 16 | + /* Hide the sidebar - editor uses full width */ |
| 17 | + #site-sidebar { display: none; } |
| 18 | + |
15 | 19 | .editor-layout { display: flex; flex: 1; overflow: hidden; } |
| 20 | + |
16 | 21 | .editor-pane { display: flex; flex-direction: column; width: 50%; border-right: 1px solid #e0e0e0; } |
17 | 22 | .preview-pane { display: flex; flex-direction: column; width: 50%; } |
18 | | - .pane-toolbar { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.75rem; background: #f5f5f5; border-bottom: 1px solid #e0e0e0; flex-shrink: 0; } |
19 | | - .pane-label { font-size: 12px; color: #888; flex: 1; font-family: monospace; } |
| 23 | + |
| 24 | + .pane-toolbar { |
| 25 | + display: flex; |
| 26 | + align-items: center; |
| 27 | + gap: 0.75rem; |
| 28 | + padding: 0.5rem 0.75rem; |
| 29 | + background: #f7f7f7; |
| 30 | + border-bottom: 1px solid #e0e0e0; |
| 31 | + flex-shrink: 0; |
| 32 | + } |
| 33 | + .pane-label { font-size: 12px; color: #888; flex: 1; font-family: "SF Mono", "Fira Code", monospace; } |
| 34 | + |
20 | 35 | #status { font-size: 12px; color: #888; } |
21 | 36 | #status.compiling { color: #c07000; } |
22 | 37 | #status.error { color: #c00; } |
23 | | - #status.ok { color: #080; } |
24 | | - .btn-run { background: #e8b400; color: #111; border: none; padding: 0.4rem 1.1rem; border-radius: 5px; font-size: 13px; font-weight: 700; cursor: pointer; } |
25 | | - .btn-run:hover { background: #f5c400; } |
26 | | - .btn-run:disabled { background: #ccc; color: #888; cursor: not-allowed; } |
27 | | - .btn-stop { background: #eee; color: #555; border: 1px solid #ccc; padding: 0.4rem 0.9rem; border-radius: 5px; font-size: 12px; cursor: pointer; } |
28 | | - .btn-stop:hover { background: #ddd; } |
29 | | - .CodeMirror { flex: 1; height: 100%; font-size: 13px; line-height: 1.55; } |
| 38 | + #status.ok { color: #3a3; } |
| 39 | + |
| 40 | + .btn-run { |
| 41 | + background: #e8b400; |
| 42 | + color: #111; |
| 43 | + border: none; |
| 44 | + padding: 0.4rem 1.1rem; |
| 45 | + border-radius: 6px; |
| 46 | + font-size: 13px; |
| 47 | + font-weight: 700; |
| 48 | + cursor: pointer; |
| 49 | + } |
| 50 | + .btn-run:hover { background: #d4a300; } |
| 51 | + .btn-run:disabled { background: #e0e0e0; color: #aaa; cursor: not-allowed; } |
| 52 | + |
| 53 | + .btn-stop { |
| 54 | + background: #f0f0f0; |
| 55 | + color: #555; |
| 56 | + border: 1px solid #e0e0e0; |
| 57 | + padding: 0.4rem 0.9rem; |
| 58 | + border-radius: 6px; |
| 59 | + font-size: 12px; |
| 60 | + cursor: pointer; |
| 61 | + } |
| 62 | + .btn-stop:hover { background: #e6e6e6; } |
| 63 | + |
| 64 | + .CodeMirror { flex: 1; height: 100%; font-size: 13px; line-height: 1.6; } |
30 | 65 | .CodeMirror-scroll { height: 100% !important; } |
31 | | - #preview-area { flex: 1; background: #000; overflow: hidden; position: relative; } |
| 66 | + |
| 67 | + #preview-area { |
| 68 | + flex: 1; |
| 69 | + background: #111; |
| 70 | + display: flex; |
| 71 | + align-items: center; |
| 72 | + justify-content: center; |
| 73 | + overflow: hidden; |
| 74 | + } |
| 75 | + #preview-area canvas { display: block !important; } |
32 | 76 | #preview-area iframe { width: 100%; height: 100%; border: none; display: block; } |
33 | | - #preview-placeholder { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); color: #aaa; font-size: 13px; text-align: center; line-height: 2; pointer-events: none; } |
34 | | - #preview-placeholder kbd { background: #fff; border: 1px solid #ccc; padding: 2px 6px; border-radius: 3px; font-size: 12px; color: #666; } |
35 | | - #error-panel { display: none; background: #fff5f5; border-top: 1px solid #ffcccc; padding: 0.6rem 1rem; font-family: monospace; font-size: 11.5px; color: #c00; max-height: 130px; overflow-y: auto; white-space: pre; flex-shrink: 0; } |
| 77 | + |
| 78 | + #preview-placeholder { color: #555; font-size: 13px; text-align: center; line-height: 2.2; } |
| 79 | + #preview-placeholder kbd { |
| 80 | + background: #222; |
| 81 | + border: 1px solid #444; |
| 82 | + padding: 2px 6px; |
| 83 | + border-radius: 3px; |
| 84 | + font-size: 12px; |
| 85 | + color: #bbb; |
| 86 | + font-family: "SF Mono", "Fira Code", monospace; |
| 87 | + } |
| 88 | + |
| 89 | + #error-panel { |
| 90 | + display: none; |
| 91 | + background: #fff5f5; |
| 92 | + border-top: 1px solid #fcc; |
| 93 | + padding: 0.6rem 1rem; |
| 94 | + font-family: "SF Mono", "Fira Code", monospace; |
| 95 | + font-size: 11.5px; |
| 96 | + color: #c00; |
| 97 | + max-height: 130px; |
| 98 | + overflow-y: auto; |
| 99 | + white-space: pre; |
| 100 | + flex-shrink: 0; |
| 101 | + } |
36 | 102 | </style> |
37 | 103 | </head> |
38 | 104 | <body> |
39 | | - |
40 | | -<nav id="site-nav"> |
41 | | - <a class="logo" href="/">C++ Mode for Processing</a> |
42 | | - <a href="/reference" style="color:#888;font-size:13px;">Reference</a> |
43 | | -</nav> |
| 105 | +<nav id="site-nav"></nav> |
44 | 106 |
|
45 | 107 | <div class="editor-layout"> |
46 | 108 | <div class="editor-pane"> |
47 | 109 | <div class="pane-toolbar"> |
48 | | - <span class="pane-label">sketch.cpp</span> |
| 110 | + <span class="pane-label">sketch.pde</span> |
49 | 111 | <span id="status"></span> |
| 112 | + <button class="btn-stop" onclick="stopSketch()">Stop</button> |
50 | 113 | <button class="btn-run" id="btn-run" onclick="runSketch()">▶ Run</button> |
51 | 114 | </div> |
52 | | - <textarea id="code-editor"></textarea> |
| 115 | + <textarea id="code"></textarea> |
53 | 116 | <div id="error-panel"></div> |
54 | 117 | </div> |
| 118 | + |
55 | 119 | <div class="preview-pane"> |
56 | 120 | <div class="pane-toolbar"> |
57 | | - <span class="pane-label">Output</span> |
58 | | - <button class="btn-stop" onclick="stopSketch()">■ Stop</button> |
| 121 | + <span class="pane-label">preview</span> |
59 | 122 | </div> |
60 | 123 | <div id="preview-area"> |
61 | | - <div id="preview-placeholder">Press <kbd>Run</kbd> or <kbd>Ctrl+Enter</kbd></div> |
| 124 | + <div id="preview-placeholder"> |
| 125 | + Press <kbd>Ctrl+Enter</kbd> to run your sketch |
| 126 | + </div> |
62 | 127 | </div> |
63 | 128 | </div> |
64 | 129 | </div> |
65 | 130 |
|
66 | 131 | <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script> |
67 | 132 | <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/clike/clike.min.js"></script> |
68 | 133 | <script src="../assets/cppmode-keywords.js"></script> |
| 134 | +<script src="../assets/cm-cppmode.js"></script> |
69 | 135 | <script src="../assets/cppmode.js"></script> |
| 136 | +<script src="../assets/nav.js"></script> |
70 | 137 | <script> |
71 | 138 | const API = 'https://bench-volumes-pontiac-riders.trycloudflare.com'; |
72 | 139 |
|
|
87 | 154 | angle += 0.02f; |
88 | 155 | }`; |
89 | 156 |
|
90 | | -const editor = CodeMirror.fromTextArea(document.getElementById('code-editor'), { |
91 | | - mode: 'text/x-cppmode', |
| 157 | +const editor = CodeMirror.fromTextArea(document.getElementById('code'), { |
| 158 | + mode: 'cppmode', |
92 | 159 | theme: 'cppmode', |
93 | 160 | lineNumbers: true, |
94 | 161 | indentUnit: 2, |
95 | 162 | tabSize: 2, |
96 | 163 | indentWithTabs: false, |
97 | 164 | autofocus: true, |
| 165 | + matchBrackets: true, |
| 166 | + autoCloseBrackets: true, |
| 167 | + lineWrapping: false, |
| 168 | + extraKeys: { |
| 169 | + 'Ctrl-Enter': runSketch, |
| 170 | + 'Cmd-Enter': runSketch, |
| 171 | + 'Tab': cm => cm.execCommand('indentMore'), |
| 172 | + 'Shift-Tab': cm => cm.execCommand('indentLess'), |
| 173 | + 'Backspace': cm => cm.execCommand('delCharBefore'), |
| 174 | + }, |
98 | 175 | }); |
99 | 176 | editor.setValue(DEFAULT); |
100 | 177 | editor.setSize('100%', '100%'); |
101 | 178 |
|
102 | | -// Patch Event.prototype.preventDefault so Emscripten GLFW keydown handlers |
103 | | -// can't prevent default when the CodeMirror textarea is focused. |
104 | | -const _origPreventDefault = Event.prototype.preventDefault; |
105 | | -Event.prototype.preventDefault = function() { |
106 | | - if (this.type === 'keydown' || this.type === 'keyup' || this.type === 'keypress') { |
107 | | - const ta = document.querySelector('.CodeMirror textarea'); |
108 | | - if (ta && document.activeElement === ta) return; |
109 | | - } |
110 | | - return _origPreventDefault.call(this); |
111 | | -}; |
| 179 | +// Load pre-built default sketch immediately -- no compile needed |
| 180 | +async function loadDefaultSketch() { |
| 181 | + const area = document.getElementById('preview-area'); |
| 182 | + const placeholder = document.getElementById('preview-placeholder'); |
| 183 | + if (placeholder) placeholder.style.display = 'none'; |
| 184 | + |
| 185 | + const canvas = document.createElement('canvas'); |
| 186 | + canvas.id = 'canvas'; |
| 187 | + canvas.setAttribute('tabindex', '-1'); |
| 188 | + area.appendChild(canvas); |
| 189 | + |
| 190 | + const [wasmResp, dataResp] = await Promise.all([ |
| 191 | + fetch('default_sketch.wasm'), |
| 192 | + fetch('default_sketch.data'), |
| 193 | + ]); |
| 194 | + const wasmBinary = new Uint8Array(await wasmResp.arrayBuffer()); |
| 195 | + const dataBytes = new Uint8Array(await dataResp.arrayBuffer()); |
| 196 | + |
| 197 | + window.Module = { |
| 198 | + canvas: canvas, |
| 199 | + wasmBinary: wasmBinary, |
| 200 | + onRuntimeInitialized: function() { setStatus('Running', 'ok'); }, |
| 201 | + getPreloadedPackage: function(name, size) { return dataBytes.buffer; }, |
| 202 | + print: t => console.log('[sketch]', t), |
| 203 | + printErr: t => console.warn('[sketch]', t), |
| 204 | + }; |
| 205 | + |
| 206 | + const script = document.createElement('script'); |
| 207 | + script.src = 'default_sketch.js'; |
| 208 | + document.head.appendChild(script); |
| 209 | +} |
| 210 | + |
| 211 | +window.addEventListener('load', loadDefaultSketch); |
| 212 | + |
| 213 | + |
| 214 | + |
112 | 215 |
|
113 | 216 | function setStatus(msg, cls) { |
114 | 217 | const el = document.getElementById('status'); |
115 | 218 | el.textContent = msg; |
116 | 219 | el.className = cls || ''; |
117 | 220 | } |
118 | 221 |
|
119 | | -// Build iframe HTML for a compiled sketch |
| 222 | +function stopSketch() { |
| 223 | + const area = document.getElementById('preview-area'); |
| 224 | + const iframe = area.querySelector('iframe'); |
| 225 | + if (iframe) { try{URL.revokeObjectURL(iframe.src);}catch(e){} iframe.remove(); } |
| 226 | + const ph = document.getElementById('preview-placeholder'); |
| 227 | + if (ph) ph.style.display = ''; |
| 228 | + setStatus(''); |
| 229 | + editor.focus(); |
| 230 | +} |
| 231 | + |
| 232 | +// Build self-contained iframe HTML for a compiled sketch |
120 | 233 | function makeSketchHTML(jsUrl, wasmB64, dataB64) { |
121 | | - return `<!DOCTYPE html><html><head> |
122 | | -<style>*{margin:0;padding:0;overflow:hidden}body{background:#000}canvas{display:block;width:100%;height:100%}</style> |
123 | | -</head><body><canvas id="canvas"></canvas><script> |
124 | | -(function(){ |
125 | | - var w=atob('${wasmB64}'),wa=new Uint8Array(w.length); |
126 | | - for(var i=0;i<w.length;i++) wa[i]=w.charCodeAt(i); |
127 | | - var d=atob('${dataB64}'),da=new Uint8Array(d.length); |
128 | | - for(var i=0;i<d.length;i++) da[i]=d.charCodeAt(i); |
129 | | - window.Module={ |
130 | | - canvas:document.getElementById('canvas'), |
131 | | - wasmBinary:wa, |
132 | | - getPreloadedPackage:function(){return da.buffer;}, |
133 | | - onRuntimeInitialized:function(){parent.postMessage('running','*');}, |
134 | | - print:function(t){console.log(t);}, |
135 | | - printErr:function(t){console.warn(t);} |
136 | | - }; |
137 | | -})(); |
138 | | -<\/script><script src="${jsUrl}"><\/script></body></html>`; |
| 234 | + var esc = s => s.replace(/\/g,''); |
| 235 | + return '<!DOCTYPE html><html><head><style>*{margin:0;padding:0;overflow:hidden}body{background:#000}canvas{display:block;width:100vw;height:100vh}<\/style><\/head><body><canvas id="canvas"><\/canvas><script>(function(){var w=atob("'+wasmB64+'"),wa=new Uint8Array(w.length);for(var i=0;i<w.length;i++)wa[i]=w.charCodeAt(i);var d=atob("'+dataB64+'"),da=new Uint8Array(d.length);for(var i=0;i<d.length;i++)da[i]=d.charCodeAt(i);window.Module={canvas:document.getElementById("canvas"),wasmBinary:wa,getPreloadedPackage:function(){return da.buffer;},onRuntimeInitialized:function(){parent.postMessage("running","*");},print:console.log,printErr:console.warn};})();<\/script><script src="'+jsUrl+'"><\/script><\/body><\/html>'; |
139 | 236 | } |
140 | 237 |
|
141 | | -// Default sketch iframe using pre-built static files |
142 | 238 | function makeDefaultHTML() { |
143 | | - return `<!DOCTYPE html><html><head> |
144 | | -<style>*{margin:0;padding:0;overflow:hidden}body{background:#000}canvas{display:block;width:100%;height:100%}</style> |
145 | | -</head><body><canvas id="canvas"></canvas><script> |
146 | | -window.Module={ |
147 | | - canvas:document.getElementById('canvas'), |
148 | | - onRuntimeInitialized:function(){parent.postMessage('running','*');}, |
149 | | - print:function(t){console.log(t);}, |
150 | | - printErr:function(t){console.warn(t);} |
151 | | -}; |
152 | | -<\/script><script src="/editor/default_sketch.js"><\/script></body></html>`; |
| 239 | + return '<!DOCTYPE html><html><head><style>*{margin:0;padding:0;overflow:hidden}body{background:#000}canvas{display:block;width:100vw;height:100vh}<\/style><\/head><body><canvas id="canvas"><\/canvas><script>window.Module={canvas:document.getElementById("canvas"),onRuntimeInitialized:function(){parent.postMessage("running","*");},print:console.log,printErr:console.warn};<\/script><script src="/editor/default_sketch.js"><\/script><\/body><\/html>'; |
153 | 240 | } |
154 | 241 |
|
155 | 242 | function launchIframe(html) { |
156 | 243 | const area = document.getElementById('preview-area'); |
157 | 244 | const old = area.querySelector('iframe'); |
158 | | - if (old) { try { URL.revokeObjectURL(old.src); } catch(e){} old.remove(); } |
| 245 | + if (old) { try{URL.revokeObjectURL(old.src);}catch(e){} old.remove(); } |
159 | 246 | const ph = document.getElementById('preview-placeholder'); |
160 | 247 | if (ph) ph.style.display = 'none'; |
161 | 248 | const iframe = document.createElement('iframe'); |
162 | 249 | iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); |
163 | | - const blob = new Blob([html], {type:'text/html'}); |
164 | | - iframe.src = URL.createObjectURL(blob); |
| 250 | + iframe.src = URL.createObjectURL(new Blob([html], {type:'text/html'})); |
165 | 251 | area.appendChild(iframe); |
166 | 252 | } |
167 | 253 |
|
168 | | -function stopSketch() { |
169 | | - const area = document.getElementById('preview-area'); |
170 | | - const iframe = area.querySelector('iframe'); |
171 | | - if (iframe) { try { URL.revokeObjectURL(iframe.src); } catch(e){} iframe.remove(); } |
172 | | - const ph = document.getElementById('preview-placeholder'); |
173 | | - if (ph) ph.style.display = ''; |
174 | | - setStatus(''); |
175 | | - editor.focus(); |
176 | | -} |
177 | | - |
178 | 254 | window.addEventListener('message', e => { |
179 | 255 | if (e.data === 'running') setStatus('Running', 'ok'); |
180 | 256 | }); |
181 | 257 |
|
182 | | -// Load pre-built default sketch on page load |
183 | 258 | window.addEventListener('load', () => { |
184 | 259 | launchIframe(makeDefaultHTML()); |
185 | 260 | setStatus('Running', 'ok'); |
|
217 | 292 | return; |
218 | 293 | } |
219 | 294 |
|
| 295 | + setStatus('Loading...', 'compiling'); |
220 | 296 | btn.disabled = false; |
| 297 | + stopSketch(); |
221 | 298 | const blobUrl = URL.createObjectURL(new Blob([data.js], {type:'application/javascript'})); |
222 | 299 | launchIframe(makeSketchHTML(blobUrl, data.wasm, data.data || '')); |
223 | 300 | } |
224 | 301 |
|
| 302 | +const _origPreventDefault = Event.prototype.preventDefault; |
| 303 | +Event.prototype.preventDefault = function() { |
| 304 | + if (this.type === 'keydown' || this.type === 'keyup' || this.type === 'keypress') { |
| 305 | + const ta = document.querySelector('.CodeMirror textarea'); |
| 306 | + if (ta && document.activeElement === ta) return; |
| 307 | + } |
| 308 | + return _origPreventDefault.call(this); |
| 309 | +}; |
| 310 | + |
225 | 311 | document.addEventListener('keydown', e => { |
226 | 312 | if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); runSketch(); } |
227 | 313 | }); |
|
0 commit comments