← Commit history

Publish 0.19.2

Colby Knox ·87a5d288b7 ·2mo ago ·parent af711f8
6 files changed +62−8
package.json+1−1
@@ -2,7 +2,7 @@   "slug": "hero-studio",   "title": "Hero Studio",   "type": "app",-  "version": "0.19.1",+  "version": "0.19.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.19.1",+  "version": "0.19.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+1
@@ -348,6 +348,7 @@ </div>  <div id="wdock">+  <div id="wdock-grip" data-tip="Drag to resize the wizard; double-click resets"></div>   <div id="wdockintro">You're designing the <b>hero image</b> for <span class="ref" id="wintro-ref"></span>, the picture at the top of its wiki page. Everything updates live above. Nothing is saved until you hit Generate.</div>   <div id="wrailwrap"><div id="wsteprail"></div></div>   <div id="wdockhead">
public/js/app.js+5−2
@@ -504,8 +504,11 @@ function showGenerated(g) { } /* fires for local clicks AND for API-triggered generates (SSE or poll), deduped by seq */ function handleGen(g) {-  if (g && g.ok && window.wizardOnGenerated) { try { window.wizardOnGenerated(); } catch {} }-  if (g && g.ok && g.seq > seenGenSeq) { seenGenSeq = g.seq; showGenerated(g); }+  if (g && g.ok && g.seq > seenGenSeq) {+    seenGenSeq = g.seq;+    if (window.wizardOnGenerated) { try { window.wizardOnGenerated(); } catch {} }+    showGenerated(g);+  } } $('gen-close').addEventListener('click', e => { e.preventDefault(); $('genmodal').classList.remove('show'); }); $('genmodal').addEventListener('click', e => { if (e.target === $('genmodal')) $('genmodal').classList.remove('show'); });
public/js/wizard.js+39−4
@@ -433,12 +433,12 @@ function wizRenderReview(host) {       const q = document.createElement('span');       q.className = 'q'; q.textContent = typeof c.q === 'function' ? c.q() : c.q;       const ok = document.createElement('button');-      ok.className = 'btn small';-      ok.textContent = 'Looks good';+      ok.className = 'btn small' + (state.confirm[c.key] ? ' okon' : '');+      ok.textContent = (state.confirm[c.key] ? '\u2713 ' : '') + 'Looks good';       ok.addEventListener('click', () => wizSend({ confirm: { [c.key]: true }, flags: { [c.key]: false } }));       const fl = document.createElement('button');-      fl.className = 'btn small';-      fl.textContent = 'Needs fixing';+      fl.className = 'btn small' + (state.flags[c.key] ? ' flagon' : '');+      fl.textContent = (state.flags[c.key] ? '\u2713 ' : '') + 'Needs fixing';       fl.addEventListener('click', () => wizSend({ flags: { [c.key]: true }, confirm: { [c.key]: false } }));       row.append(q, ok, fl);       checks.appendChild(row);@@ -632,3 +632,38 @@ setTimeout(() => {   if (state && state.verify) wizStep = 'showcase';   wizardRender(); }, 400);++/* draggable dock height (Colby): grab the top edge; remembered per browser */+(() => {+  const grip = document.getElementById('wdock-grip');+  const dock = document.getElementById('wdock');+  if (!grip || !dock) return;+  const KEY = 'hs-dock-h';+  const saved = localStorage.getItem(KEY);+  if (saved) dock.style.setProperty('--dock-h', saved + 'px');+  let startY = 0, startH = 0, dragging = false;+  grip.addEventListener('pointerdown', (e) => {+    dragging = true; startY = e.clientY; startH = dock.getBoundingClientRect().height;+    grip.setPointerCapture(e.pointerId);+    e.preventDefault();+  });+  grip.addEventListener('pointermove', (e) => {+    if (!dragging) return;+    const h = Math.max(150, Math.min(window.innerHeight * 0.7, startH + (startY - e.clientY)));+    dock.style.setProperty('--dock-h', h + 'px');+  });+  const drop = () => {+    if (!dragging) return;+    dragging = false;+    const h = Math.round(dock.getBoundingClientRect().height);+    localStorage.setItem(KEY, String(h));+    positionWizRing();+  };+  grip.addEventListener('pointerup', drop);+  grip.addEventListener('pointercancel', drop);+  grip.addEventListener('dblclick', () => {+    localStorage.removeItem(KEY);+    dock.style.removeProperty('--dock-h');+    positionWizRing();+  });+})();
public/studio.css+15
@@ -388,3 +388,18 @@ body.wizard.show-ctx #contexts { display:flex; } #ab-close:hover { color:var(--text); background:rgba(255,255,255,.06); } /* finishing + package cards: one size, aligned */ .wiz-finish .card { width:200px; min-height:92px; justify-content:center; }+/* verdicts you can see from across the room */+.rv-check .btn.okon { background:rgba(63,185,80,.18); border-color:var(--green); color:var(--green); font-weight:600; }+.rv-check .btn.flagon { background:rgba(248,81,73,.16); border-color:#f85149; color:#f85149; font-weight:600; }+/* scrollable rows SAY they scroll: thin themed scrollbar on the card row */+#wcards { scrollbar-width:thin; scrollbar-color:var(--border) transparent; padding-bottom:6px; }+#wcards::-webkit-scrollbar { display:block; height:7px; }+#wcards::-webkit-scrollbar-thumb { background:var(--border); border-radius:4px; }+#wcards::-webkit-scrollbar-thumb:hover { background:var(--text-3); }+#wcards::-webkit-scrollbar-track { background:transparent; }+/* draggable dock: the grab bar resizes it; double-click resets */+#wdock { height:var(--dock-h, min(44vh, 330px)); position:relative; }+#wdock-grip { position:absolute; top:-1px; left:0; right:0; height:8px; cursor:ns-resize; z-index:5; }+#wdock-grip::after { content:''; position:absolute; left:50%; top:2px; transform:translateX(-50%);+  width:44px; height:4px; border-radius:999px; background:var(--border); transition:background .15s; }+#wdock-grip:hover::after { background:var(--accent); }