fusible 0.18.0

Electronic linter: a fuse that blows to block firmware code generation
(() => {
  const state = { name: null, data: null, error: null, timer: null, seq: 0 };
  const STATUS_TEXT = { running: 'RUNNING', ok: 'OK', fuse_blown: 'FUSE BLOWN', error: 'FAILED' };

  const view = () => document.getElementById('firmware-view');

  function lastBuild(name) {
    const runs = (window.Project && window.Project.runs()) || [];
    const own = runs.filter((run) => run.label === `build ${name}` || run.label === 'build all');
    return own[own.length - 1];
  }

  function headerHtml(name, data) {
    const build = lastBuild(name);
    const busy = !!(window.Project && window.Project.isBusy());
    const meta = data
      ? [data.board && `BOARD ${data.board}`, `MCU ${data.mcu}`, data.framework && `FRAMEWORK ${data.framework}`]
      : [];
    const status = build
      ? `<span class="run-status ${build.status}">${STATUS_TEXT[build.status] || build.status}</span>`
      : '<span class="run-status">NOT BUILT</span>';
    const button = !data || data.buildable
      ? `<button class="btn" id="fw-build" ${busy ? 'disabled' : ''}>BUILD</button>`
      : '';
    return `<div class="fw-head">
      <div class="fw-title">${escapeHtml(name)}</div>
      <div class="fw-meta">${meta.filter(Boolean).map((m) => escapeHtml(m)).join(' · ')}</div>
      <div class="fw-actions">${button}${status}</div>
    </div>`;
  }

  function pinHtml(pin) {
    const signal = pin.const.replace(/^gpio::/, '');
    const remote = pin.remote ? `<span class="fw-remote">${escapeHtml(pin.remote)}</span>` : '';
    const violation = pin.violation ? `<span class="fw-violation">${escapeHtml(pin.violation)}</span>` : '';
    const part = pin.remote ? pin.remote.split('.')[0] : '';
    return `<button class="fw-pin${pin.violation ? ' blown' : ''}" data-net="${escapeHtml(pin.net)}" data-part="${escapeHtml(part)}" title="${escapeHtml(pin.net)}">
      <span class="fw-pin-name">${escapeHtml(pin.pin)}</span>
      <span class="fw-net">${escapeHtml(signal)}</span>${remote}
      <span class="fw-rust">${escapeHtml(pin.field)}</span>${violation}
    </button>`;
  }

  function groupsHtml(data) {
    const groups = {};
    data.pins.forEach((pin) => { (groups[pin.group] = groups[pin.group] || []).push(pin); });
    const generated = data.pins_rs
      ? `<div class="fw-file">${escapeHtml(data.pins_rs)} · ${data.generated ? 'GENERATED' : 'NOT GENERATED YET'}</div>`
      : '';
    const conflicts = data.violations.length
      ? `<div class="fw-summary blown">${data.violations.length} PIN-MUX CONFLICT${data.violations.length === 1 ? '' : 'S'}</div>`
      : '';
    const body = Object.keys(groups).map((group) => `<div class="tree-group">${escapeHtml(group.toUpperCase())} · ${groups[group].length}</div>${groups[group].map(pinHtml).join('')}`);
    return `${generated}${conflicts}${body.join('') || '<div class="empty-state"><span>NO GPIO PINS MAPPED.</span></div>'}`;
  }

  function render() {
    if (!view()) return;
    if (!state.name) {
      view().innerHTML = '<div class="empty-state"><span>SELECT A FIRMWARE IN THE PROJECT TREE.</span></div>';
      return;
    }
    const detail = state.error
      ? `<div class="fw-error">${escapeHtml(state.error)}</div>`
      : state.data ? groupsHtml(state.data) : '<div class="empty-state"><span>LOADING PIN MAP...</span></div>';
    view().innerHTML = headerHtml(state.name, state.data) + detail;
  }

  async function load(name) {
    const seq = ++state.seq;
    let data = null;
    let error = null;
    try {
      const response = await fetch(`/api/firmware?name=${encodeURIComponent(name)}`, { cache: 'no-store' });
      const body = await response.json().catch(() => ({}));
      if (response.ok) data = body;
      else error = body.error || `HTTP ${response.status}`;
    } catch (err) {
      error = String(err);
    }
    if (seq !== state.seq) return;
    Object.assign(state, { data, error });
    render();
  }

  function reload(delay) {
    clearTimeout(state.timer);
    if (state.name) state.timer = setTimeout(() => load(state.name), delay);
  }

  function show(name) {
    if (name !== state.name) Object.assign(state, { name, data: null, error: null });
    render();
    switchSidebarTab('pins');
    toggleSidebar(true, false);
    load(name);
  }

  function onClick(event) {
    if (event.target.closest('#fw-build')) {
      if (state.name) window.Project.startRun('build', state.name);
      return;
    }
    const row = event.target.closest('.fw-pin');
    if (!row) return;
    document.querySelectorAll('.fw-pin').forEach((el) => el.classList.toggle('selected', el === row));
    selectPin(row.dataset.net, row.dataset.part);
  }

  function selectPin(net, part) {
    if (net) selectNet(net);
    const inViewer = typeof netsMap !== 'undefined' && netsMap[net];
    if (part && !inViewer) inspectComponent(part, true);
  }

  function onBoard(board) {
    if (state.data && state.data.board === board) reload(300);
  }

  function onRun(status) {
    render();
    if (status !== 'running') reload(0);
  }

  document.addEventListener('DOMContentLoaded', () => {
    view().addEventListener('click', onClick);
  });

  window.FirmwareView = { show, onBoard, onRun };
})();