skill
Movie Maker
Public Made by Adomby adom
Record narrated walkthroughs of your Gallia project with screen + mic capture, silence detection, smart playback that skips dead air, timeline markers, and a full video library with thumbnails.
← Commit history
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">▶ 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>