skill
Movie Maker
UnreviewedRecord 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.
<!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 — Smart Player</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;
}
.player-container {
flex: 1; display: flex; flex-direction: column; padding: 16px; gap: 12px;
}
.player-title {
font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px;
}
.back-btn {
background: none; border: none; color: #8b949e; cursor: pointer;
font-size: 14px; padding: 4px 8px;
}
.video-area {
flex: 1; background: #000; border-radius: 8px; border: 1px solid #21262d;
position: relative; overflow: hidden; display: flex; align-items: center;
justify-content: center; min-height: 200px;
}
.video-area canvas { width: 100%; height: 100%; }
.speed-indicator {
position: absolute; top: 12px; right: 12px;
background: rgba(210, 153, 34, 0.9); color: #fff;
padding: 4px 10px; border-radius: 4px; font-size: 12px; font-weight: 600;
display: none;
}
.speed-indicator.active { display: block; }
.player-timeline { padding: 0 4px; }
.timeline-bar {
position: relative; height: 12px; background: #21262d; border-radius: 6px;
cursor: pointer; overflow: visible;
}
.timeline-progress {
position: absolute; top: 0; left: 0; height: 100%; background: #30363d;
border-radius: 6px; pointer-events: none;
}
.timeline-playhead {
position: absolute; top: -2px; width: 4px; height: 16px;
background: #e6edf3; border-radius: 2px; pointer-events: none;
transition: left 0.1s linear;
}
.timeline-silence {
position: absolute; top: 0; height: 100%; background: #d29922;
opacity: 0.5; border-radius: 2px; cursor: pointer;
}
.timeline-silence:hover { opacity: 0.8; }
.timeline-labels {
display: flex; justify-content: space-between; font-size: 11px;
color: #8b949e; margin-top: 4px; font-variant-numeric: tabular-nums;
}
.player-bar {
display: flex; align-items: center; justify-content: space-between;
flex-wrap: wrap; gap: 8px;
}
.silence-nav { display: flex; align-items: center; gap: 8px; }
.silence-nav button {
padding: 4px 10px; font-size: 11px; border-radius: 4px;
background: #21262d; color: #e6edf3; border: 1px solid #30363d;
cursor: pointer; font-family: inherit;
}
.silence-nav button:hover { background: #30363d; }
.nav-label { color: #d29922; font-size: 12px; font-weight: 500; }
.smart-toggle {
display: flex; align-items: center; gap: 8px;
color: #8b949e; font-size: 12px;
}
.smart-toggle input { accent-color: #d29922; width: 14px; height: 14px; }
.smart-toggle .active-label { color: #d29922; font-weight: 600; }
.controls { display: flex; gap: 8px; }
.ctrl-btn {
padding: 6px 14px; font-size: 12px; 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-secondary { background: #21262d; color: #e6edf3; border: 1px solid #30363d; }
.legend {
display: flex; gap: 16px; font-size: 11px; color: #8b949e;
padding: 4px 0; align-items: center;
}
.legend-dot {
display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 4px;
}
.demo-note {
position: fixed; bottom: 8px; right: 12px; color: #484f58; font-size: 10px;
}
</style>
</head>
<body>
<div class="player-container">
<div class="player-title">
<button class="back-btn">←</button>
PCB Review — Rev B
</div>
<div class="video-area">
<canvas id="vid-canvas" width="640" height="360"></canvas>
<div class="speed-indicator" id="speed-ind">16x »</div>
</div>
<div class="player-timeline">
<div class="timeline-bar" id="timeline-bar">
<div class="timeline-progress" id="progress" style="width:35%"></div>
<div class="timeline-playhead" id="playhead" style="left:35%"></div>
</div>
<div class="timeline-labels">
<span id="current-time">2:01</span>
<span>5:47</span>
</div>
</div>
<div class="player-bar">
<div class="silence-nav">
<button>« Prev</button>
<span class="nav-label">4 silences</span>
<button>Next »</button>
</div>
<label class="smart-toggle">
<input type="checkbox" checked id="smart-cb">
<span class="active-label">Smart Playback ON</span> — skipping silence at 16x
</label>
</div>
<div class="controls">
<button class="ctrl-btn btn-secondary">❚❚ Pause</button>
<button class="ctrl-btn btn-secondary">Copy Link</button>
</div>
<div class="legend">
<span><span class="legend-dot" style="background:#d29922;"></span>Silence region (click to jump)</span>
<span><span class="legend-dot" style="background:#30363d;"></span>Playback progress</span>
<span><span class="legend-dot" style="background:#e6edf3;"></span>Playhead</span>
</div>
</div>
<div class="demo-note">Demo — simulated data</div>
<script>
// Simulated silence regions for a 347s video
const duration = 347;
const silences = [
{startMs: 12000, endMs: 18000},
{startMs: 45000, endMs: 52000},
{startMs: 120000, endMs: 135000},
{startMs: 210000, endMs: 218000}
];
// Render silence markers on timeline
const bar = document.getElementById('timeline-bar');
silences.forEach((r, i) => {
const left = (r.startMs / 1000) / duration * 100;
const width = ((r.endMs - r.startMs) / 1000) / duration * 100;
const el = document.createElement('div');
el.className = 'timeline-silence';
el.style.left = left + '%';
el.style.width = Math.max(width, 0.5) + '%';
el.title = 'Silence ' + (i + 1);
bar.appendChild(el);
});
// Animate playback simulation
let pos = 0.35;
const speedInd = document.getElementById('speed-ind');
const progress = document.getElementById('progress');
const playhead = document.getElementById('playhead');
const currentTime = document.getElementById('current-time');
function fmtTime(s) {
const m = Math.floor(s / 60);
const sec = Math.floor(s % 60);
return m + ':' + String(sec).padStart(2, '0');
}
setInterval(() => {
const sec = pos * duration;
const inSilence = silences.some(r => sec * 1000 >= r.startMs && sec * 1000 <= r.endMs);
const step = inSilence ? 0.004 : 0.0003;
pos = Math.min(pos + step, 1);
progress.style.width = (pos * 100) + '%';
playhead.style.left = (pos * 100) + '%';
currentTime.textContent = fmtTime(pos * duration);
speedInd.classList.toggle('active', inSilence);
if (pos >= 1) pos = 0;
}, 50);
// Draw fake video frame
const canvas = document.getElementById('vid-canvas');
const ctx = canvas.getContext('2d');
const grd = ctx.createLinearGradient(0, 0, 640, 360);
grd.addColorStop(0, '#0d2636');
grd.addColorStop(1, '#1a3a4a');
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 640, 360);
// Fake toolbar
ctx.fillStyle = '#161b22';
ctx.fillRect(0, 0, 640, 32);
ctx.fillStyle = '#8b949e';
ctx.font = '12px sans-serif';
ctx.fillText('Gallia Viewer — PCB Review', 10, 21);
// Fake PCB outline
ctx.strokeStyle = '#00b8b0';
ctx.lineWidth = 2;
ctx.strokeRect(150, 80, 340, 220);
ctx.strokeStyle = '#30363d';
ctx.lineWidth = 1;
for (let x = 170; x < 480; x += 40) {
for (let y = 100; y < 280; y += 30) {
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI * 2);
ctx.stroke();
}
}
// Fake component rectangles
ctx.fillStyle = '#21262d';
ctx.fillRect(200, 120, 80, 40);
ctx.fillRect(320, 180, 60, 60);
ctx.fillRect(250, 240, 100, 30);
ctx.fillStyle = '#3fb950';
ctx.font = '9px sans-serif';
ctx.fillText('U1', 230, 145);
ctx.fillText('C3', 340, 215);
ctx.fillText('R7-R12', 275, 260);
</script>
</body>
</html>