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.16.2
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 {