← Commit history

Migrate asset from Helium wiki

Kyle Bergstedt ·5dcd9c714a ·4mo ago ·parent 9c81824
1 file changed +184
demo-library.htmladded+184
@@ -0,0 +1,184 @@+<!DOCTYPE html>+<html lang="en">+<head><script>(function(){var n=0;function show(p,m){var el=document.createElement('div');el.style.cssText='position:fixed;left:0;right:0;background:#e04040;color:#fff;padding:6px 12px;z-index:'+(99999-n)+';font:13px/1.4 monospace;white-space:pre-wrap;border-bottom:1px solid #b02020;top:'+n*30+'px';el.textContent=p+m;document.body.appendChild(el);n++}window.onerror=function(msg,src,line){show('ERROR '+(src||'').split('/').pop()+':'+line+' \u2014 ',msg)};window.addEventListener('unhandledrejection',function(e){show('REJECT \u2014 ',e.reason&&e.reason.message||e.reason||'unknown')})})()</script>+<meta charset="UTF-8">+<meta name="viewport" content="width=device-width, initial-scale=1.0">+<title>Movie Maker — Library</title>+<style>+* { margin: 0; padding: 0; box-sizing: border-box; }+body {+  background: #0d1117; color: #e6edf3;+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;+  height: 100vh; display: flex; flex-direction: column; overflow: hidden;+}+.tab-bar {+  display: flex; border-bottom: 1px solid #21262d; background: #161b22; flex-shrink: 0;+}+.tab-bar button {+  padding: 10px 24px; background: none; border: none; color: #8b949e;+  font-size: 13px; font-weight: 500; cursor: pointer; position: relative;+  font-family: inherit;+}+.tab-bar button.active { color: #e6edf3; }+.tab-bar button.active::after {+  content: ''; position: absolute; bottom: 0; left: 0; right: 0;+  height: 2px; background: #00b8b0; border-radius: 1px 1px 0 0;+}+.library-header {+  display: flex; align-items: center; justify-content: space-between;+  padding: 16px 20px; border-bottom: 1px solid #21262d; flex-shrink: 0;+}+.library-header h2 { font-size: 16px; font-weight: 600; }+.library-header span { color: #8b949e; font-size: 13px; }+.video-grid {+  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));+  gap: 16px; padding: 20px; overflow-y: auto; flex: 1;+}+.video-card {+  background: #161b22; border: 1px solid #21262d; border-radius: 8px;+  overflow: hidden; transition: border-color 0.15s;+}+.video-card:hover { border-color: #30363d; }+.video-thumb {+  width: 100%; aspect-ratio: 16/9; background: #0d1117; position: relative;+  cursor: pointer; overflow: hidden;+}+.video-thumb canvas { width: 100%; height: 100%; }+.thumb-duration {+  position: absolute; bottom: 6px; right: 6px;+  background: rgba(0,0,0,0.8); color: #fff; font-size: 11px;+  padding: 2px 6px; border-radius: 3px; font-variant-numeric: tabular-nums;+}+.thumb-play {+  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);+  width: 44px; height: 44px; background: rgba(0,0,0,0.6); border-radius: 50%;+  display: flex; align-items: center; justify-content: center;+  opacity: 0; transition: opacity 0.15s;+}+.video-thumb:hover .thumb-play { opacity: 1; }+.video-info { padding: 12px; }+.video-title {+  font-size: 14px; font-weight: 600; margin-bottom: 4px;+  background: none; border: none; color: #e6edf3; width: 100%;+  font-family: inherit; outline: none;+}+.video-meta { color: #8b949e; font-size: 12px; display: flex; gap: 12px; margin-bottom: 6px; }+.video-silence { font-size: 12px; margin-bottom: 8px; }+.video-actions { display: flex; gap: 8px; }+.btn-sm {+  padding: 5px 12px; font-size: 11px; border-radius: 5px; border: none;+  font-family: inherit; font-weight: 500; cursor: pointer;+  display: inline-flex; align-items: center; gap: 5px;+}+.btn-play { background: #238636; color: #fff; }+.btn-link { background: #21262d; color: #e6edf3; border: 1px solid #30363d; }+.btn-del { background: #21262d; color: #f85149; border: 1px solid #30363d; }+.demo-note {+  position: fixed; bottom: 8px; right: 12px; color: #484f58; font-size: 10px;+}+</style>+</head>+<body>+<div class="tab-bar">+  <button>Record</button>+  <button class="active">Library</button>+  <button>Scripts</button>+</div>+<div class="library-header">+  <h2>Video Library</h2>+  <span>3 recordings</span>+</div>+<div class="video-grid" id="grid"></div>+<div class="demo-note">Demo — simulated data</div>+<script>+const videos = [+  { title: 'PCB Review — Rev B', duration: 347000, size: 24500000, date: '2026-03-02',+    silences: [{startMs:12000,endMs:18000},{startMs:45000,endMs:52000},{startMs:120000,endMs:135000},{startMs:210000,endMs:218000}],+    color1: '#1a3a4a', color2: '#0d2636' },+  { title: '3D Model Walkthrough', duration: 182000, size: 12800000, date: '2026-03-01',+    silences: [{startMs:8000,endMs:14000},{startMs:90000,endMs:96000}],+    color1: '#2a1a3a', color2: '#1a0d2d' },+  { title: 'Schematic Annotation Demo', duration: 95000, size: 6700000, date: '2026-02-28',+    silences: [],+    color1: '#1a3a2a', color2: '#0d2d1a' }+];++function fmt(ms) {+  const s = Math.floor(ms / 1000);+  const m = Math.floor(s / 60);+  const sec = s % 60;+  return m + ':' + String(sec).padStart(2, '0');+}+function fmtSize(b) {+  return (b / 1048576).toFixed(1) + ' MB';+}++const grid = document.getElementById('grid');+videos.forEach(v => {+  const totalSilence = v.silences.reduce((s, r) => s + (r.endMs - r.startMs), 0);+  const talkMs = v.duration - totalSilence;+  let silenceHtml;+  if (v.silences.length > 0) {+    silenceHtml = `<span style="color:#d29922;">${v.silences.length} silence${v.silences.length>1?'s':''} (${fmt(totalSilence)} dead air, ${fmt(talkMs)} talking)</span>`;+  } else {+    silenceHtml = `<span style="color:#3fb950;">${fmt(talkMs)} talking, no dead air</span>`;+  }++  const card = document.createElement('div');+  card.className = 'video-card';+  card.innerHTML = `+    <div class="video-thumb">+      <canvas width="320" height="180" id="c-${v.title.replace(/\s/g,'')}"></canvas>+      <span class="thumb-duration">${fmt(v.duration)}</span>+      <div class="thumb-play">+        <svg width="20" height="20" viewBox="0 0 24 24" fill="#fff"><polygon points="8,5 19,12 8,19"/></svg>+      </div>+    </div>+    <div class="video-info">+      <div class="video-title">${v.title}</div>+      <div class="video-meta">+        <span>${v.date}</span>+        <span>${fmtSize(v.size)}</span>+        <span>${fmt(v.duration)}</span>+      </div>+      <div class="video-silence">${silenceHtml}</div>+      <div class="video-actions">+        <button class="btn-sm btn-play">&#9654; Play</button>+        <button class="btn-sm btn-link">Copy Link</button>+        <button class="btn-sm btn-del">Delete</button>+      </div>+    </div>+  `;+  grid.appendChild(card);++  // Draw a fake thumbnail+  const canvas = card.querySelector('canvas');+  const ctx = canvas.getContext('2d');+  const grd = ctx.createLinearGradient(0, 0, 320, 180);+  grd.addColorStop(0, v.color1);+  grd.addColorStop(1, v.color2);+  ctx.fillStyle = grd;+  ctx.fillRect(0, 0, 320, 180);+  // Add some fake UI elements+  ctx.fillStyle = '#30363d';+  ctx.fillRect(0, 0, 320, 24);+  ctx.fillStyle = '#8b949e';+  ctx.font = '10px sans-serif';+  ctx.fillText('Gallia Viewer', 8, 16);+  // Simulate content area+  ctx.strokeStyle = '#21262d';+  ctx.lineWidth = 1;+  for (let i = 40; i < 160; i += 20) {+    ctx.beginPath();+    ctx.moveTo(20, i);+    ctx.lineTo(300, i);+    ctx.stroke();+  }+  ctx.fillStyle = '#00b8b0';+  ctx.globalAlpha = 0.3;+  ctx.fillRect(60, 50, 200, 80);+  ctx.globalAlpha = 1;+});+</script>+</body>+</html>\ No newline at end of file