Pipeline Hub

format-ui-mockup

Native-UI static ad for DawnBands, a short text-message thread (co-parent blames her, teen texts from bed, sister vent) rendered deterministically and set inside a Dawn brand frame with the band, root cause, mechanism and pill. The thread is a scene of the blame on her, never a review.

Lane: statics · not built yet: 1 (see below) · source: /Users/ayden/.openclaw/workspace/skills/format-ui-mockup

SKILL.mdbeat-sheet.mdrender-spec.mdsources.mdEvals (6)Learnings (12)

Deterministic render — spec for render_thread.py (BUILT 2026-10-06: scripts/statics/render_thread.py, see "As built" at the end)

What exists (checked 2026-10-06)

Pipeline

  1. Ref first. Source 2-3 real current-iOS Messages light-mode screenshots (Reddit r/iphone or r/ios via StealthyFetcher, per reference_scrapling_reddit_access). Save to skills/format-ui-mockup/references/ui-refs/ios-messages-light-<n>.png, cropped to the thread (no status bar, usernames removed). Measure from the ref and write down: bubble radius, padding, max bubble width (%), grey received and blue sent fills, text colors, sender-group spacing vs turn spacing, tail shape, header layout. CSS values come from the ref, never from memory.
  2. HTML. One self-contained HTML per static, generated from spec.json (lines.bullets + thread.sides + thread.contact + thread.receipt). @font-face points to SFNS.ttf (or the licensed substitute). Header = initials avatar circle + contact name + chevron only. Then bubbles, then "Delivered" right-aligned under the last me bubble. No status bar, no timestamp row, no input bar, no keyboard.
  3. Screenshot. Playwright Chromium, viewport width 390 (iPhone logical width), device_scale_factor=3, locator('#thread').screenshot() → renders/<slug>.thread.png (≈1170 px wide).
  4. Frame (PIL), 2048×2048, sand #f3ece4: - Caption 0-18%: Halant 700, slate navy #2f4a63, centered, max 2 lines, fit with _fit_font (start ~120 px). Optional gold #fdac2b underline under one word. - Thread card 20-64%: thread.png scaled so the bubble text renders ≥60 px tall at 2048; rounded 48 px corners, white card, soft shadow (blur ~30 px, 18% black). Width ≤80% of canvas, centered. If the thread at 60 px text overflows 44% height, the copy is too long: cut a bubble, never shrink the text. - Product zone 66-96%: cutout left (~28% canvas width, contact shadow per compose_c1.py); right column = lines.sub (Montserrat 600, ~58 px) then lines.mechanism (Montserrat 400, ~50 px), slate navy; pill centered at the bottom, navy #2d3452, white Montserrat 600 text = lines.pill. - Output renders/<slug>.png + renders/<slug>.render.json (fonts, sizes, card box, sha1 of the ref used).
  5. Checks before review: OCR-free text check = the HTML is the source, so diff lines against the HTML text nodes; assert bubble font px ≥60 at 2048; assert no \d{1,2}:\d{2} anywhere in the HTML.
  6. Review: python3 scripts/statics/static_review.py <B>/renders --spec <B>/spec.json. Move or delete the *.thread.png intermediates out of renders/ first: static_review reviews every png in the dir, and stage-session stages every image.

Variant B (photo overlay)

Same thread.png, composited onto a real home photo of the band (Fish-shot; the only real worn-band asset today is output/dawn-iterate-2026-10-04/statics/assets/arm-real-unlit.png). Card at 55-65% width in the photo's quiet area; its drop shadow offset points AWAY from the photo's key light (direction noted at the shoot), so the card reads as lying in that light, not pasted on. Before compositing, check the photo at 100% zoom: band edges sharp, colour true to the matte black unit. Mechanism + pill in a sand bar across the bottom 22%, caption in a sand bar at the top. The band must be lying flat or worn naturally: static_review failed every non-flat placement on 10-04 (illogical_product_placement).

Build estimate

~150 lines (HTML template + Playwright + PIL frame), one session, $0. Accept test: render the §Structure example from beat-sheet.md with placeholder-free real VOC, side-by-side with the iOS ref, Fish's eye, then static_review PASS/WARN.

As built (2026-10-06)