Skip to content

Commit 2db69d2

Browse files
committed
Editor: iframe isolation, pre-built default on load, backspace fix, restore styling
1 parent cd3a0c3 commit 2db69d2

1 file changed

Lines changed: 169 additions & 83 deletions

File tree

editor/index.html

Lines changed: 169 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,137 @@
33
<head>
44
<meta charset="UTF-8">
55
<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>
77
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
88
<link rel="stylesheet" href="../assets/cppmode-theme.css">
99
<style>
1010
* { margin: 0; padding: 0; box-sizing: border-box; }
1111
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+
1519
.editor-layout { display: flex; flex: 1; overflow: hidden; }
20+
1621
.editor-pane { display: flex; flex-direction: column; width: 50%; border-right: 1px solid #e0e0e0; }
1722
.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+
2035
#status { font-size: 12px; color: #888; }
2136
#status.compiling { color: #c07000; }
2237
#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; }
3065
.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; }
3276
#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+
}
36102
</style>
37103
</head>
38104
<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>
44106

45107
<div class="editor-layout">
46108
<div class="editor-pane">
47109
<div class="pane-toolbar">
48-
<span class="pane-label">sketch.cpp</span>
110+
<span class="pane-label">sketch.pde</span>
49111
<span id="status"></span>
112+
<button class="btn-stop" onclick="stopSketch()">Stop</button>
50113
<button class="btn-run" id="btn-run" onclick="runSketch()">&#9654; Run</button>
51114
</div>
52-
<textarea id="code-editor"></textarea>
115+
<textarea id="code"></textarea>
53116
<div id="error-panel"></div>
54117
</div>
118+
55119
<div class="preview-pane">
56120
<div class="pane-toolbar">
57-
<span class="pane-label">Output</span>
58-
<button class="btn-stop" onclick="stopSketch()">&#9632; Stop</button>
121+
<span class="pane-label">preview</span>
59122
</div>
60123
<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>
62127
</div>
63128
</div>
64129
</div>
65130

66131
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
67132
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/clike/clike.min.js"></script>
68133
<script src="../assets/cppmode-keywords.js"></script>
134+
<script src="../assets/cm-cppmode.js"></script>
69135
<script src="../assets/cppmode.js"></script>
136+
<script src="../assets/nav.js"></script>
70137
<script>
71138
const API = 'https://bench-volumes-pontiac-riders.trycloudflare.com';
72139

@@ -87,99 +154,107 @@
87154
angle += 0.02f;
88155
}`;
89156

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',
92159
theme: 'cppmode',
93160
lineNumbers: true,
94161
indentUnit: 2,
95162
tabSize: 2,
96163
indentWithTabs: false,
97164
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+
},
98175
});
99176
editor.setValue(DEFAULT);
100177
editor.setSize('100%', '100%');
101178

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+
112215

113216
function setStatus(msg, cls) {
114217
const el = document.getElementById('status');
115218
el.textContent = msg;
116219
el.className = cls || '';
117220
}
118221

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
120233
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>';
139236
}
140237

141-
// Default sketch iframe using pre-built static files
142238
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>';
153240
}
154241

155242
function launchIframe(html) {
156243
const area = document.getElementById('preview-area');
157244
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(); }
159246
const ph = document.getElementById('preview-placeholder');
160247
if (ph) ph.style.display = 'none';
161248
const iframe = document.createElement('iframe');
162249
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'}));
165251
area.appendChild(iframe);
166252
}
167253

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-
178254
window.addEventListener('message', e => {
179255
if (e.data === 'running') setStatus('Running', 'ok');
180256
});
181257

182-
// Load pre-built default sketch on page load
183258
window.addEventListener('load', () => {
184259
launchIframe(makeDefaultHTML());
185260
setStatus('Running', 'ok');
@@ -217,11 +292,22 @@
217292
return;
218293
}
219294

295+
setStatus('Loading...', 'compiling');
220296
btn.disabled = false;
297+
stopSketch();
221298
const blobUrl = URL.createObjectURL(new Blob([data.js], {type:'application/javascript'}));
222299
launchIframe(makeSketchHTML(blobUrl, data.wasm, data.data || ''));
223300
}
224301

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+
225311
document.addEventListener('keydown', e => {
226312
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); runSketch(); }
227313
});

0 commit comments

Comments
 (0)