diff --git a/ts/index.html b/ts/index.html index 00d4559..1004dd1 100644 --- a/ts/index.html +++ b/ts/index.html @@ -406,7 +406,7 @@ flex-shrink: 0 } - /* red × — section & element */ + /* red × - section & element */ .x-btn { background: none; border: none; @@ -681,11 +681,11 @@
- + -
- + -
@@ -728,7 +728,7 @@ let currentFilename = 'untitled'; let isDirty = false; - // ─── Element type definitions ──────────────────────────────────────────────── + // --- Element type definitions ------------------------------------------------ const EL_TYPES = ['Image2D', 'Animation', 'ClippedText', 'HScrollText', 'CurrentTime']; const EL_FIELDS = { Image2D: [ @@ -746,7 +746,7 @@ { k: 'width', l: 'Width', t: 'number', d: W }, { k: 'height', l: 'Height', t: 'number', d: 16 }, ], HScrollText: [ - { k: 'text', l: 'Text', t: 'text', d: 'Scrolling text — ', full: true }, + { k: 'text', l: 'Text', t: 'text', d: 'Scrolling text - ', full: true }, { k: 'xOffset', l: 'X offset', t: 'number', d: 0 }, { k: 'yOffset', l: 'Y offset', t: 'number', d: 32 }, { k: 'width', l: 'Width', t: 'number', d: W }, { k: 'height', l: 'Height', t: 'number', d: 16 }, { k: 'scrollSpeed', l: 'Scroll speed', t: 'number', d: 50 }, @@ -763,7 +763,7 @@ CurrentTime: [{ k: 'clock12h', l: '12h mode' }, { k: 'showHours', l: 'Show hours' }, { k: 'showSeconds', l: 'Show seconds' }], }; - // ─── Confirm dialog ─────────────────────────────────────────────────────────── + // --- Confirm dialog ----------------------------------------------------------- function confirm(msg, buttons) { // buttons: [{label,primary,action}] return new Promise(resolve => { @@ -782,7 +782,7 @@ }); } - // ─── Dirty tracking ─────────────────────────────────────────────────────────── + // --- Dirty tracking ----------------------------------------------------------- function markDirty() { isDirty = true; document.getElementById('filename').classList.add('dirty'); @@ -800,7 +800,7 @@ return action; } - // ─── Helpers ───────────────────────────────────────────────────────────────── + // --- Helpers ----------------------------------------------------------------- function newSec() { secCounter++; return { @@ -819,7 +819,7 @@ function getSec(id) { return sections.find(s => s.id === id) } function getEl(secId, elId) { const s = getSec(secId); return s && s.elements.find(e => e.id === elId) } - // ─── Mutations ──────────────────────────────────────────────────────────────── + // --- Mutations ---------------------------------------------------------------- function addSection() { const s = newSec(); sections.push(s); activeSec = s.id; activeEl = null; @@ -871,7 +871,7 @@ el.flags[key] = val; markDirty(); triggerPreview(); } - // ─── Load demo into editor state ───────────────────────────────────────────── + // --- Load demo into editor state --------------------------------------------- const DEMO_DEFS = [ { label: 'Checkerboard', build() { @@ -900,7 +900,7 @@ label: 'Scrolltext', build() { const s = newSec(); s.name = 'Scrolltext'; s.flags = { drawFront: true, clearBuffer: true }; const el = newEl('HScrollText'); - el.fields.text = 'MONO DISPLAY — scrolling ticker — 🚀 '; + el.fields.text = 'MONO DISPLAY - scrolling ticker - 🚀 '; el.fields.yOffset = 32; el.fields.scrollSpeed = 50; el.flags.endless = true; el.flags.invertDirection = false; s.elements.push(el); return [s]; @@ -925,7 +925,7 @@ }, ]; - // ─── Preview via MonoDisplayFile (same as original demos) ──────────────────── + // --- Preview via MonoDisplayFile (same as original demos) -------------------- const DEMO_PREVIEWS = { Checkerboard() { const { MonoDisplayFile, ElementType } = window.MonoDisplay; @@ -963,7 +963,7 @@ elements_always: { flags: { drawFront: true, clearBuffer: true }, elements: [{ - type: ElementType.HScrollText, text: 'MONO DISPLAY — scrolling ticker — 🚀 ', + type: ElementType.HScrollText, text: 'MONO DISPLAY - scrolling ticker - 🚀 ', xOffset: 0, yOffset: 32, width: W, height: 16, scrollSpeed: 50, flags: { endless: true, invertDirection: false } }] } @@ -1011,7 +1011,7 @@ }); } - // ─── Render ─────────────────────────────────────────────────────────────────── + // --- Render ------------------------------------------------------------------- function render() { updateMeta(); const wrap = document.getElementById('sections-wrap'); @@ -1085,20 +1085,20 @@ } function elSummary(el) { - if (el.fields.text) return esc(el.fields.text.slice(0, 24) + (el.fields.text.length > 24 ? '…' : '')); + if (el.fields.text) return esc(el.fields.text.slice(0, 24) + (el.fields.text.length > 24 ? '...' : '')); return `${el.fields.xOffset ?? 0}, ${el.fields.yOffset ?? 0}`; } function esc(s) { return String(s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') } function updateMeta() { const s = activeSec ? getSec(activeSec) : null; - document.getElementById('meta-name').textContent = s ? s.name : '—'; - document.getElementById('meta-count').textContent = s ? s.elements.length + ' element(s)' : '—'; + document.getElementById('meta-name').textContent = s ? s.name : '-'; + document.getElementById('meta-count').textContent = s ? s.elements.length + ' element(s)' : '-'; document.getElementById('flag-drawFront').checked = s ? !!s.flags.drawFront : false; document.getElementById('flag-clearBuffer').checked = s ? !!s.flags.clearBuffer : false; } - // ─── Preview ────────────────────────────────────────────────────────────────── + // --- Preview ------------------------------------------------------------------ let previewTimer = null; function triggerPreview() { clearTimeout(previewTimer); @@ -1120,17 +1120,69 @@ )); } - // ─── Load / Export ──────────────────────────────────────────────────────────── + // --- Load / Export ------------------------------------------------------------ + function parsedToSections(parsedSecs) { + const TYPE_MAP = { 1: 'Image2D', 2: 'Animation', 16: 'ClippedText', 17: 'HScrollText', 32: 'CurrentTime' }; + return parsedSecs + .filter(s => s.sectionType === 1 || s.sectionType === 2) + .map(s => { + secCounter++; + const elements = (s.elements || []).map(el => { + const typeName = TYPE_MAP[el.type]; + if (!typeName) return null; + elCounter++; + let fields = {}, flags = {}; + switch (typeName) { + case 'Image2D': + fields = { xOffset: el.xOffset, yOffset: el.yOffset, width: el.image?.width ?? W, height: el.image?.height ?? H }; + break; + case 'Animation': + fields = { xOffset: el.xOffset, yOffset: el.yOffset, width: el.width, height: el.height, updateInterval: el.updateInterval }; + break; + case 'ClippedText': + fields = { text: el.text, xOffset: el.xOffset, yOffset: el.yOffset, width: el.width, height: el.height }; + break; + case 'HScrollText': + fields = { text: el.text, xOffset: el.xOffset, yOffset: el.yOffset, width: el.width, height: el.height, scrollSpeed: el.scrollSpeed }; + flags = { endless: !!el.flags?.endless, invertDirection: !!el.flags?.invertDirection }; + break; + case 'CurrentTime': + fields = { xOffset: el.xOffset, yOffset: el.yOffset, width: el.width, height: el.height, utcOffsetMinutes: el.utcOffsetMinutes }; + flags = { clock12h: !!el.flags?.clock12h, showHours: !!el.flags?.showHours, showSeconds: !!el.flags?.showSeconds }; + break; + } + return { id: 'e' + elCounter, type: typeName, fields, flags }; + }).filter(Boolean); + return { + id: 's' + secCounter, + name: 'Section ' + secCounter, + open: true, + flags: { drawFront: !!s.flags?.drawFront, clearBuffer: !!s.flags?.clearBuffer }, + elements + }; + }); + } + function loadBin(input) { const file = input.files[0]; if (!file) return; currentFilename = file.name; document.getElementById('filename').textContent = file.name; const reader = new FileReader(); reader.onload = e => { - const buf = new Uint8Array(e.target.result); + const arrayBuf = e.target.result; + const buf = new Uint8Array(arrayBuf); if (!window._mdDriver && window.MonoDisplay) window._mdDriver = new window.MonoDisplay.MonoDisplayDriver('canvas_root', { onColor: '#EC0', offColor: '#000', fps: 25 }); if (window._mdDriver) window._mdDriver.load(() => Promise.resolve(buf)); + try { + const parsed = new window.MonoDisplay.MonoDisplayParser().parse(arrayBuf); + sections = parsedToSections(parsed.sections); + activeSec = sections.length ? sections[0].id : null; + activeEl = null; + render(); + } catch (err) { + console.warn('Could not parse sections from bin:', err); + } }; reader.readAsArrayBuffer(file); input.value = ''; markClean(); @@ -1149,7 +1201,7 @@ a.click(); markClean(); } - // ─── Theme ──────────────────────────────────────────────────────────────────── + // --- Theme -------------------------------------------------------------------- var _THEMES = ['dark', 'light', 'auto']; function _getTheme() { return document.documentElement.getAttribute('data-theme') || 'auto'; } function _applyTheme(t) { @@ -1172,11 +1224,11 @@ var t = _getTheme(), dr = _detectDR(); var lbl = { dark: '☾ dark', light: '☀ light', auto: '⊙ auto' }; btn.textContent = (dr ? 'DR · ' : '') + (lbl[t] || lbl.auto); - btn.title = dr ? 'DarkReader active — controlling theme' : ('Theme: ' + t + ' (click to cycle)'); + btn.title = dr ? 'DarkReader active - controlling theme' : ('Theme: ' + t + ' (click to cycle)'); } window.addEventListener('DOMContentLoaded', _updateThemeBtn); - // ─── Init ───────────────────────────────────────────────────────────────────── + // --- Init --------------------------------------------------------------------- render(); if (window.MonoDisplay) { initDemos(); } else { const iv = setInterval(() => { if (window.MonoDisplay) { clearInterval(iv); initDemos(); } }, 100); }