app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge — multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
#!/usr/bin/env python3
"""Compose the kicad-bridge hero in the Adom bridge-family style (Pup/Fusion/KiCad).
Clones the Pup hero's measured geometry exactly, drops in a fresh vitrine capture,
and ASSERTS the ink alignment before saving. See SKILL.md in this directory for the
full workflow (capture -> compose -> Hero Studio gate -> apply).
Usage:
python3 compose_hero.py <vitrine-capture.png> <out.png>
The vitrine capture should be a real KiCad window screenshotted at exactly
1030x1025 (resize it first with desktop_set_window_bounds {"w":1030,"h":1025}).
Anything else gets LANCZOS-scaled, which softens UI text.
"""
import sys
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont
HERE = Path(__file__).parent
PUP = HERE / "reference-pup-hero.png" # Pup's true published asset (hero-v9)
FONTS = HERE.parent / "kicad-tour" / "hero" / "fonts"
W, H = 2000, 1250
TITLE = "KiCad"
SUB_1A, SUB_1B = "Your real KiCad, ", "driven by AI"
SUB_2A, SUB_2B = "entirely in the ", "background." # accent word(s) in teal
TEAL = (0, 230, 220)
# Measured off Pup's asset — every bridge hero shares these (see SKILL.md "Layout numbers"):
LOGO_CROP, LOGO_AT = (60, 48, 335, 130), (60, 48) # ADOM wordmark, verbatim from Pup
PILL_CROP, PILL_AT = (1796, 36, 1964, 108), (1796, 36) # BRIDGE pill, verbatim from Pup
INK_EDGES = (79, 83, 83) # logo / title / subtitle left ink edges (title SHARES the
# subtitle's line — John's ruling; Pup's own hero has the title
# 14px right of its subtitle, do NOT copy that stagger)
CAP_TOP = 263 # title cap line
SUB_Y1, SUB_Y2 = 501, 565 # subtitle line tops
VITRINE_AT, VITRINE_W, VITRINE_H = (920, 150), 1030, 1025
RADIUS, SHADOW_BLUR, SHADOW_DY = 24, 28, 12
def ink_edges(im):
a = np.array(im).astype(int)
diff = np.abs(a - a[700, 400]).sum(axis=2) > 45
def left(y0, y1):
for x in range(40, 900):
if diff[y0:y1, x].sum() > 2:
return x
return left(48, 132), left(263, 460), left(495, 640)
def compose(title_dx, sub_dx, pup, base, tf, sf):
hero = base.copy()
hero.paste(pup.crop(LOGO_CROP), LOGO_AT)
hero.paste(pup.crop(PILL_CROP), PILL_AT)
d = ImageDraw.Draw(hero)
x, y = title_dx, CAP_TOP - tf.getbbox(TITLE[0])[1]
for ch in TITLE: # -5px tracking, per the studio's .h-title letter-spacing
d.text((x, y), ch, font=tf, fill=(230, 237, 243))
x += d.textlength(ch, font=tf) - 5
scol = (232, 238, 244)
w = d.textlength(SUB_1A, font=sf)
d.text((sub_dx, SUB_Y1), SUB_1A, font=sf, fill=scol)
d.text((sub_dx + w, SUB_Y1), SUB_1B, font=sf, fill=scol)
w = d.textlength(SUB_2A, font=sf)
d.text((sub_dx, SUB_Y2), SUB_2A, font=sf, fill=scol)
d.text((sub_dx + w, SUB_Y2), SUB_2B, font=sf, fill=TEAL)
return hero
def main(capture_path, out_path):
if not PUP.exists():
sys.exit(f"missing {PUP}\nFetch it: adom-wiki repo download pup-bridge --extract /tmp/pup-repo\n"
f"then copy hero-v9.png here. (Do NOT use `repo show` — it mangles binaries.)")
pup = Image.open(PUP).convert("RGB")
pa = np.array(pup)
# Background: bilinear gradient through Pup's four corner colors.
tl, tr = pa[6, 6].astype(float), pa[6, W - 7].astype(float)
bl, br = pa[H - 7, 6].astype(float), pa[H - 7, W - 7].astype(float)
xs = np.linspace(0, 1, W)[None, :, None]
ys = np.linspace(0, 1, H)[:, None, None]
base = Image.fromarray((tl * (1 - xs) * (1 - ys) + tr * xs * (1 - ys)
+ bl * (1 - xs) * ys + br * xs * ys).astype(np.uint8))
# Title: Familjen Grotesk BOLD (brand doctrine, hero-studio .h-title = 700).
# 242px makes cap height match Pup's measured 155px.
tf = ImageFont.truetype(str(FONTS / "FamiljenGrotesk-Bold.ttf"), 242)
sf = ImageFont.truetype(str(FONTS / "Satoshi-Regular.ttf"), 52)
# Calibrate: fonts lie about left bearings (Familjen reported 0 for K, drew 16).
# Render, measure actual ink, correct, re-render, assert.
t_dx, s_dx = 98, 84
lo, ti, su = ink_edges(compose(t_dx, s_dx, pup, base, tf, sf))
t_dx += INK_EDGES[1] - ti
s_dx += INK_EDGES[2] - su
hero = compose(t_dx, s_dx, pup, base, tf, sf)
assert ink_edges(hero) == INK_EDGES, f"alignment drifted: {ink_edges(hero)} != {INK_EDGES}"
# Vitrine: real window capture in Pup's exact floating-window rect.
win = Image.open(capture_path).convert("RGB")
if win.size != (VITRINE_W, VITRINE_H):
print(f"WARNING: capture is {win.size}, scaling to {(VITRINE_W, VITRINE_H)} (softens text; "
f"prefer resizing the real window before capture)")
win = win.resize((VITRINE_W, VITRINE_H), Image.LANCZOS)
mask = Image.new("L", (VITRINE_W, VITRINE_H), 0)
ImageDraw.Draw(mask).rounded_rectangle((0, 0, VITRINE_W - 1, VITRINE_H - 1), radius=RADIUS, fill=255)
sh = Image.new("L", (W, H), 0)
vx, vy = VITRINE_AT
ImageDraw.Draw(sh).rounded_rectangle((vx, vy + SHADOW_DY, vx + VITRINE_W, vy + SHADOW_DY + VITRINE_H),
radius=RADIUS, fill=110)
sh = sh.filter(ImageFilter.GaussianBlur(SHADOW_BLUR))
hero = Image.composite(Image.new("RGB", (W, H), (0, 0, 0)), hero, sh)
hero.paste(win, VITRINE_AT, mask)
hero.save(out_path)
print(f"saved {out_path} (alignment {INK_EDGES} verified)")
if __name__ == "__main__":
if len(sys.argv) != 3:
sys.exit(__doc__)
main(sys.argv[1], sys.argv[2])