Loading...
Loading...
Maintain the LovStudio website's partners section AND align partner logo rows on event posters / hero strips: scrape brand logos from homepages, normalize to a 240px-tall content canvas (retina-ready), rasterize SVGs via rsvg-convert before normalizing (so SVG viewBox padding gets cropped), strip embedded background rects from icon-style SVGs, composite icon + wordmark when only an icon is available (using brand fonts), wrap logos in a fixed-size grid box (96×30 with subtle border) for stable matrix layouts, replace existing logos with user-provided files, append new partners to the PARTNERS array with i18n taglines across zh-CN/en/ja/th, and audit the section for dead URLs / missing files / missing translations. Also handles cross-asset visual height parity (multi-logo strips on dark backgrounds, "logo 不等高", unified-color filter recipe). Trigger when the user mentions "合作伙伴", "partners", "trusted by", "新增 logo", "标准化 logo", "替换 logo", "审计合作伙伴", "维护合作伙伴", "logo 不一样高", "logo 对齐", "logo 大小不一致", "logo 颜色不统一", "logo 不清晰", "logo 糊了", "矩阵格子", "等宽 box", "图标加文字", "compose wordmark".
npx skill4agent add lovstudio/skills lovstudio-maintain-partners/Users/mark/lovstudio/coding/webapp/(main)/(home)/WorkshopDispatch.tsxPARTNERS: Partner[]public/partners/<slug>/logo.pngsrc/i18n/messages/{zh-CN,en,ja,th}.jsondispatch.partner*Taglineheight: 32pxscale: 2--show-name<品牌名> · <一句话定位>AskUserQuestionpython3 ~/.claude/skills/lovstudio-maintain-partners/scripts/scrape_logo.py \
--url <URL> --download /tmp/<slug>.png--jspython3 ~/.claude/skills/lovstudio-maintain-partners/scripts/normalize_logo.py \
--src /tmp/<slug>.png \
--dst /Users/mark/lovstudio/coding/web/public/partners/<slug>/logo.png \
--invert autopython3 ~/.claude/skills/lovstudio-maintain-partners/scripts/add_partner.py \
--name "<显示名>" --href "<URL>" \
--logo "/partners/<slug>/logo.png" \
--key partner<Slug>Tagline \
--zh "..." --en "..." --ja "..." --th "..." \
[--show-name]python3 ~/.claude/skills/lovstudio-maintain-partners/scripts/normalize_logo.py \
--src public/partners/<slug>/logo.png \
--dst public/partners/<slug>/logo.png \
--invert auto~/lovstudio/partners/<品牌>/<file>python3 ~/.claude/skills/lovstudio-maintain-partners/scripts/normalize_logo.py \
--src "<user-provided path>" \
--dst /Users/mark/lovstudio/coding/web/public/partners/<slug>/logo.png \
--invert autopython3 ~/.claude/skills/lovstudio-maintain-partners/scripts/audit_partners.py
# add --probe to also HTTP-check every href (slow, requires proxy)heightheight: 24pxscale: 2--invert offfor f in lujiazui juanyi citic-bookstore citic-thinker-lab; do
python3 ~/.claude/skills/lovstudio-maintain-partners/scripts/normalize_logo.py \
--src ~/lovstudio/partners/<brand>/<file>.png \
--dst <event-assets>/partners/$f.png \
--height 240 --invert auto
donenormalize_logo.pyrsvg-convert -h 720 brand.svg -o /tmp/brand-raw.png # 3× of 240
python3 ~/.claude/skills/lovstudio-maintain-partners/scripts/normalize_logo.py \
--src /tmp/brand-raw.png --dst <event-assets>/partners/brand.png \
--height 240 --invert offrsvg-convertlibrsvgbrew install librsvgfind-logobrightness(0) invert(1)# Drop the outer <rect fill="#000"...> wrapper
sed -E 's|<rect[^/]*fill="#0+"[^/]*/>||' brand.svg > /tmp/brand-clean.svg
rsvg-convert -h 720 /tmp/brand-clean.svg -o /tmp/brand-raw.png<span class="ps-logo-box"><img src="..." class="ps-logo"></span>.ps-logo-box {
width: 96px; height: 30px; /* fixed grid cell */
display: inline-flex;
align-items: center; justify-content: center;
border: 1px solid rgba(255,255,255,0.10);
border-radius: 4px;
padding: 3px 6px;
box-sizing: border-box;
}
.ps-logo { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; }.ps-logo { filter: brightness(0) invert(1) opacity(0.88); }
/* logos already white-on-transparent — opt out of inversion */
.ps-logo.ps-logo-original { filter: opacity(0.88); }brightness(0)invert(1).ps-logo-originalfrom PIL import Image, ImageDraw, ImageFont, ImageOps
# 1. rasterize cleaned SVG, invert white→black so default filter works
icon = Image.open('/tmp/brand-icon.png').convert('RGBA')
r, g, b, a = icon.split()
inv = Image.merge('RGB', (ImageOps.invert(r), ImageOps.invert(g), ImageOps.invert(b)))
icon = Image.merge('RGBA', (*inv.split(), a))
icon = icon.crop(icon.getbbox())
target_h = 240
icon = icon.resize((int(icon.width * target_h / icon.height), target_h), Image.LANCZOS)
# 2. render wordmark in brand font (find-logo bundles fonts/ when found)
font = ImageFont.truetype('partners/<brand>/fonts/<Family>.ttf', 150)
# 3. compose icon + gap + text on transparent canvasbrightness(0) invert(1)<brand>/fonts/.ps-logo-juanyi { height: 26px }| Flag | Default | Notes |
|---|---|---|
| required | input image (PNG/JPG/rasterized SVG) |
| required | output PNG path; parent dirs auto-created |
| | target content height. Use 240 for retina poster export ( |
| | |
| Flag | Default | Notes |
|---|---|---|
| required | brand homepage |
| off | use Playwright headless Chromium for SPAs |
| off | save first candidate to this path |
| Flag | Notes |
|---|---|
| display name (CJK ok) |
| brand URL |
| path under |
| i18n key, e.g. |
| tagline strings (all required) |
| render name next to icon for narrow logos |
| Flag | Notes |
|---|---|
| HTTP-probe every href (slow, needs proxy env vars) |
export https_proxy=http://127.0.0.1:7890 \
http_proxy=http://127.0.0.1:7890 \
all_proxy=socks5://127.0.0.1:7891scrape_logo.pyaudit_partners.pycurlpip install Pillow --break-system-packages
# Optional, for JS-rendered SPAs:
pip install playwright --break-system-packages && playwright install chromium