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.8.0
6 files changed
+96−17
SKILL.md+4−2@@ -114,8 +114,10 @@ curl -s localhost:8846/console # page console relay State fields: `page` (owner/slug), `layout`, `theme`, `gradAngle`, `titleMode`, `show.logo/title/subtitle/description`, `overlay.pill/scores/author/slug`, `brand.accent/brand.bg` (non-adom owner palette), `verify` (verification chrome),-`confirm.title/subtitle/vitrine/type` (the checklist), `vitrine.src/full`-(imported content; src is a data URL), `guides`.+`confirm.title/subtitle/vitrine/type` (the checklist), `vitrine.src/full/cover`+(imported content; src is a data URL; cover crops instead of letterboxing),+`marqueePreview` (composite today's homepage-marquee overlay for judging+composition and title redundancy; never exported), `guides`. ## Showing the studio manually
data/pages.json+13@@ -1,4 +1,17 @@ [+ {+ "type": "skill",+ "title": "Hardware Pipeline",+ "author": "",+ "score": 0,+ "stars": 0,+ "installs30d": 0,+ "brief": "Fusion CAD to a live Component page with a 3D viewer.",+ "tags": [],+ "updated_at": "2026-07-25T15:39:15.064Z",+ "owner": "adom",+ "slug": "hardware-pipeline"+ }, { "type": "app", "title": "Hero Studio",
package.json+1−1@@ -2,7 +2,7 @@ "slug": "hero-studio", "title": "Hero Studio", "type": "app",- "version": "0.7.1",+ "version": "0.8.0", "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.7.1",+ "version": "0.8.0", "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+75−12@@ -187,10 +187,11 @@ label.chk input { accent-color:var(--accent); width:14px; height:14px; cursor:po /* ---------- image-zone text column ---------- */ .textzone {- position:absolute; left:var(--inset); top:var(--inset); width:37cqw; z-index:3;+ position:absolute; left:var(--inset); top:var(--inset); width:34cqw; z-index:3;+ padding-right:1.5cqw; } .hero[data-layout=mirror] .textzone { left:auto; right:var(--inset); }-.wordmark { display:flex; align-items:center; margin-bottom:2.2cqw; }+.wordmark { display:flex; align-items:center; margin-bottom:3.2cqw; } /* the official Adom wordmark (adom-logo.svg), recolored white for dark heroes via mask */ .wordmark .wmark { display:block; width:7.8cqw; height:1.72cqw; background:#e6edf3;@@ -198,10 +199,13 @@ label.chk input { accent-color:var(--accent); width:14px; height:14px; cursor:po mask:url('adom-logo.svg') no-repeat left center / contain; } .h-title {- font-family:'Familjen Grotesk'; font-weight:700; font-size:5.3cqw; line-height:1.02;- letter-spacing:-.09cqw; color:#e6edf3; margin-bottom:1.4cqw;+ font-family:'Familjen Grotesk'; font-weight:700; font-size:6.4cqw; line-height:1.04;+ letter-spacing:-.1cqw; color:#e6edf3; margin-bottom:1.8cqw; }-.h-sub { font-size:2cqw; line-height:1.48; color:#aeb8c4; margin-bottom:1.1cqw; }+.h-title { max-width:30cqw; }+.h-sub { font-size:2.5cqw; line-height:1.45; color:#e8eef4; margin-bottom:1.2cqw;+ max-width:30cqw; text-shadow:0 1px 6px rgba(0,0,0,.45); }+.hero[data-theme=gradient] .h-sub { color:#f6fafd; } .h-sub .accent { background:var(--grad); -webkit-background-clip:text; background-clip:text; color:transparent; font-weight:500; } .h-sub b { color:#e6edf3; font-weight:500; } .h-desc { font-size:1.35cqw; line-height:1.5; color:#8b949e; max-width:30cqw; }@@ -228,7 +232,9 @@ label.chk input { accent-color:var(--accent); width:14px; height:14px; cursor:po .winbtns { margin-left:auto; display:flex; gap:.9cqw; align-items:center; } .winbtns svg { width:.72cqw; height:.72cqw; stroke:#8b949e; stroke-width:2; fill:none; } .appbody { flex:1; display:flex; min-height:0; position:relative; }-.vitrine-img { display:none; position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:2; }+.vitrine-img { display:none; position:absolute; inset:0; width:100%; height:100%;+ object-fit:contain; object-position:top center; background:#0d1117; z-index:2; }+.hero.vitrine-cover .vitrine-img { object-fit:cover; } .hero.has-vitrine .vitrine-img { display:block; } .fullimport-img { display:none; position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:1; } .hero.fullimport .fullimport-img { display:block; }@@ -246,6 +252,14 @@ label.chk input { accent-color:var(--accent); width:14px; height:14px; cursor:po .tcell { height:1.05cqw; border-radius:.25cqw; background:rgba(255,255,255,.06); } /* ---------- the lower-third textual overlay ---------- */+#mqoverlay { display:none; position:absolute; left:0; right:0; bottom:0; z-index:5;+ padding:3cqw 3cqw 2.2cqw;+ background:linear-gradient(180deg, rgba(5,9,14,0) 0%, rgba(5,9,14,.55) 35%, rgba(5,9,14,.9) 100%); }+.hero.show-mq #mqoverlay { display:block; }+#mqoverlay .mqo-badges { display:flex; gap:1cqw; align-items:center; margin-bottom:.9cqw; }+#mqoverlay .mqo-title { font-family:'Familjen Grotesk'; font-weight:700; font-size:3.2cqw; color:#fff; margin:0 0 .5cqw; }+#mqoverlay .mqo-brief { font-size:1.5cqw; color:#c9d1d9; margin:0 0 .7cqw; max-width:60cqw; }+#mqoverlay .mqo-owner { font-family:'JetBrains Mono',monospace; font-size:1.1cqw; color:#00b8b1; } .pill-tr { position:absolute; top:var(--badge-inset); right:var(--badge-inset); z-index:6; } .slug-br { position:absolute; right:var(--badge-inset); bottom:var(--badge-inset); z-index:6; } .lower {@@ -429,6 +443,7 @@ body[data-render] #toasts, body[data-render] .guide, body[data-render] #verifyba dynamic info (stats, author), and it brings its own, so the lower band sim never exports. */ body[data-render] .hero .lower { display:none !important; }+body[data-render] #mqoverlay { display:none !important; } /* The dashed YOUR LOGO box is a posing placeholder, not content. No logo posed = nothing exported, not a placeholder. */ body[data-render] .wordmark.noadom { display:none !important; }@@ -539,9 +554,13 @@ body[data-render] #hero { width:1600px; max-width:none; aspect-ratio:auto; heigh </div> <div class="hint">The shot may run under the lower third; the overlay scrim covers it. That is the new rule.</div> + <div class="hint" id="slot-size" data-tip="Click to copy the pixel size">measuring vitrine slot...</div>+ <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"><input type="checkbox" id="vitrine-cover"> Crop to fill (cover) instead of letterboxing a mismatched shot</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> <button class="btn" id="vitrine-pull">Pull real screenshot from the page</button> <button class="btn" id="vitrine-clear">Clear import</button> <div class="hint">Import a real screenshot into the vitrine instead of the CSS mock (apps show their real UI). Full-hero mode is the re-stamp path: bring in an existing hero image, ratify it here as the human, and the export carries the studio provenance stamp. The type pill and slug still bake on top.</div>@@ -608,11 +627,11 @@ body[data-render] #hero { width:1600px; max-width:none; aspect-ratio:auto; heigh <div class="bg"></div> <div class="textzone">- <div class="wordmark" id="el-logo">+ <div class="wordmark" id="el-logo" data-tip="Wordmark: adom-org pages get the house mark automatically; other owners show nothing here"> <span class="wmark"></span> </div>- <div class="h-title" id="el-title"></div>- <div class="h-sub" id="el-sub"></div>+ <div class="h-title" id="el-title" data-tip="Title (image-zone text): bakes into the PNG. Size and placement are fixed; toggle under Image-zone text"></div>+ <div class="h-sub" id="el-sub" data-tip="Value-prop subtitle (image-zone text): bakes into the PNG. Keep it one line; accent styling is automatic"></div> <div class="h-desc" id="el-desc"></div> </div> @@ -628,7 +647,7 @@ body[data-render] #hero { width:1600px; max-width:none; aspect-ratio:auto; heigh <div class="c-spec" id="c-spec"></div> </div> - <div class="window">+ <div class="window" data-tip="Vitrine: the floating app window. Content comes from your page screenshots, an import, or the mock; placement from Vitrine placement"> <div class="titlebar"> <span class="t" id="win-label"></span> <span class="winbtns">@@ -661,8 +680,14 @@ body[data-render] #hero { width:1600px; max-width:none; aspect-ratio:auto; heigh </div> <img class="fullimport-img" id="fullimport-img" alt="">- <div class="pill-tr" id="pill-tr"><span class="pill app" id="l-pill">APP</span></div>- <div class="slug-br" id="slug-br"><a class="l-slug" id="l-slug" target="_blank" href="#"></a></div>+ <div id="mqoverlay">+ <div class="mqo-badges" id="mqo-badges"></div>+ <h3 class="mqo-title" id="mqo-title"></h3>+ <p class="mqo-brief" id="mqo-brief"></p>+ <span class="mqo-owner" id="mqo-owner"></span>+ </div>+ <div class="pill-tr" id="pill-tr" data-tip="Type pill: bakes into the PNG top right (app / skill / component). 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"> <div class="lowerin"> <div class="lmain">@@ -817,8 +842,39 @@ function render() { $('v-type-label').classList.toggle('warn', !modeMatchesType); $('v-generate').disabled = !(confirm.title && confirm.subtitle && confirm.vitrine && confirm.type); + // marquee overlay preview (ask 5): today's REAL homepage treatment,+ // composited over the hero so redundancy and balance are judgeable+ hero.classList.toggle('show-mq', !!state.marqueePreview && mode === 'app');+ if (state.marqueePreview) {+ $('mqo-badges').innerHTML = '<span class="badge badge-' + d.type + '">' + d.type + '</span>' ++ (d.score > 0 ? '<span class="ts-slide-score">' + TREND13 + ' ' + d.score + '</span>' : '') ++ (d.stars > 0 ? '<span class="ts-slide-stars">' + STAR12 + ' ' + d.stars + '</span>' : '');+ $('mqo-title').textContent = d.title;+ $('mqo-brief').textContent = firstSentence(d.brief || p.brief || '', 120);+ $('mqo-owner').textContent = d.owner + '/' + d.slug;+ }+ $('chk-mqpreview').checked = !!state.marqueePreview;++ // vitrine slot size (ask 1): measure the window body, scale to export px+ requestAnimationFrame(() => {+ const win = hero.querySelector('.appbody');+ const el = $('slot-size');+ if (!win || !el) return;+ const hb = hero.getBoundingClientRect();+ const wb = win.getBoundingClientRect();+ if (hb.width < 10 || wb.width < 10) { el.textContent = 'vitrine slot hidden in this layout'; return; }+ const k = 2000 / hb.width;+ const w = Math.round(wb.width * k);+ const h = Math.round(wb.height * k);+ el.dataset.size = w + 'x' + h;+ el.textContent = 'shoot the vitrine screenshot at ' + w + 'x' + h ++ ' (aspect ' + (wb.width / wb.height).toFixed(2) + ') for this placement; click to copy';+ });+ // vitrine content: real screenshot in the window, or full-hero import const vit = state.vitrine || {};+ hero.classList.toggle('vitrine-cover', !!vit.cover);+ $('vitrine-cover').checked = !!vit.cover; hero.classList.toggle('has-vitrine', !!vit.src && !vit.full); hero.classList.toggle('fullimport', !!vit.src && !!vit.full); $('vitrine-img').src = (!vit.full && vit.src) || '';@@ -1116,6 +1172,13 @@ $('vitrine-file').addEventListener('change', e => { r.readAsDataURL(f); }); $('vitrine-full').addEventListener('change', e => send({ vitrine: { full: e.target.checked } }));+$('vitrine-cover').addEventListener('change', e => send({ vitrine: { cover: e.target.checked } }));+$('chk-mqpreview').addEventListener('change', e => send({ marqueePreview: e.target.checked }));+$('slot-size').addEventListener('click', () => {+ const sz = $('slot-size').dataset.size;+ if (sz && navigator.clipboard) navigator.clipboard.writeText(sz)+ .then(() => handleToast({ text: 'Copied ' + sz, kind: 'success', seq: Date.now() })).catch(() => {});+}); $('vitrine-clear').addEventListener('click', () => send({ vitrine: { src: '', full: false } })); $('vitrine-pull').addEventListener('click', () => { fetch('vitrine-from-page?page=' + encodeURIComponent(state.page), { method: 'POST' })
server.js+2−1@@ -21,7 +21,8 @@ let state = { 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 },- vitrine: { src: '', full: false }, // imported screenshot (data URL); full = whole-hero re-stamp+ vitrine: { src: '', full: false, cover: false }, // imported screenshot (data URL); full = whole-hero re-stamp; cover = crop-to-fill+ marqueePreview: false, // composite today's homepage-marquee overlay over the hero (preview only) gradAngle: 155, guides: true, };