← Commit history

Publish 0.16.2

Colby Knox ·681a8b2db4 ·2mo ago ·parent 03a4a9f
5 files changed +34−8
SKILL.md+7
@@ -287,6 +287,13 @@ every full-import thread rediscovered it the hard way (hero-studio#32):   measures it (canvas ink measurement) in composed mode. Imports must check   it themselves; John's phone caught a 13px miss the box math called clean. - The lower ~24% is the dynamic marquee band: keep it calm.+- The Adom wordmark has ONE size on every hero (hero-studio#37): 156x35px at+  export scale, top-left at the 90px inset, adom-owned pages only. Composed+  heroes get it automatically. Full imports must NOT draw their own: leave the+  logo out of the import and set `overlay.logo=true` (the "Stamp the standard+  Adom wordmark" checkbox), which bakes the standard mark at the standard spot;+  the stamp composes, so pixel-identical passthrough is deliberately off for+  that generate. - The family preset (`hero-studio preset bridge-family`) carries the measured   constants (12cqw title, bare right-side vitrine, overlays off). 
package.json+1−1
@@ -2,7 +2,7 @@   "slug": "hero-studio",   "title": "Hero Studio",   "type": "app",-  "version": "0.16.1",+  "version": "0.16.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.16.1",+  "version": "0.16.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+20−1
@@ -444,6 +444,16 @@ body[data-mode=component] .mode-app { display:none; }    bring their own palette via the Owner brand controls; the placement grid    is the consistency we keep, not the colors. */ .wordmark.noadom { display:none; }+/* full-import wordmark stamp (#37): the ONE standard logo (same size and+   position as every composed hero: 7.8cqw at the --inset corner) baked over+   an import, so builders stop drawing their own at improvised sizes */+.fi-logo {+  position:absolute; left:var(--inset); top:var(--inset); z-index:4; display:none;+  width:7.8cqw; height:1.72cqw; background:#e6edf3;+  -webkit-mask:url('adom-logo.svg') no-repeat left center / contain;+          mask:url('adom-logo.svg') no-repeat left center / contain;+}+.hero.fullimport.stamp-logo .fi-logo { display:block; }  /* owner brand: non-adom accent + background */ #hero.branded { background: var(--brand-bg, #0d1117) !important; }@@ -659,6 +669,7 @@ body[data-render] #hero { width:1600px; max-width:none; aspect-ratio:auto; heigh     <h3>Vitrine content</h3>     <input type="file" id="vitrine-file" accept="image/*">     <label class="chk"><input type="checkbox" id="vitrine-full"> Use the imported image as the FULL hero</label>+    <label class="chk" id="stamp-logo-row" style="display:none" data-tip="Bakes the ONE standard wordmark (size and corner identical to every composed hero) over the import. Leave the logo OUT of your import and stamp it here; note this disables pixel-identical passthrough since something now composes on top."><input type="checkbox" id="chk-stamp-logo"> Stamp the standard Adom wordmark</label>     <label class="chk"><input type="checkbox" id="vitrine-cover"> Crop to fill (cover) instead of letterboxing a mismatched shot</label>     <label class="chk" data-tip="Real window screenshots already have a real titlebar; stacking the fake one above it reads as fake. Auto-off when an import is present; the CSS mock keeps it."><input type="checkbox" id="vitrine-chrome"> Draw the fake window titlebar</label>     <label class="chk"><input type="checkbox" id="chk-mqpreview"> Show the homepage-marquee overlay over the hero (judge composition and title redundancy; never exported)</label>@@ -828,6 +839,7 @@ body[data-render] #hero { width:1600px; max-width:none; aspect-ratio:auto; heigh         <span class="mqo-owner" id="mqo-owner"></span>         <div class="mqo-prompt" id="mqo-prompt" style="display:none"><span id="mqo-prompt-text"></span><button id="mqo-prompt-copy">copy</button></div>       </div>+      <span class="fi-logo" id="fi-logo" data-tip="The standard Adom wordmark (156px at export scale, standard corner), stamped over the import"></span>       <div class="pill-tr" id="pill-tr" data-tip="Type pill: bakes into the PNG top right (app / skill / hardware). Toggle under Image badges"><span class="pill app" id="l-pill">APP</span></div>       <div class="slug-br" id="slug-br" data-tip="Wiki slug: bakes into the PNG bottom right. Toggle under Image badges"><a class="l-slug" id="l-slug" target="_blank" href="#"></a></div>       <div class="lower">@@ -1119,6 +1131,12 @@ function render() {   // Every preview wordmark (hero AND the in-context mock cards) follows.   const foreign = d.owner !== 'adom';   document.querySelectorAll('.wordmark').forEach(w => w.classList.toggle('noadom', foreign));+  // #37: the standard-wordmark stamp for imports; adom-owned pages only (the+  // no-Adom-mark-for-foreign-owners policy applies to the stamp too)+  hero.classList.toggle('stamp-logo', !!(state.overlay || {}).logo && !foreign);+  $('stamp-logo-row').style.display = (simOverImport && !foreign) ? '' : 'none';+  $('chk-stamp-logo').checked = !!(state.overlay || {}).logo;+  $('fi-logo').classList.toggle('sim-fenced', simOverImport && !!(state.overlay || {}).logo);   const brand = state.brand || {};   hero.classList.toggle('branded', foreign && !!(brand.accent || brand.bg));   if (foreign && brand.accent) hero.style.setProperty('--brand-accent', brand.accent);@@ -1455,6 +1473,7 @@ $('vitrine-file').addEventListener('change', e => {   r.readAsDataURL(f); }); $('vitrine-full').addEventListener('change', e => send({ vitrine: { full: e.target.checked } }));+$('chk-stamp-logo').addEventListener('change', e => send({ overlay: { logo: e.target.checked } })); $('vitrine-cover').addEventListener('change', e => send({ vitrine: { cover: e.target.checked } })); $('vitrine-chrome').addEventListener('change', e => send({ vitrine: { chrome: e.target.checked ? 'fake' : 'none' } })); $('chk-mqpreview').addEventListener('change', e => send({ marqueePreview: e.target.checked }));@@ -1616,7 +1635,7 @@ function runLint() {   const vit2 = state.vitrine || {};   if (vit2.full && vit2.src) {     scanImportBand(vit2.src);-    items.push({ id: 'import-unlinted', level: 'warn', el: 'fullimport-img', msg: 'full-hero import: composed-layout checks cannot inspect imported pixels; verify by eye before ratifying. Doctrine still applies (#32): title set in Familjen Grotesk 700, body copy in Satoshi, optical left alignment on the ink edge, calm lower band' });+    items.push({ id: 'import-unlinted', level: 'warn', el: 'fullimport-img', msg: 'full-hero import: composed-layout checks cannot inspect imported pixels; verify by eye before ratifying. Doctrine still applies (#32/#37): title set in Familjen Grotesk 700, body copy in Satoshi, optical left alignment on the ink edge, calm lower band; wordmark at the ONE standard size (156x35px at export scale, 90px inset top-left): if the import lacks it, use Stamp the standard Adom wordmark instead of drawing your own' });     if (bandScan.status === 'done' && bandScan.texty) {       items.push({ id: 'import-band-text', level: 'warn', el: 'fullimport-img', msg: 'the imported image appears to carry text or graphics in the lower dynamic band (heuristic edge scan): the homepage overlays its own info there, keep the band calm' });     }
server.js+5−5
@@ -24,7 +24,7 @@ let state = {   theme: 'midnight',        // midnight | pcb | onion | pill | gradient   show: { logo: true, title: true, subtitle: true, description: false },   titleMode: 'image-only', // overlay-only | image-only | both; image-only per John: the overlay is dynamic data, not a second title-  overlay: { pill: true, scores: false, author: true, slug: false }, // slug defaults OFF (#29: the baked slug doubled with the marquee's live slug and was the top rejection cause); scores are live-replaced on the marquee; sim defaults them off+  overlay: { pill: true, scores: false, author: true, slug: false, logo: false }, // logo = the standard-wordmark stamp over full imports (#37, adom pages only); slug defaults OFF (#29: the baked slug doubled with the marquee's live slug and was the top rejection cause); scores are live-replaced on the marquee; sim defaults them off   brand: { accent: '', bg: '' },  // non-adom owner palette; empty = studio default   verify: false,  // the pose gate turns this on: verification chrome + checklist   confirm: { title: false, subtitle: false, vitrine: false, type: false },@@ -60,7 +60,7 @@ function noteChange(bucket, field, from, to) { const POSE_TRACKED = [   'mode', 'theme', 'layout', 'gradAngle', 'titleMode',   'show.logo', 'show.title', 'show.subtitle', 'show.description',-  'overlay.pill', 'overlay.scores', 'overlay.author', 'overlay.slug',+  'overlay.pill', 'overlay.scores', 'overlay.author', 'overlay.slug', 'overlay.logo',   'brand.accent', 'brand.bg', 'fontSize.title', 'fontSize.sub', 'fontSize.desc', 'fontSize.leading', 'fontSize.gap', 'headline',   'vitrine.chrome', 'vitrine.full', 'vitrine.cover', ];@@ -89,7 +89,7 @@ const PRESETS = {   'bridge-family': {     layout: 'showcase', theme: 'midnight', titleMode: 'image-only',     fontSize: { title: 12, sub: 2.2, desc: 1.35 },-    overlay: { pill: true, scores: false, author: false, slug: false },+    overlay: { pill: true, scores: false, author: false, slug: false, logo: false },     vitrine: { chrome: 'bare' },     guides: false,   },@@ -190,7 +190,7 @@ const server = http.createServer((req, res) => {       }       const turningFullOn = body.vitrine && body.vitrine.full && !(state.vitrine && state.vitrine.full);       if (turningFullOn && !body.overlay) {-        body.overlay = { pill: false, scores: false, author: false, slug: false };+        body.overlay = { pill: false, scores: false, author: false, slug: false, logo: false };         logLine('info', 'full import: composed overlays auto-disabled (#32); re-enable explicitly to simulate them (they render amber-fenced)');       }       const before = snapshotPose();@@ -527,7 +527,7 @@ const server = http.createServer((req, res) => {     // let finalize do the standard 2000px normalize + provenance stamp.     const ov = state.overlay || {};     const fullImport = state.vitrine && state.vitrine.full && state.vitrine.src;-    if (fullImport && !ov.pill && !ov.scores && !ov.author && !ov.slug) {+    if (fullImport && !ov.pill && !ov.scores && !ov.author && !ov.slug && !ov.logo) {       const b64 = String(state.vitrine.src).match(/^data:image\/png;base64,(.+)$/);       if (b64) {         try {