app
Hero Studio
Public Made by Adomby adom
The hero-image lab with a human gate: `hero-studio pose <owner/slug>` opens the studio in front of the human, posed for their package, and blocks until they click Generate, producing the provenance-stamped 2000x1250 PNG the registry's hero gate requires. The image carries the title and artwork; the exported image also bakes the type pill (top right) and full wiki slug (bottom right); the page shows the hero bare, and only the homepage marquee overlays its own dynamic info. Every hero wears its P
← Commit history
Publish 0.18.2
5 files changed
+34−15
package.json+1−1@@ -2,7 +2,7 @@ "slug": "hero-studio", "title": "Hero Studio", "type": "app",- "version": "0.18.1",+ "version": "0.18.2", "description": "The hero-image lab with a human gate: `hero-studio pose <owner/slug>` opens the studio in front of the human, posed for their package, and blocks until they click Generate, producing the provenance-stamped 2000x1250 PNG the registry's hero gate requires. The image carries the title and artwork; the exported image also bakes the type pill (top right) and full wiki slug (bottom right); the page shows the hero bare, and only the homepage marquee overlays its own dynamic info. Adom-org pages get the house wordmark and palette automatically; every other owner brings their own accent and background colors, with placement fixed for all. The floating app window is the vitrine: it auto-loads a real screenshot from the package's wiki page (screenshots/, docs/), takes a manual import, or a full-hero import (the human-ratified re-stamp path for existing heroes). The pose gate opens in verification mode: per-item checklist (title, subtitle, vitrine, type match) and a Generate that unlocks only fully confirmed. Includes vitrine bleeds, brand tile themes, gradient angles, pixel-faithful wiki marquee and index-card previews, and a component-hero mode with a photo/raytrace/phong fallback ladder.", "license": "MIT", "tags": [
page.json+1−1@@ -2,7 +2,7 @@ "slug": "hero-studio", "title": "Hero Studio", "type": "app",- "version": "0.18.1",+ "version": "0.18.2", "description": "The hero-image lab with a human gate: `hero-studio pose <owner/slug>` opens the studio in front of the human, posed for their package, and blocks until they click Generate, producing the provenance-stamped 2000x1250 PNG the registry's hero gate requires. The image carries the title and artwork; the exported image also bakes the type pill (top right) and full wiki slug (bottom right); the page shows the hero bare, and only the homepage marquee overlays its own dynamic info. Adom-org pages get the house wordmark and palette automatically; every other owner brings their own accent and background colors, with placement fixed for all. The floating app window is the vitrine: it auto-loads a real screenshot from the package's wiki page (screenshots/, docs/), takes a manual import, or a full-hero import (the human-ratified re-stamp path for existing heroes). The pose gate opens in verification mode: per-item checklist (title, subtitle, vitrine, type match) and a Generate that unlocks only fully confirmed. Includes vitrine bleeds, brand tile themes, gradient angles, pixel-faithful wiki marquee and index-card previews, and a component-hero mode with a photo/raytrace/phong fallback ladder.", "license": "MIT", "tags": [
public/index.html+6−9@@ -24,21 +24,17 @@ <div class="brand"> <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><line x1="3" y1="14.5" x2="21" y2="14.5"/><line x1="6" y1="17.5" x2="13" y2="17.5"/></svg> <span class="name">Hero Studio</span>- <span class="sub">· lower-third billboard lab</span> </div> <div class="vsep"></div> <div class="subject"><b id="subj-title">...</b><span class="meta" id="subj-meta"></span></div> <div class="actions">- <button class="iconbtn" id="btn-zen" data-tip="Focus mode: hide the panel so the hero gets the whole surface (Esc restores); also requests browser fullscreen">- <svg viewBox="0 0 24 24"><polyline points="9 3 3 3 3 9"/><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><polyline points="15 21 21 21 21 15"/></svg>- </button>- <button class="iconbtn" id="btn-guides" data-tip="Toggle the lower-third guide line on the canvas">- <svg viewBox="0 0 24 24"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6z"/><circle cx="12" cy="12" r="2.6"/></svg>- </button>- <button class="btn small" id="wiz-classic" data-tip="Flip between the step-by-step wizard and the classic full control panel">Classic</button>- <button class="iconbtn" id="btn-rand" data-tip="Randomize package, layout and theme">+ <button class="hbtn" id="btn-rand" data-tip="Random layout and background: a fresh starting point"> <svg viewBox="0 0 24 24"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>+ <span>Surprise me</span> </button>+ <button class="hbtn" id="btn-guides" data-tip="Show the layout guide lines on the hero">Guides</button>+ <button class="hbtn" id="btn-zen" data-tip="Hide everything but the hero (Esc restores)">Focus</button>+ <button class="hbtn" id="wiz-classic" data-tip="The full control panel, for fine-grained edits">Classic</button> </div> </header> @@ -185,6 +181,7 @@ <div id="stage"> <div id="approvedbar">+ <button id="ab-close" data-tip="Hide this banner (the approval itself is safe in the PNG and the state)">×</button> <div class="ab-head">Hero approved</div> <div class="ab-body" id="ab-body"></div> <button class="btn" id="ab-again">Generate a new version</button>
public/js/app.js+15−4@@ -88,9 +88,13 @@ function render() { // header subject $('subj-title').textContent = d.title;- $('subj-meta').textContent = mode === 'component'- ? d.type + ' · score ' + d.score + ' · MPN ' + d.mpn- : d.type + ' · score ' + d.score + ' · ' + d.stars + ' stars · ' + d.installs30d + ' installs/30d';+ // zeros are noise: show only stats that exist (a fresh page reads clean)+ const metaParts = [wireLabel(d.type)];+ if (mode === 'component' && d.mpn) metaParts.push('MPN ' + d.mpn);+ if (d.score > 0) metaParts.push('score ' + d.score);+ if (d.stars > 0) metaParts.push(d.stars + ' stars');+ if (mode !== 'component' && d.installs30d > 0) metaParts.push(d.installs30d + ' installs/30d');+ $('subj-meta').textContent = metaParts.join(' · '); $('btn-guides').classList.toggle('on', !!state.guides); // verification chrome (the pose gate sets state.verify); per-item confirms@@ -135,7 +139,7 @@ function render() { // the gate visibly CLOSES (hero-studio#5): approval is persistent state const ap = state.approved;- const approvedHere = !!(ap && ap.page === state.page);+ const approvedHere = !!(ap && ap.page === state.page) && window._abDismissed !== (ap && ap.ts); $('approvedbar').style.display = approvedHere ? 'block' : 'none'; if (approvedHere) { $('ab-body').textContent = ap.file + ', generated ' + new Date(ap.ts).toLocaleString() +@@ -1297,3 +1301,10 @@ Promise.all([ }).catch(()=>{}); }, 1000); });++/* the approved banner is dismissible per approval (Colby): the record lives+ in state + the PNG, the banner does not need to squat on the stage */+$('ab-close').addEventListener('click', () => {+ window._abDismissed = (state.approved || {}).ts;+ $('approvedbar').style.display = 'none';+});
public/studio.css+11@@ -375,3 +375,14 @@ body[data-render] #wdock, body[data-render] #weditring { display:none !important /* context previews: Review-step opt-in, not standing furniture */ body.wizard #contexts { display:none; } body.wizard.show-ctx #contexts { display:flex; }+/* header buttons: one quiet family, labels not mystery icons */+.hbtn { display:inline-flex; align-items:center; gap:6px; border:1px solid var(--border); background:transparent;+ color:var(--text-2); border-radius:8px; padding:5px 12px; font-size:12.5px; font-family:'Satoshi'; cursor:pointer;+ transition:all .15s ease; }+.hbtn:hover { border-color:var(--accent); color:var(--text); background:rgba(0,184,176,.07); }+.hbtn svg { width:13px; height:13px; stroke:currentColor; stroke-width:2; fill:none; }+.hbtn.on { border-color:var(--accent); color:var(--accent); }+#approvedbar { position:relative; }+#ab-close { position:absolute; top:8px; right:10px; background:none; border:none; color:var(--text-2);+ font-size:18px; line-height:1; cursor:pointer; padding:2px 6px; border-radius:6px; }+#ab-close:hover { color:var(--text); background:rgba(255,255,255,.06); }