Skip to content

Commit cd3a0c3

Browse files
committed
Editor: iframe isolation for clean re-compile, pre-built default sketch on load
1 parent ae4f1e7 commit cd3a0c3

1 file changed

Lines changed: 104 additions & 193 deletions

File tree

editor/index.html

Lines changed: 104 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -3,136 +3,70 @@
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-
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-
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; }
1915
.editor-layout { display: flex; flex: 1; overflow: hidden; }
20-
2116
.editor-pane { display: flex; flex-direction: column; width: 50%; border-right: 1px solid #e0e0e0; }
2217
.preview-pane { display: flex; flex-direction: column; width: 50%; }
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-
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; }
3520
#status { font-size: 12px; color: #888; }
3621
#status.compiling { color: #c07000; }
3722
#status.error { color: #c00; }
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; }
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; }
6530
.CodeMirror-scroll { height: 100% !important; }
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; }
76-
77-
#preview-placeholder { color: #555; font-size: 13px; text-align: center; line-height: 2.2; }
78-
#preview-placeholder kbd {
79-
background: #222;
80-
border: 1px solid #444;
81-
padding: 2px 6px;
82-
border-radius: 3px;
83-
font-size: 12px;
84-
color: #bbb;
85-
font-family: "SF Mono", "Fira Code", monospace;
86-
}
87-
88-
#error-panel {
89-
display: none;
90-
background: #fff5f5;
91-
border-top: 1px solid #fcc;
92-
padding: 0.6rem 1rem;
93-
font-family: "SF Mono", "Fira Code", monospace;
94-
font-size: 11.5px;
95-
color: #c00;
96-
max-height: 130px;
97-
overflow-y: auto;
98-
white-space: pre;
99-
flex-shrink: 0;
100-
}
31+
#preview-area { flex: 1; background: #000; overflow: hidden; position: relative; }
32+
#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; }
10136
</style>
10237
</head>
10338
<body>
104-
<nav id="site-nav"></nav>
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>
10544

10645
<div class="editor-layout">
10746
<div class="editor-pane">
10847
<div class="pane-toolbar">
109-
<span class="pane-label">sketch.pde</span>
48+
<span class="pane-label">sketch.cpp</span>
11049
<span id="status"></span>
111-
<button class="btn-stop" onclick="stopSketch()">Stop</button>
11250
<button class="btn-run" id="btn-run" onclick="runSketch()">&#9654; Run</button>
11351
</div>
114-
<textarea id="code"></textarea>
52+
<textarea id="code-editor"></textarea>
11553
<div id="error-panel"></div>
11654
</div>
117-
11855
<div class="preview-pane">
11956
<div class="pane-toolbar">
120-
<span class="pane-label">preview</span>
57+
<span class="pane-label">Output</span>
58+
<button class="btn-stop" onclick="stopSketch()">&#9632; Stop</button>
12159
</div>
12260
<div id="preview-area">
123-
<div id="preview-placeholder">
124-
Press <kbd>Ctrl+Enter</kbd> to run your sketch
125-
</div>
61+
<div id="preview-placeholder">Press <kbd>Run</kbd> or <kbd>Ctrl+Enter</kbd></div>
12662
</div>
12763
</div>
12864
</div>
12965

13066
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
13167
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/clike/clike.min.js"></script>
13268
<script src="../assets/cppmode-keywords.js"></script>
133-
<script src="../assets/cm-cppmode.js"></script>
13469
<script src="../assets/cppmode.js"></script>
135-
<script src="../assets/nav.js"></script>
13670
<script>
13771
const API = 'https://bench-volumes-pontiac-riders.trycloudflare.com';
13872

@@ -153,82 +87,104 @@
15387
angle += 0.02f;
15488
}`;
15589

156-
const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
157-
mode: 'cppmode',
90+
const editor = CodeMirror.fromTextArea(document.getElementById('code-editor'), {
91+
mode: 'text/x-cppmode',
15892
theme: 'cppmode',
15993
lineNumbers: true,
16094
indentUnit: 2,
16195
tabSize: 2,
16296
indentWithTabs: false,
16397
autofocus: true,
164-
matchBrackets: true,
165-
autoCloseBrackets: true,
166-
lineWrapping: false,
167-
extraKeys: {
168-
'Ctrl-Enter': runSketch,
169-
'Cmd-Enter': runSketch,
170-
'Tab': cm => cm.execCommand('indentMore'),
171-
'Shift-Tab': cm => cm.execCommand('indentLess'),
172-
'Backspace': cm => cm.execCommand('delCharBefore'),
173-
},
17498
});
17599
editor.setValue(DEFAULT);
176100
editor.setSize('100%', '100%');
177101

178-
// Load pre-built default sketch immediately -- no compile needed
179-
async function loadDefaultSketch() {
180-
const area = document.getElementById('preview-area');
181-
const placeholder = document.getElementById('preview-placeholder');
182-
if (placeholder) placeholder.style.display = 'none';
183-
184-
const canvas = document.createElement('canvas');
185-
canvas.id = 'canvas';
186-
canvas.setAttribute('tabindex', '-1');
187-
area.appendChild(canvas);
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+
};
188112

189-
const [wasmResp, dataResp] = await Promise.all([
190-
fetch('default_sketch.wasm'),
191-
fetch('default_sketch.data'),
192-
]);
193-
const wasmBinary = new Uint8Array(await wasmResp.arrayBuffer());
194-
const dataBytes = new Uint8Array(await dataResp.arrayBuffer());
113+
function setStatus(msg, cls) {
114+
const el = document.getElementById('status');
115+
el.textContent = msg;
116+
el.className = cls || '';
117+
}
195118

196-
window.Module = {
197-
canvas: canvas,
198-
wasmBinary: wasmBinary,
199-
onRuntimeInitialized: function() { setStatus('Running', 'ok'); },
200-
getPreloadedPackage: function(name, size) { return dataBytes.buffer; },
201-
print: t => console.log('[sketch]', t),
202-
printErr: t => console.warn('[sketch]', t),
119+
// Build iframe HTML for a compiled sketch
120+
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);}
203136
};
204-
205-
const script = document.createElement('script');
206-
script.src = 'default_sketch.js';
207-
document.head.appendChild(script);
137+
})();
138+
<\/script><script src="${jsUrl}"><\/script></body></html>`;
208139
}
209140

210-
window.addEventListener('load', loadDefaultSketch);
211-
212-
213-
141+
// Default sketch iframe using pre-built static files
142+
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>`;
153+
}
214154

215-
function setStatus(msg, cls) {
216-
const el = document.getElementById('status');
217-
el.textContent = msg;
218-
el.className = cls || '';
155+
function launchIframe(html) {
156+
const area = document.getElementById('preview-area');
157+
const old = area.querySelector('iframe');
158+
if (old) { try { URL.revokeObjectURL(old.src); } catch(e){} old.remove(); }
159+
const ph = document.getElementById('preview-placeholder');
160+
if (ph) ph.style.display = 'none';
161+
const iframe = document.createElement('iframe');
162+
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
163+
const blob = new Blob([html], {type:'text/html'});
164+
iframe.src = URL.createObjectURL(blob);
165+
area.appendChild(iframe);
219166
}
220167

221168
function stopSketch() {
222169
const area = document.getElementById('preview-area');
223-
const canvas = area.querySelector('canvas');
224-
if (canvas) canvas.remove();
225-
const placeholder = document.getElementById('preview-placeholder');
226-
if (placeholder) placeholder.style.display = '';
227-
window.Module = undefined;
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 = '';
228174
setStatus('');
229175
editor.focus();
230176
}
231177

178+
window.addEventListener('message', e => {
179+
if (e.data === 'running') setStatus('Running', 'ok');
180+
});
181+
182+
// Load pre-built default sketch on page load
183+
window.addEventListener('load', () => {
184+
launchIframe(makeDefaultHTML());
185+
setStatus('Running', 'ok');
186+
});
187+
232188
async function runSketch() {
233189
const btn = document.getElementById('btn-run');
234190
const errorPanel = document.getElementById('error-panel');
@@ -261,56 +217,11 @@
261217
return;
262218
}
263219

264-
setStatus('Loading...', 'compiling');
265220
btn.disabled = false;
266-
stopSketch();
267-
268-
const area = document.getElementById('preview-area');
269-
const placeholder = document.getElementById('preview-placeholder');
270-
if (placeholder) placeholder.style.display = 'none';
271-
272-
const canvas = document.createElement('canvas');
273-
canvas.id = 'canvas';
274-
canvas.setAttribute('tabindex', '-1');
275-
area.appendChild(canvas);
276-
277-
const wasmBinary = Uint8Array.from(atob(data.wasm), c => c.charCodeAt(0));
278-
const dataBytes = data.data ? Uint8Array.from(atob(data.data), c => c.charCodeAt(0)) : null;
279-
280-
window.Module = {
281-
canvas: canvas,
282-
wasmBinary: wasmBinary,
283-
onRuntimeInitialized: function() { setStatus('Running', 'ok'); },
284-
print: t => console.log('[sketch]', t),
285-
printErr: t => console.warn('[sketch]', t),
286-
};
287-
288-
if (dataBytes) {
289-
window.Module.getPreloadedPackage = function(name, size) { return dataBytes.buffer; };
290-
}
291-
292-
const blob = new Blob([data.js], { type: 'application/javascript' });
293-
const blobUrl = URL.createObjectURL(blob);
294-
const script = document.createElement('script');
295-
script.src = blobUrl;
296-
script.onload = () => URL.revokeObjectURL(blobUrl);
297-
script.onerror = () => {
298-
setStatus('Load error', 'error');
299-
errorPanel.textContent = 'Failed to load compiled sketch.';
300-
errorPanel.style.display = 'block';
301-
};
302-
document.head.appendChild(script);
221+
const blobUrl = URL.createObjectURL(new Blob([data.js], {type:'application/javascript'}));
222+
launchIframe(makeSketchHTML(blobUrl, data.wasm, data.data || ''));
303223
}
304224

305-
const _origPreventDefault = Event.prototype.preventDefault;
306-
Event.prototype.preventDefault = function() {
307-
if (this.type === 'keydown' || this.type === 'keyup' || this.type === 'keypress') {
308-
const ta = document.querySelector('.CodeMirror textarea');
309-
if (ta && document.activeElement === ta) return;
310-
}
311-
return _origPreventDefault.call(this);
312-
};
313-
314225
document.addEventListener('keydown', e => {
315226
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); runSketch(); }
316227
});

0 commit comments

Comments
 (0)