Fix syntax coloring, modernize toolbar UI, and clean up CSS

- Fix Monarch tokenizer loading: await initLinter() before editor creation
  so loadScript() doesn't clobber window.define during lazy tokenizer init
- Fix JSX/TSX coloring: use file URIs with proper extensions (.jsx/.tsx)
  so Monaco enables JSX tokenization via the TypeScript language service
- Modernize toolbar: move settings to gear popover, replace text buttons
  with SVG icons, consolidate toggle checkboxes into compact group
- Clean up CSS: remove duplicate toggle classes, dead selectors, orphaned rules
This commit is contained in:
root
2026-02-27 15:19:10 -06:00
parent 0d84c56008
commit 26e232fd41
4 changed files with 222 additions and 140 deletions

View File

@@ -290,8 +290,10 @@ function applyLayout(layout) {
}
async function init() {
// Load Emmet before editors so completion providers are registered
// Load ALL CDN scripts before editor creation so window.define
// (Monaco's RequireJS) is never clobbered during Monarch tokenizer init
await initEmmet();
await initLinter();
// Register custom Monaco themes before creating editors
registerCustomThemes();
@@ -312,7 +314,6 @@ async function init() {
initPerformance();
initResizer();
initKeybindings();
initLinter();
// Auto-run checkbox
const autoRunCb = $('#auto-run-checkbox');
@@ -571,6 +572,18 @@ async function init() {
$('#shortcuts-modal-close').addEventListener('click', () => scModal.classList.add('hidden'));
scModal.addEventListener('click', (e) => { if (e.target === scModal) scModal.classList.add('hidden'); });
// Settings popover toggle
const settingsPopover = $('#settings-popover');
$('#btn-settings').addEventListener('click', (e) => {
e.stopPropagation();
settingsPopover.classList.toggle('hidden');
});
document.addEventListener('click', (e) => {
if (!settingsPopover.classList.contains('hidden') && !e.target.closest('.settings-popover-wrap')) {
settingsPopover.classList.add('hidden');
}
});
// Load fiddle from URL if present
loadFromUrl();

View File

@@ -37,41 +37,41 @@ const editorOpts = {
export const MODE_TABS = {
'html-css-js': [
{ id: 'html', label: 'HTML', lang: 'html' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'js', label: 'JavaScript', lang: 'javascript' },
{ id: 'html', label: 'HTML', lang: 'html', ext: 'html' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
{ id: 'js', label: 'JavaScript', lang: 'javascript', ext: 'js' },
],
'typescript': [
{ id: 'html', label: 'HTML', lang: 'html' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'js', label: 'TypeScript', lang: 'typescript' },
{ id: 'html', label: 'HTML', lang: 'html', ext: 'html' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
{ id: 'js', label: 'TypeScript', lang: 'typescript', ext: 'ts' },
],
'react': [
{ id: 'js', label: 'JSX', lang: 'javascript' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'html', label: 'HTML', lang: 'html' },
{ id: 'js', label: 'JSX', lang: 'javascript', ext: 'jsx' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
{ id: 'html', label: 'HTML', lang: 'html', ext: 'html' },
],
'react-ts': [
{ id: 'js', label: 'TSX', lang: 'typescript' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'html', label: 'HTML', lang: 'html' },
{ id: 'js', label: 'TSX', lang: 'typescript', ext: 'tsx' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
{ id: 'html', label: 'HTML', lang: 'html', ext: 'html' },
],
'vue': [
{ id: 'js', label: 'Vue SFC', lang: 'html' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'js', label: 'Vue SFC', lang: 'html', ext: 'vue' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
],
'svelte': [
{ id: 'js', label: 'Svelte', lang: 'html' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'js', label: 'Svelte', lang: 'html', ext: 'svelte' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
],
'markdown': [
{ id: 'js', label: 'Markdown', lang: 'markdown' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'js', label: 'Markdown', lang: 'markdown', ext: 'md' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
],
'wasm': [
{ id: 'html', label: 'HTML', lang: 'html' },
{ id: 'css', label: 'CSS', lang: 'css' },
{ id: 'js', label: 'JavaScript', lang: 'javascript' },
{ id: 'html', label: 'HTML', lang: 'html', ext: 'html' },
{ id: 'css', label: 'CSS', lang: 'css', ext: 'css' },
{ id: 'js', label: 'JavaScript', lang: 'javascript', ext: 'js' },
],
};
@@ -179,16 +179,20 @@ function configureJsxSupport(mode) {
}
}
let modelCounter = 0;
function createEditor(tabDef) {
const container = document.createElement('div');
container.className = 'editor-container';
container.id = `editor-${tabDef.id}`;
editorArea().appendChild(container);
const uri = monaco.Uri.parse(`file:///fiddle-${++modelCounter}.${tabDef.ext}`);
const model = monaco.editor.createModel('', tabDef.lang, uri);
const editor = monaco.editor.create(container, {
...editorOpts,
language: tabDef.lang,
value: '',
model,
});
if (onChangeCallback) {
@@ -289,11 +293,13 @@ export function initEditors(mode = 'html-css-js') {
export function switchMode(mode) {
if (mode === currentMode) return;
// Dispose existing editors
// Dispose existing editors and their models
const oldTabs = MODE_TABS[currentMode];
oldTabs.forEach((tab) => {
if (editors[tab.id]) {
const model = editors[tab.id].getModel();
editors[tab.id].dispose();
if (model) model.dispose();
}
const container = editors[`_container_${tab.id}`];
if (container && container.parentNode) {