Skip to content

Commit bdc6f2d

Browse files
committed
Integrate Monaco editor for better code editing
Replaces plain textarea with Monaco editor from CDN, providing: - Syntax highlighting for C code - Better keyboard shortcuts (Ctrl+Enter to compile) - Foundation for LSP diagnostics integration - Professional editor UX Monaco is loaded from CDN with proper browser caching.
1 parent d80d2e3 commit bdc6f2d

File tree

1 file changed

+73
-51
lines changed

1 file changed

+73
-51
lines changed

nullsafe-playground/index.html

Lines changed: 73 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Null-Safe Clang Playground</title>
7+
8+
<!-- Monaco Editor from CDN (cached by browser) -->
9+
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/editor/editor.main.min.css">
10+
711
<style>
812
* {
913
margin: 0;
@@ -323,31 +327,18 @@
323327
border-radius: 2px;
324328
}
325329

330+
#editor-wrapper {
331+
flex: 1;
332+
position: relative;
333+
overflow: hidden;
334+
}
335+
326336
#editor {
327337
position: absolute;
328338
top: 0;
329339
left: 0;
330340
right: 0;
331341
bottom: 0;
332-
padding: 20px;
333-
font-family: 'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace;
334-
font-size: 14px;
335-
line-height: 1.5;
336-
background: var(--bg-primary);
337-
color: var(--text-primary);
338-
border: none;
339-
outline: none;
340-
resize: none;
341-
tab-size: 4;
342-
overflow-y: auto;
343-
caret-color: white;
344-
z-index: 2;
345-
}
346-
347-
#editor-wrapper {
348-
flex: 1;
349-
position: relative;
350-
overflow: hidden;
351342
}
352343

353344
#output {
@@ -555,7 +546,7 @@ <h1>Null-Safe Clang Playground</h1>
555546
</div>
556547
</div>
557548
<div id="editor-wrapper">
558-
<textarea id="editor" spellcheck="false" placeholder="Write your C code here..."></textarea>
549+
<div id="editor"></div>
559550
</div>
560551
</div>
561552

@@ -581,8 +572,15 @@ <h1>Null-Safe Clang Playground</h1>
581572

582573
<div class="toast" id="toast"></div>
583574

575+
<!-- Load Monaco Editor from CDN -->
576+
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js"></script>
584577
<script>
585-
const editor = document.getElementById('editor');
578+
// Configure Monaco loader
579+
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' }});
580+
</script>
581+
582+
<script>
583+
const editorElement = document.getElementById('editor');
586584
const outputNullsafe = document.getElementById('output-nullsafe');
587585
const outputMainline = document.getElementById('output-mainline');
588586
const compileBtn = document.getElementById('compileBtn');
@@ -594,6 +592,8 @@ <h1>Null-Safe Clang Playground</h1>
594592
const divider = document.getElementById('divider');
595593
const outputDivider = document.getElementById('outputDivider');
596594

595+
let editor = null; // Monaco editor instance
596+
597597
const examples = {
598598
'null-check': `void process(int* data) {
599599
if (data) {
@@ -631,13 +631,55 @@ <h1>Null-Safe Clang Playground</h1>
631631
};
632632

633633
const defaultCode = examples['null-check'];
634-
editor.value = defaultCode;
634+
635+
// Monaco editor will be initialized async
636+
function initMonaco() {
637+
return new Promise((resolve) => {
638+
require(['vs/editor/editor.main'], function() {
639+
editor = monaco.editor.create(editorElement, {
640+
value: defaultCode,
641+
language: 'c',
642+
theme: 'vs-dark',
643+
automaticLayout: true,
644+
minimap: { enabled: false },
645+
fontSize: 14,
646+
lineNumbers: 'on',
647+
scrollBeyondLastLine: false,
648+
wordWrap: 'off',
649+
tabSize: 4
650+
});
651+
652+
// Add Ctrl+Enter shortcut
653+
editor.addAction({
654+
id: 'compile-code',
655+
label: 'Compile Code',
656+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
657+
run: function() {
658+
compile();
659+
}
660+
});
661+
662+
resolve();
663+
});
664+
});
665+
}
666+
667+
// Helper to get/set editor content
668+
function getEditorValue() {
669+
return editor ? editor.getValue() : '';
670+
}
671+
672+
function setEditorValue(value) {
673+
if (editor) {
674+
editor.setValue(value);
675+
}
676+
}
635677

636678
let clangVersion = '';
637679
let isCompiling = false;
638680
let isDragging = false;
639-
let scriptUrl = null; // URL for worker to load clang.js
640-
let wasmBinary = null; // Pre-loaded WASM binary to share with workers
681+
let scriptUrl = null;
682+
let wasmBinary = null;
641683

642684
// Toast notification
643685
function showToast(message, duration = 2000) {
@@ -686,16 +728,15 @@ <h1>Null-Safe Clang Playground</h1>
686728
examplesSelect.addEventListener('change', (e) => {
687729
const example = e.target.value;
688730
if (example && examples[example]) {
689-
editor.value = examples[example];
731+
setEditorValue(examples[example]);
690732
showToast('Example loaded!');
691-
// Don't reset - keep the selection showing
692733
}
693734
});
694735

695736
// Share button - encode code in URL
696737
shareBtn.addEventListener('click', async () => {
697738
try {
698-
const code = editor.value;
739+
const code = getEditorValue();
699740
const encoded = btoa(unescape(encodeURIComponent(code)));
700741
const url = new URL(window.location.href.split('?')[0]);
701742
url.searchParams.set('code', encoded);
@@ -715,7 +756,7 @@ <h1>Null-Safe Clang Playground</h1>
715756
if (encodedCode) {
716757
try {
717758
const code = decodeURIComponent(escape(atob(encodedCode)));
718-
editor.value = code;
759+
setEditorValue(code);
719760
showToast('Code loaded from URL!');
720761
} catch (err) {
721762
console.error('Failed to decode URL code:', err);
@@ -792,7 +833,7 @@ <h1>Null-Safe Clang Playground</h1>
792833
const startTime = performance.now();
793834

794835
try {
795-
const code = editor.value;
836+
const code = getEditorValue();
796837

797838
// Compile both versions in parallel
798839
const [nullsafeResult, mainlineResult] = await Promise.all([
@@ -911,30 +952,11 @@ <h1>Null-Safe Clang Playground</h1>
911952
compileBtn.addEventListener('click', compile);
912953

913954
// Auto-compile on load
914-
window.addEventListener('load', () => {
915-
loadCodeFromURL(); // Load code from URL if present
955+
window.addEventListener('load', async () => {
956+
await initMonaco(); // Initialize Monaco editor first
957+
loadCodeFromURL(); // Load code from URL if present
916958
initCompiler();
917959
});
918-
919-
// Keyboard shortcut: Cmd/Ctrl + Enter to compile
920-
editor.addEventListener('keydown', (e) => {
921-
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
922-
e.preventDefault();
923-
compile();
924-
}
925-
});
926-
927-
// Tab key handling
928-
editor.addEventListener('keydown', (e) => {
929-
if (e.key === 'Tab') {
930-
e.preventDefault();
931-
const start = editor.selectionStart;
932-
const end = editor.selectionEnd;
933-
const value = editor.value;
934-
editor.value = value.substring(0, start) + ' ' + value.substring(end);
935-
editor.selectionStart = editor.selectionEnd = start + 4;
936-
}
937-
});
938960
</script>
939961
</body>
940962
</html>

0 commit comments

Comments
 (0)