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
0.21.2: render view is read-only, slotSize on own change only, real render errors (Ray, #57 / PR #1); test:render; brand test reads the package version
6 files changed
+13−2
SKILL.md+7@@ -556,6 +556,13 @@ Tests: `npm run test:brand` (node test/brand.mjs) runs every status, the can_edit refusal, the confirm push body, and the four provenance fields against a stub registry, so nothing here is verified by writing to a real owner's guide.+`npm run test:render` (node test/render-view.mjs) starts a real server, opens+a surface at one width and the render view at another, and asserts the render+view POSTs no state, reaches networkidle2, and that a renderer's error line is+reported instead of Node's version footer (hero-studio#57). The render view+(`?render=1`) is a read-only consumer of the state it loaded: it never posts+state and opens no event stream, so Generate and compose are unaffected by+whichever studio tabs are open. ## Brand doctrine (what makes a hero look like the family)
package.json+2−1@@ -2,7 +2,7 @@ "slug": "hero-studio", "title": "Hero Studio", "type": "app",- "version": "0.21.1",+ "version": "0.21.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. Every hero wears its PAGE OWNER's brand guide (<owner>/brand): colors, type, logo and background themes, Adom's included; an unresolved guide holds unbranded rather than borrowing Adom's look. Placement is fixed for all. The floating app window is the showcase: 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, showcase, type match) and a Generate that unlocks only fully confirmed. Includes showcase 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": [@@ -98,3 +98,4 @@ "screenshots/hero.png" ] }+
public/js/app.js+1@@ -1669,3 +1669,4 @@ $('ab-close').addEventListener('click', () => { window._abDismissed = (state.approved || {}).ts; $('approvedbar').style.display = 'none'; });+
server.js+1@@ -1706,3 +1706,4 @@ server.listen(PORT, () => { writeRegistry(); console.log('hero-studio on ' + PORT + ' (thread "' + AI_THREAD + '", ttl ' + (KEEP_ALIVE ? 'off' : TTL_HOURS + 'h') + ')'); });+
test/brand.mjs+1−1@@ -300,7 +300,7 @@ async function main() { const meta = await readPngComment(path.join(ROOT, 'generated', gen21.file)); eq(' the stamp says confirmed (post-push), not starter', meta && meta.brand_status, 'confirmed'); eq(' branding is the owner', meta && meta.branding, 'starterco');- eq(' and the stamp is versioned 0.21.0 so the wiki judges it', meta && meta.version, '0.21.0');+ eq(' and the stamp carries this package version so the wiki judges it', meta && meta.version, JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version); try { fs.unlinkSync(path.join(ROOT, 'generated', gen21.file)); } catch {} } await pose('lockedco/brand');
test/render-view.mjs+1@@ -96,3 +96,4 @@ try { } console.log('\n' + pass + ' passed, ' + fails.length + ' failed'); process.exit(fails.length ? 1 : 0);+