← Commit history

Publish 0.8.0

Colby Knox ·44106f5325 ·2mo ago ·parent f90d66a
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, };