← Commit history

Publish 0.18.2

Colby Knox ·e986f8e543 ·2mo ago ·parent f785104
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">&middot; 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)">&times;</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); }