mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Final whole-branch review (Opus) triaged two FIX items from the SDD nits
ledger; the rest ship as-is.
- render.js: a synchronous throw from createRenderJob (post-mkdtemp, not
awaited) left an empty outDir on disk — the outer catch only reclaimed
extractDir. Reclaim outDir too when it exists, and correct the stale
comment that claimed the out dir was never created.
- {vertical,square}.html: the 4th highlights <li> lived in the DOM hidden
only by JS, so a no-JS / failed-script render would show an empty bullet.
Start it style="display:none" and reveal on populate, so an unscripted
render shows nothing instead.
Vitest smoke (release-announcement.test.js) 4/4 green; render.js syntax
checked. Python suite untouched by this fix (JS/HTML only).
79 lines
4.1 KiB
HTML
79 lines
4.1 KiB
HTML
<!doctype html>
|
||
<html lang="en" data-composition-id="release-announcement" data-composition-duration="10" data-fps="30">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<title>Release Announcement — square (1080×1080)</title>
|
||
<link rel="stylesheet" href="theme.css" />
|
||
<script src="props.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="stage" data-composition-id="release-announcement" data-width="1080" data-height="1080" data-start="0" data-duration="10" data-fps="30">
|
||
<!-- Scan line: thin hairline + travelling accent marker. Square cut
|
||
uses a tighter scan top (72) than the vertical cut (104). -->
|
||
<div id="scan" class="scan clip" data-start="0" data-duration="10" data-track-index="0" style="top: 72px;">
|
||
<div class="scan__marker"></div>
|
||
</div>
|
||
|
||
<!-- Content anchored in the lower two-thirds, left-aligned. Square cut
|
||
uses a tighter bottom pad (72) than the vertical cut (148). -->
|
||
<div class="content" style="--content-bottom: 72px;">
|
||
<div id="kicker" class="kicker clip" data-start="0" data-duration="10" data-track-index="1">New release</div>
|
||
<h1 id="headline" class="headline clip" data-start="0.267" data-duration="9.733" data-track-index="2">v0.19.0</h1>
|
||
<p id="script" class="script clip" data-start="0.8" data-duration="9.2" data-track-index="3">Ship day. Faster renders, sharper docs, one less thing to babysit.</p>
|
||
<ul id="highlights" class="highlights clip" data-start="1.667" data-duration="8.333" data-track-index="4">
|
||
<li class="highlights__item"><span class="highlights__tick"></span><span class="highlights__text">Fable-mode adopted fleet-wide</span></li>
|
||
<li class="highlights__item"><span class="highlights__tick"></span><span class="highlights__text">FE/UX-UI design bar shipped</span></li>
|
||
<li class="highlights__item"><span class="highlights__tick"></span><span class="highlights__text">Feature spotlight goes live on X</span></li>
|
||
<li class="highlights__item" style="display:none"><span class="highlights__tick"></span><span class="highlights__text"></span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="outro" class="outro clip" data-start="7.667" data-duration="2.333" data-track-index="5">roboco.tech</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Register the composition timeline metadata. Motion is driven by CSS
|
||
// keyframes (no GSAP — the render is offline, no CDN), but HyperFrames
|
||
// requires the registry entry to consider the composition valid.
|
||
window.__timelines = window.__timelines || {};
|
||
window.__timelines["release-announcement"] = {
|
||
id: "release-announcement",
|
||
duration: 10,
|
||
fps: 30,
|
||
animations: []
|
||
};
|
||
|
||
// Read sidecar-supplied props (props.js is overwritten at render time)
|
||
// and populate the DOM. Defensive: a missing prop leaves the placeholder
|
||
// text intact rather than throwing — a missing prop never crashes a render.
|
||
(function () {
|
||
var props = (window.__PROPS__ && typeof window.__PROPS__ === "object") ? window.__PROPS__ : {};
|
||
|
||
var version = props.version ? String(props.version) : "";
|
||
if (version && !version.startsWith("v")) version = "v" + version;
|
||
if (version) {
|
||
var headline = document.getElementById("headline");
|
||
if (headline) headline.textContent = version;
|
||
}
|
||
|
||
if (typeof props.script === "string" && props.script) {
|
||
var scriptEl = document.getElementById("script");
|
||
if (scriptEl) scriptEl.textContent = props.script;
|
||
}
|
||
|
||
var highlights = Array.isArray(props.highlights) ? props.highlights.slice(0, 4) : [];
|
||
var items = document.querySelectorAll(".highlights__item");
|
||
for (var i = 0; i < items.length; i++) {
|
||
var textEl = items[i].querySelector(".highlights__text");
|
||
if (!textEl) continue;
|
||
if (i < highlights.length && highlights[i]) {
|
||
textEl.textContent = String(highlights[i]);
|
||
items[i].style.display = "";
|
||
} else {
|
||
items[i].style.display = "none";
|
||
}
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html> |