<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>STM32F103C8T6 on its footprint — 3D Viewer</title>
  <script>
    // Defined BEFORE the viewer bundle tag so its onerror handler can call
    // it. A failed load fires onerror during head parsing, before the main
    // body script runs — without this, the user gets a permanent spinner.
    // (N7 / C7 / F-UX2) The handler is idempotent and DOM-safe (the #error /
    // #loading nodes may not exist yet, so it guards on them).
    window.__viewerLoadFailed = function (msg) {
      window.__viewerFailed = true;
      function show() {
        var err = document.getElementById("error");
        var load = document.getElementById("preload-msg");
        var em = document.getElementById("error-msg");
        if (em) em.textContent = msg || "The 3D viewer could not be loaded. Try reloading the page.";
        if (err) err.classList.add("visible");
        if (load) load.style.display = "none";
      }
      if (document.getElementById("error")) show();
      else document.addEventListener("DOMContentLoaded", show);
    };
  </script>
  <link rel="stylesheet" href="https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/style.css">
  <!-- Babylon 9 bundle is an ES module; loading it sets window.Adom3DViewerBabylon9.
       The init module below runs after this one (modules execute in document
       order), so the global is defined by the time it runs. -->
  <script type="module" src="https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/adom-3d-viewer-babylon9.esm.js?v=20260611d" onerror="window.__viewerLoadFailed && window.__viewerLoadFailed()"></script>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    html, body { width: 100%; height: 100%; overflow: hidden; background: #0d1117; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; }
    #viewer-wrap { position: absolute; inset: 0; }

    /* No wiki-side loading screen — the embedded viewer shows its own. The only
       thing here is a minimal text message during the brief window before the
       viewer bundle finishes downloading and paints its loader (z-index 1, so
       the viewer's canvas + its own loader cover it the moment they mount). On
       most navigations the bundle is already prefetched on hover, so this is
       barely seen. */
    #preload-msg {
      position: absolute; inset: 0; z-index: 1; pointer-events: none;
      display: flex; align-items: center; justify-content: center;
      color: #6e7681; font-size: 13px;
    }

    /* Info bar (kept above the viewer's own UI, click-through) */
    #info-bar {
      position: absolute; top: 0; left: 0; right: 0; padding: 12px 16px;
      background: linear-gradient(180deg, rgba(13,17,23,0.85) 0%, rgba(13,17,23,0) 100%);
      z-index: 10; pointer-events: none;
    }
    #info-bar h1 { font-size: 16px; font-weight: 600; color: #e6edf3; line-height: 1.3; }
    #info-bar .meta { font-size: 12px; color: #8b949e; margin-top: 4px; }
    #info-bar .meta span { margin-right: 12px; }

    /* Error state */
    #error {
      position: absolute; inset: 0; display: none; flex-direction: column;
      align-items: center; justify-content: center; background: #0d1117; z-index: 200;
    }
    #error.visible { display: flex; }
    #error h2 { color: #f85149; margin-bottom: 8px; }
    #error p { color: #8b949e; font-size: 14px; max-width: 400px; text-align: center; }
  </style>
</head>
<body>
  <div id="viewer-wrap"></div>
  <div id="preload-msg">Loading 3D viewer…</div>

  <div id="info-bar">
    <h1>STM32F103C8T6 on its footprint</h1>
    <div class="meta">
      <span>STMicroelectronics</span>
      <span>STM32F103C8T6</span>
      <span>LQFP-48 board-context</span>
    </div>
  </div>

  <div id="error">
    <h2>Failed to load model</h2>
    <p id="error-msg">The 3D model could not be loaded. It may be missing or corrupted.</p>
  </div>

  <script type="module">
    (function() {
      const loadingEl = document.getElementById("preload-msg");
      const errorMsg = document.getElementById("error-msg");
      const errorEl = document.getElementById("error");

      let modelLoaded = false;
      function showError(msg) {
        if (errorMsg) errorMsg.textContent = msg || "The 3D model could not be loaded. It may be missing or corrupted.";
        if (errorEl) errorEl.classList.add("visible");
        if (loadingEl) loadingEl.style.display = "none";
      }

      // Watchdog: if the bundle never defined the global or the GLB hasn't
      // loaded within the budget, surface the error div instead of spinning
      // forever. (N7 / C7 / F-UX2, F-UX3)
      const LOAD_TIMEOUT_MS = 20000;
      const loadTimer = setTimeout(function() {
        if (modelLoaded) return;
        if (typeof window.Adom3DViewerBabylon9 === "undefined") {
          showError("The 3D viewer could not be loaded. Try reloading the page.");
        } else {
          showError("The 3D model is taking too long to load. It may be unavailable or too large.");
        }
      }, LOAD_TIMEOUT_MS);

      const Viewer = window.Adom3DViewerBabylon9;
      if (window.__viewerFailed || typeof Viewer === "undefined") {
        clearTimeout(loadTimer);
        showError("The 3D viewer could not be loaded. Try reloading the page.");
        return;
      }

      // Self-host the Draco decoder (#18). Babylon defaults to fetching the
      // decoder wasm from the Babylon CDN at decode time, which the viewer CSP
      // blocks (status 0) -- so any KHR_draco_mesh_compression GLB (the default
      // step2glb output) failed with "Failed to load model". Point Draco at the
      // vendored copy before any model loads. window.BABYLON is set by the bundle
      // above; the URLs are ASSET_BASE-aware so they resolve behind a proxy too.
      try {
        if (window.BABYLON && window.BABYLON.DracoCompression) {
          window.BABYLON.DracoCompression.Configuration = { decoder: {
            wasmUrl: "https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/draco/draco_wasm_wrapper_gltf.js",
            wasmBinaryUrl: "https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/draco/draco_decoder_gltf.wasm",
            fallbackUrl: "https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/draco/draco_decoder_gltf.js",
          } };
        }
      } catch (e) { /* non-fatal: a non-Draco GLB still renders */ }

      const glbUrl = "/blob/component/stm32f103c8t6/stm32f103c8t6-board.glb";

      try {
        // zUp:true — components are CAD/KiCad parts authored Z-up; this both
        // orients them correctly and puts the scene in a right-handed system
        // that matches the view cube (so the cube tracks + clicks correctly).
        // It also sets the overhead spotlight + ground for proper shadows. No
        // environmentUrl — the Babylon 9 build ships a gradient studio default.
        const viewer = Viewer.init(document.getElementById("viewer-wrap"), {
          zUp: true,
          showViewCube: true,
          showGround: true,
        });
        // The bundle has loaded and the viewer is mounting — remove the text
        // message; the viewer's own loading screen takes over from here. (It's
        // z-index 1, above the canvas, so it must be removed, not just covered.)
        if (loadingEl) loadingEl.style.display = "none";
        viewer.loadModel(glbUrl).then(function() {
          modelLoaded = true;
          clearTimeout(loadTimer);
          viewer.frameModel();
          // Hide the world-origin XYZ axis gizmo — the view cube already
          // conveys orientation, so the in-scene axes are redundant clutter.
          // (refreshAxisScale preserves this state across later re-frames.)
          try { if (viewer.toggleAxes) viewer.toggleAxes("world", false); } catch (e) {}
        }).catch(function(err) {
          clearTimeout(loadTimer);
          showError("Error: " + ((err && err.message) || "the GLB could not be loaded"));
        });
      } catch (err) {
        clearTimeout(loadTimer);
        showError("The 3D viewer failed to start: " + ((err && err.message) || "unknown error"));
      }
    })();
  </script>
</body>
</html>