// browser.ts — IIFE-style browser entry point
// Built by:  bun build src/browser.ts --target browser --outfile public/mono-display.js
// Attaches everything to window.MonoDisplay so inline <script> tags can use it
// without ES module syntax.

import * as MonoDisplay from "./index";
import { MonoDisplayFile } from "./file";
import { cycleTheme } from "./themes";
import { ElementType } from "./types";

// Expose on globalThis (= window in browser, globalThis elsewhere)
(globalThis as typeof globalThis & { MonoDisplay: typeof MonoDisplay }).MonoDisplay = MonoDisplay;


const W = 120, H = 60;
let file: MonoDisplayFile = new MonoDisplayFile([]);
let activeSecIndex:number|null = null, activeElIndex:number|null = null;
let currentFilename = 'untitled';
let isDirty = false;

// --- Element type definitions ------------------------------------------------
type ElFieldMeta = { key: string; label: string; type: string; default: any; full?: boolean };
type ElFlagMeta  = { key: string; label: string };
const EL_FIELDS: Partial<Record<keyof typeof ElementType, ElFieldMeta[]>> = {
  Image2D: [
    { key: 'xOffset', label: 'X offset', type: 'number', default: 0 }, { key: 'yOffset', label: 'Y offset', type: 'number', default: 0 },
    { key: 'width', label: 'Width', type: 'number', default: W }, { key: 'height', label: 'Height', type: 'number', default: H },
  ],
  Animation: [
    { key: 'xOffset', label: 'X offset', type: 'number', default: 0 }, { key: 'yOffset', label: 'Y offset', type: 'number', default: 0 },
    { key: 'width', label: 'Width', type: 'number', default: W }, { key: 'height', label: 'Height', type: 'number', default: H },
    { key: 'updateInterval', label: 'Update interval', type: 'number', default: 12 },
    { key: 'frames', label: 'Frames', type: 'list', default: [] },
  ],
  ClippedText: [
    { key: 'text', label: 'Text', type: 'text', default: 'Hello, World!', full: true },
    { key: 'xOffset', label: 'X offset', type: 'number', default: 0 }, { key: 'yOffset', label: 'Y offset', type: 'number', default: 32 },
    { key: 'width', label: 'Width', type: 'number', default: W }, { key: 'height', label: 'Height', type: 'number', default: 16 },
    { key: 'fontIndex', label: 'Font', type: 'font', default: 'Font 0' },
  ],
  HScrollText: [
    { key: 'text', label: 'Text', type: 'text', default: 'Scrolling text - ', full: true },
    { key: 'xOffset', label: 'X offset', type: 'number', default: 0 }, { key: 'yOffset', label: 'Y offset', type: 'number', default: 32 },
    { key: 'width', label: 'Width', type: 'number', default: W }, { key: 'height', label: 'Height', type: 'number', default: 16 },
    { key: 'scrollSpeed', label: 'Scroll speed', type: 'number', default: 50 },
    { key: 'fontIndex', label: 'Font', type: 'font', default: 'Font 0' },
  ],
  VScrollText: [
    { key: 'text', label: 'Text', type: 'text', default: 'Scrolling text - ', full: true },
    { key: 'xOffset', label: 'X offset', type: 'number', default: 0 }, { key: 'yOffset', label: 'Y offset', type: 'number', default: 32 },
    { key: 'width', label: 'Width', type: 'number', default: W }, { key: 'height', label: 'Height', type: 'number', default: 16 },
    { key: 'scrollSpeed', label: 'Scroll speed', type: 'number', default: 50 },
    { key: 'fontIndex', label: 'Font', type: 'font', default: 'Font 0' },
  ],
  CurrentTime: [
    { key: 'xOffset', label: 'X offset', type: 'number', default: 0 }, { key: 'yOffset', label: 'Y offset', type: 'number', default: 8 },
    { key: 'width', label: 'Width', type: 'number', default: W }, { key: 'height', label: 'Height', type: 'number', default: 16 },
    { key: 'utcOffsetMinutes', label: 'UTC offset (min)', type: 'number', default: 0 },
    { key: 'fontIndex', label: 'Font', type: 'font', default: 'Font 0' },
  ],
};
const EL_FLAGS: Partial<Record<keyof typeof ElementType, ElFlagMeta[]>> = {
  HScrollText: [{ key: 'endless', label: 'Endless' }, { key: 'invertDirection', label: 'Invert direction' }],
  CurrentTime: [{ key: 'clock12h', label: '12h mode' }, { key: 'showHours', label: 'Show hours' }, { key: 'showSeconds', label: 'Show seconds' }],
};
const EL_TYPES: ElementType[] = Object.keys(EL_FIELDS).map((x: string) => MonoDisplay.StringToElementType[x as MonoDisplay.ElementTypeName]);

const DEFAULT_FONTS = [
  "Font 1",
  "Font 2",
  "Font 3",
  "Font 4",
  "Font 5",
]

// --- Confirm dialog -----------------------------------------------------------
function confirm(msg: string, buttons: {
  primary?: boolean,
  label: string,
  action: boolean,
}[]) {
  // buttons: [{label,primary,action}]
  return new Promise(resolve => {
    const overlay: HTMLElement | null = document.getElementById('confirm-overlay');
    const confirm = document.getElementById('confirm-msg');
    if(confirm) confirm.textContent = msg;
    const btns = document.getElementById('confirm-btns');
    if(btns) btns.innerHTML = '';
    buttons.forEach(b => {
      const el = document.createElement('button');
      el.className = 'cb' + (b.primary ? ' primary' : '');
      el.textContent = b.label;
      el.onclick = () => { overlay?.classList.remove('show'); resolve(b.action); };
      if(btns) btns.appendChild(el);
    });
    overlay?.classList.add('show');
  });
}

// --- Dirty tracking -----------------------------------------------------------
function markDirty() {
  isDirty = true;
  document.getElementById('filename').classList.add('dirty');
}
function markClean() {
  isDirty = false;
  document.getElementById('filename').classList.remove('dirty');
}
async function guardDirty() {
  if (!isDirty || !file.sections.length) return true;
  const action = await confirm(
    'You have unsaved changes. Loading a demo will replace the current editor state.',
    [{ label: 'Cancel', action: false }, { label: 'Load anyway', primary: true, action: true }]
  );
  return action;
}

// --- Helpers -----------------------------------------------------------------
function getElementId(elementIndex: number): string {
  return `Element-${elementIndex}`;
}
function getSectionId(sectionIndex: number): string {
  return `Section-${sectionIndex}`;
}
function newSec() {
  return {
    sectionType: MonoDisplay.SectionType.ElementsAlways,
    elements: [],
    flags: {},
  };
}
function newEl(type: MonoDisplay.ElementType) {
  const fields = {};
  (EL_FIELDS[type] || []).forEach(f => fields[f.k] = f.d);
  const flags = {};
  (EL_FLAGS[type] || []).forEach(f => flags[f.k] = false);
  return { type, fields, flags };
}
function getSec(sectionIndex: number | null) {
  return file.sections.find((s,index) => index === sectionIndex)
}

function getEl(sectionIndex: number, elementIndex: number) {
  const section = getSec(sectionIndex);
  return section && "elements" in section && section.elements.find((e, index) => index == elementIndex)
}

// --- Mutations ----------------------------------------------------------------
function addSection() {
  const s = newSec(); file.sections.push(s);
  activeSecIndex = file.sections.length-1; activeElIndex = null;
  markDirty(); renderHTML(); triggerPreview();
}
function removeSection(sectionIndex:number) {
  file.sections = file.sections.filter((section, index) => index !== sectionIndex);
  if (activeSecIndex === sectionIndex) {
    activeSecIndex = file.sections.length ? file.sections.length - 1 : null;
    activeElIndex = null;
  }
  markDirty(); renderHTML(); triggerPreview();
}
function toggleSection(id) {
  const s = getSec(id); if (!s) return;
  s.open = !s.open; activeSecIndex = id; renderHTML(); updateMeta();
}
function setSectionFlag(flag, val) {
  if (!activeSecIndex) return;
  const s = getSec(activeSecIndex); if (!s) return;
  s.flags[flag] = val; markDirty(); triggerPreview();
}
function addElement(sectionIndex:number) {
  const s = getSec(secId); if (!s) return;
  const el = newEl(EL_TYPES[0]); s.elements.push(el);
  activeSecIndex = secId; activeElIndex = { secId, elId: el.id };
  markDirty(); renderHTML(); triggerPreview();
}

function removeElement(sectionIndex:number, elementIndex:number) {
  const s = getSec(sectionIndex); if (!s) return;
  s.elements = s.elements.filter(e => e.id !== elId);
  if (activeElIndex && activeElIndex.secId === secId && activeElIndex.elId === elId) activeElIndex = null;
  markDirty(); renderHTML(); triggerPreview();
}
function selectElement(sectionIndex:number, elementIndex:number) {
  activeSecIndex = sectionIndex;
  activeElIndex = activeElIndex &&
    activeElIndex === sectionIndex &&
    activeElIndex === elementIndex ?
      null : elementIndex;
  renderHTML();
}
function changeElType(sectionIndex:number, elementIndex:number, typeString: string) {
  const el = getEl(sectionIndex, elementIndex); if (!el) return;
  const type = MonoDisplay.StringToElementType[typeString as MonoDisplay.ElementTypeName];
  const fresh = newEl(type);
  el.type = type; el.fields = fresh.fields; el.flags = fresh.flags;
  markDirty(); renderHTML(); triggerPreview();
}
function setElField(sectionIndex:number, elementIndex:number, key:string, val:any) {
  const el = getEl(sectionIndex, elementIndex); if (!el) return;
  el[key] = val;
  markDirty(); triggerPreview();
}
function setElFlag(sectionIndex:number, elementIndex:number, key:string, val:any) {
  const el = getEl(sectionIndex, elementIndex); if (!el) return;
  el.flags[key] = val;
  markDirty(); triggerPreview();
}
function setSectionType(sectionIndex: number, sectionType: string) {
  const sec = getSec(sectionIndex);
  if (sec) {
    sec.sectionType = MonoDisplay.StringToSectionType[sectionType as MonoDisplay.SectionTypeName];
    if (sec.sectionType == MonoDisplay.SectionType.CustomFont) {
      sec.fontData = new Uint8Array();
    } else {
      sec.elements = [];
      sec.flags = {};
    }
    markDirty(); triggerPreview();
  }
}

// --- Load demo into editor state ---------------------------------------------
const DEMO = {
  Checkerboard() {
    const pixels = new Uint8Array(W * H);
    for (let y = 0; y < H; y++)for (let x = 0; x < W; x++)pixels[y * W + x] = (x + y) % 2;
    return new MonoDisplayFile(
      [{
        sectionType: MonoDisplay.SectionType.ElementsAlways,
        elements: [{
          type: ElementType.Image2D,
          image: {
            pixels,
            height: H,
            width: W,
          },
          xOffset: 0, yOffset: 0,
        }],
        flags: {
          clearBuffer: true,
          drawFront: true,
          drawBack: true
        }
      }]);
  },
  Blink() {
    return new MonoDisplayFile(
      [{
        sectionType: MonoDisplay.SectionType.ElementsAlways,
        elements:[{
          type: ElementType.Animation, width: W, height: H, updateInterval: 12,
          xOffset: 0,
          yOffset: 0,
          frames: [{
            pixels: new Uint8Array(W * H).fill(1),
            width: 0,
            height: 0
          }, {
            pixels: new Uint8Array(W * H).fill(0),
            width: 0,
            height: 0
          }]
        }],
        flags: {
          clearBuffer: true,
          drawFront: true,
          drawBack: true
        }
      }]);
  },
  Text() {
    return new MonoDisplayFile(
      [{
        sectionType: MonoDisplay.SectionType.ElementsAlways,
        elements: [{
          type: ElementType.ClippedText,
          fontIndex: 0,
          text: 'Hello, World!',
          xOffset: 0, yOffset: 32,
          width: W, height: 16
        }],
        flags: {
          clearBuffer: true,
          drawFront: true,
          drawBack: true
        }
      }]);
  },
  Scrolltext() {
    return new MonoDisplayFile(
      [{
        sectionType: MonoDisplay.SectionType.ElementsAlways,
        elements: [{
          type: ElementType.HScrollText,
          fontIndex: 0,
          text: 'MONO DISPLAY - scrolling ticker - 🚀 ',
          xOffset: 0, yOffset: 32,
          width: W, height: 16,
          scrollSpeed: 50,
          flags: {
            endless: true,
            invertDirection: false,
            padStart: false,
            padEnd: false
          }
        }],
        flags: {
          clearBuffer: true,
          drawFront: true,
          drawBack: true
        }
      }]);
  },
  Time() {
    return new MonoDisplayFile(
      [{
        sectionType: MonoDisplay.SectionType.ElementsAlways,
        elements: [
          {
            type: ElementType.CurrentTime,
            fontIndex: 0,
            flags: {},
            xOffset: 0, yOffset: 8, width: W, height: 16, utcOffsetMinutes: 120
          },
          {
            type: ElementType.CurrentTime,
            fontIndex: 0,
            flags: { clock12h: true },
            xOffset: 40, yOffset: 16, width: W, height: 16, utcOffsetMinutes: 120
          },
          {
            type: ElementType.CurrentTime,
            fontIndex: 0,
            flags: { clock12h: true, showHours: true },
            xOffset: 0, yOffset: 24, width: W, height: 16, utcOffsetMinutes: 120
          },
          {
            type: ElementType.CurrentTime,
            flags: { clock12h: true, showHours: false },
            fontIndex: 0,
            xOffset: 40, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120
          },
          {
            type: ElementType.CurrentTime,
            fontIndex: 0,
            flags: { clock12h: true, showSeconds: true },
            xOffset: 80, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120
          },
        ],
        flags: {
          clearBuffer: true,
          drawFront: true,
          drawBack: true
        }
      }]);
  },
};

function initDemos() {
  const wrap = document.getElementById('demo-btns');
  let activeBtn = null;
  Object.entries(DEMO).forEach((entry: [demoName:string, demoFileFunc: () => MonoDisplayFile]) => {
    const [demoName, demoFileFunc] = entry;
    const btn = document.createElement('button');
    btn.className = 'tb-btn'; btn.textContent = demoName;
    btn.onclick = async () => {
      const ok = await guardDirty();
      if (!ok) return;
      // load into editor
      file = demoFileFunc();
      activeSecIndex = null;
      activeElIndex = null;
      currentFilename = demoName.toLowerCase();
      document.getElementById('filename').textContent = currentFilename;
      markClean(); renderHTML();
      triggerPreview();
      if (activeBtn) activeBtn.style.color = '';
      btn.style.color = '#33ff66'; activeBtn = btn;
    };
    wrap.appendChild(btn);
  });
}

// --- Render -------------------------------------------------------------------
function renderHTML() {
  updateMeta();
  const wrap = document.getElementById('sections-wrap');
  if (!file.sections.length) {
    wrap.innerHTML = '<div class="empty-state">No sections yet.<br/>Use <b>Add section</b> to get started.</div>';
    return;
  }
  wrap.innerHTML = file.sections.map((section, index, sections) => renderHTMLSection(index)).join('');
}

function renderHTMLSection(sectionIndex: number) {
  const section = file.sections.at(sectionIndex);
  if (!section) return ``;
  if (activeSecIndex == null && file.sections.length == 1) activeSecIndex = 0;
  const isActive = activeSecIndex === sectionIndex, isOpen = true;
  const sectionId = getSectionId(sectionIndex);
  switch (section.sectionType) {
    case MonoDisplay.SectionType.ElementsAlways:
    case MonoDisplay.SectionType.ElementsTimespan:
    return `
<div class="sec-card${isActive ? ' active' : ''}${isOpen ? ' open' : ''}" id="${sectionId}">
<div class="sec-hdr${isActive ? ' active' : ''}" onclick="toggleSection('${sectionId}')">
<span class="sec-arrow">▶</span>
<span class="sec-label">${MonoDisplay.SectionTypeToString[section.sectionType]}</span>
<span class="sec-badge">${section.elements.length} el</span>
<button class="x-btn" title="Remove section" onclick="event.stopPropagation();removeSection(${sectionIndex})">X</button>
</div>
${isOpen ? `
<div class="el-list">
${section.elements.map((el: MonoDisplay.MonoFormatElement, index:number) => renderHTMLElement(sectionIndex, index, el)).join('')}
<button class="add-el" onclick="addElement('${sectionId}')">+ add element</button>
</div>`: ''}
</div>`;
    case MonoDisplay.SectionType.CustomFont: {
    return `
<div class="sec-card${isActive ? ' active' : ''}${isOpen ? ' open' : ''}" id="${sectionId}">
<div class="sec-hdr${isActive ? ' active' : ''}" onclick="toggleSection('${sectionId}')">
<span class="sec-arrow">▶</span>
<span class="sec-label">Custom Font</span>
<span class="sec-badge">1 el</span>
<button class="x-btn" title="Remove section" onclick="event.stopPropagation();removeSection(${sectionIndex})">X</button>
</div>
${isOpen ? `
<div class="el-list">
?
</div>`: ''}
</div>`;
    }
  }
}

function renderHTMLElement(sectionIndex: number, elementIndex: number, el: MonoDisplay.MonoFormatElement) {
  const section = file.sections.at(sectionIndex);
  const currentSectionElements: any[] = section && "elements" in section ? section.elements: [];
  if (activeElIndex == null && currentSectionElements.length == 1) activeElIndex = 0;
  const isActive = activeElIndex == elementIndex && activeSecIndex == sectionIndex;
  const elementId = getElementId(elementIndex);
  const sectionId = getSectionId(sectionIndex);
  const typeString = MonoDisplay.ElementTypeToString[el.type];
  const fields = EL_FIELDS[typeString] || [];
  const flags = EL_FLAGS[typeString] || [];
  const getCustomFonts = ():{fontname:string, index:number}[] => {
    return file.sections
      .filter(section => section.sectionType == MonoDisplay.SectionType.CustomFont)
      .map((fontSection, index) => {
        return {
          fontname: `CustomFont ${index}`,
          index: 0x8000 + index,
        };
      });
  }
  function renderHTMLField(field: ElFieldMeta) {
    switch (field.type) {
      case 'text': {
        return `<textarea
          onchange="setElField(${sectionIndex},${elementIndex},'${field.key}',this.value)"
          >${esc(el[field.key] ?? field.default)}</textarea>`
      }
      case 'font': {
        return `<select onchange="setElField(${sectionIndex},${elementIndex},'${field.key}',+this.value)">
        ${DEFAULT_FONTS.map((fontname, index) => `<option value="${index}" >${fontname}</option>`).join("")}
        ${getCustomFonts().map((k:{fontname:string, index:number}) => `<option value="${k.index}" >${k.fontname}</option>`).join("")}
        </select>
        `;
      }
      default:
        return `<input type="number"
        value="${el[field.key as string] ?? field.default}"
        onchange="setElField(${sectionIndex},${elementIndex},'${field.key}',+this.value)"
        />`;
    }
  }
  function renderHTMLFlag(el: MonoDisplay.MonoFormatElement & {flags: Record<string, any>}, flag: ElFlagMeta | null) {
    if (el.flags && flag)
      return `
    <label class="flag-check">
      <input type="checkbox" ${el.flags[flag.key] ? 'checked' : ''}
        onchange="setElFlag(${sectionIndex},${elementIndex},'${flag.key}',this.checked)"
      />
      ${flag.label}
    </label>`;
    return ``;
  }

  const fieldsGrid = fields.map(field => `
  <div class="field${field.full ? ' full' : ''}">
    <label>${field.label}</label>
    ${renderHTMLField(field)}
  </div>`).join('');

  const flagsGrid = flags.length ? `
<div class="field full">
  <label>Flags</label>
  <div class="flags-row">
    ${flags.map(flag => renderHTMLFlag(el, flag)).join('')}
  </div>
</div>`: ``;

  const activeRender = `
<div class="el-fields">
<div class="field full">
  <label>Type</label>
  <select onchange="changeElType(${sectionIndex},${elementIndex},this.value)">
    ${EL_TYPES.map(t => `<option value="${MonoDisplay.ElementTypeToString[t]}"${t === el.type ? ' selected' : ''}>${MonoDisplay.ElementTypeToString[t]}</option>`).join('')}
  </select>
</div>
<div class="fields-grid">
  ${fieldsGrid}
</div>
${flagsGrid}
</div>`;

  return `
<div class="el-item${isActive ? ' active' : ''}" id="${elementId}">
  <div class="el-item-hdr" onclick="selectElement(${sectionIndex},${elementIndex})">
    <span class="el-type">${MonoDisplay.ElementTypeToString[el.type]}</span>
    <span class="el-name">${elSummary(el)}</span>
    <button class="x-btn" title="Remove element" onclick="event.stopPropagation();removeElement(${sectionIndex},${elementIndex})">X</button>
  </div>
${isActive ? activeRender : ``}
</div>`;
}

function elSummary(el: MonoDisplay.MonoFormatElement) {
  switch (el.type) {
    case ElementType.ClippedText:
    case ElementType.HScrollText:
      // case ElementType.VScrollText:
      return esc(el.text.slice(0, 24) + (el.text.length > 24 ? '...' : ''));
    case ElementType.CurrentTime:
    case ElementType.Image2D:
    case ElementType.HorizontalScroll:
    case ElementType.VerticalScroll:
      return `${el.xOffset ?? 0}, ${el.yOffset ?? 0}`;

    case ElementType.Animation:
    case ElementType.Line:
      return `${el.type.toString()}`;
    default:
      return "?";
  }
}
function esc(s) { return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') }

function updateMeta() {
  const section = getSec(activeSecIndex);
  const sectionSelection = document.getElementById('meta-name');
  if (sectionSelection) {
    if (section) {
      const options = Object.entries(MonoDisplay.SectionTypeToString)
        .map((element, index) => `<option value=${element[1]}>${element[1]}</option>`)
        .join("");
      sectionSelection.innerHTML = `<select id="section-type-selection" onchange="setSectionType(activeSecIndex, this.value)">
    ${options}
    </select>`;
    } else {
      sectionSelection.innerHTML = "-";
    }
  }
  document.getElementById('meta-count').textContent = section && "elements" in section ? section.elements.length + ' element(s)' : '-';
  document.getElementById('flag-drawFront').checked = section ? !!section.flags.drawFront : false;
  document.getElementById('flag-drawBack').checked = section ? !!section.flags.drawBack : false;
  document.getElementById('flag-clearBuffer').checked = section ? !!section.flags.clearBuffer : false;
}

// --- Preview ------------------------------------------------------------------
let previewTimer = null;
function triggerPreview() {
  clearTimeout(previewTimer);
  previewTimer = setTimeout(() => { try { buildPreview(); } catch (e) { console.warn('preview', e); } }, 150);
}
function buildPreview() {
  if (!window.MonoDisplay) return;
  if (!window._mdDriver)
    window._mdDriver = new MonoDisplay.MonoDisplayDriver('canvas_root', { onColor: '#EC0', offColor: '#000', fps: 25 });
  const s = file.sections[activeSecIndex || 0];
  window._mdDriver.load(() => Promise.resolve(file.toBuffer()));
  // now update the sections
  renderHTML();
}

// --- Load / Export ------------------------------------------------------------
function loadBin(input: { files: any[]; value: string; }) {
  const binFile = input.files[0]; if (!binFile) return;
  currentFilename = binFile.name;
  document.getElementById('filename').textContent = binFile.name;
  const reader = new FileReader();
  reader.onload = e => {
    const arrayBuf = e.target.result;
    try {
      file = new window.MonoDisplay.MonoDisplayParser().parse(arrayBuf);
      activeSecIndex = null;
      activeElIndex = null;
      triggerPreview();
    } catch (err) {
      console.warn('Could not parse sections from bin:', err);
    }
  };
  reader.readAsArrayBuffer(binFile);
  input.value = ''; markClean();
}
function exportBin() {
  if (!window.MonoDisplay) { alert('MonoDisplay library not loaded.'); return; }
  const blob = new Blob([file.toBuffer() as any], { type: 'application/octet-stream' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = currentFilename.replace(/\.bin$/, '') + '.bin';
  a.click(); markClean();
}



// --- Init ---------------------------------------------------------------------
// Pin all functions referenced from inline HTML onclick/onchange strings so
// Bun's bundler does not tree-shake them as unreachable dead code.
Object.assign(window, {
  loadBin, exportBin,
  addSection, removeSection, toggleSection, setSectionFlag,
  addElement, removeElement, selectElement,
  changeElType, setElField, setElFlag, setSectionType,
  cycleTheme,
});

triggerPreview();
if (window.MonoDisplay) { initDemos(); }
else { const iv = setInterval(() => { if (window.MonoDisplay) { clearInterval(iv); initDemos(); } }, 100); }
