{"id":"eb5ccd5b-65f4-42da-bf53-7ef70a19d596","shortId":"A3bpyZ","kind":"skill","title":"lovstudio-maintain-partners","tagline":"Maintain the LovStudio website's partners section AND align partner logo rows on event posters / hero strips: reuse lovstudio-find-logo for brand logo discovery, normalize collected logos to a 240px-tall content canvas (retina-ready), rasterize SVGs via rsvg-convert before normal","description":"# maintain-partners — LovStudio 合作伙伴板块维护\n\nMaintains the configured website repo. Resolve the path from `--repo`,\n`LOVSTUDIO_MAINTAIN_PARTNERS_SITE_ROOT`, or the shared user profile. The partners\nstrip usually lives in `app/(main)/(home)/PartnersGrid.tsx` as a `PARTNERS:\nPartner[]` array; older sites may still keep it in\n`app/(main)/(home)/WorkshopDispatch.tsx`. Logos live in\n`public/partners/<slug>/logo.png`; taglines in\n`src/i18n/messages/{zh-CN,en,ja,th}.json` under `dispatch.partner*Tagline`.\n\n## User Configuration\n\nBefore touching files, resolve:\n\n```bash\nSKILL_ROOT=\"${LOVSTUDIO_SKILLS_INSTALL_DIR:?Set LOVSTUDIO_SKILLS_INSTALL_DIR}\"\nSKILL_DIR=\"${SKILL_DIR:-$SKILL_ROOT/lovstudio-maintain-partners}\"\nWEB_ROOT=\"${LOVSTUDIO_MAINTAIN_PARTNERS_SITE_ROOT:?Set this or pass --repo}\"\nPARTNERS_TSX=\"${LOVSTUDIO_MAINTAIN_PARTNERS_FILE:-app/(main)/(home)/PartnersGrid.tsx}\"\n```\n\nUse this precedence for the website root:\n\n1. Explicit `--repo <path>` on `add_partner.py` / `audit_partners.py`.\n2. `LOVSTUDIO_MAINTAIN_PARTNERS_SITE_ROOT`.\n3. Shared profile JSON at\n   `${LOVSTUDIO_SKILLS_PROFILE:-$HOME/.lovstudio/skills/profile.json}`.\n\n`LOVSTUDIO_WEB_ROOT` and `PARTNERS_SITE_ROOT` are accepted as legacy aliases,\nbut should not be the public contract for reusable skills.\n\nUse this precedence for the partners TSX file:\n\n1. Explicit `--partners-file <path>`.\n2. `LOVSTUDIO_MAINTAIN_PARTNERS_FILE`.\n3. Shared profile keys `sites.partners_file`, `lovstudio.partners_file`,\n   `partners.file`, or `workspace.partners_file`.\n4. `app/(main)/(home)/PartnersGrid.tsx`, then legacy\n   `app/(main)/(home)/WorkshopDispatch.tsx`.\n\n`LOVSTUDIO_PARTNERS_FILE` and `PARTNERS_FILE` are accepted as legacy aliases,\nbut should not be the public contract for reusable skills.\n\nFor details and supported profile keys, read `references/user-config.md`.\n\n## Skill Dependencies\n\n- `lovstudio-find-logo` is required for all logo discovery. This skill must\n  not scrape homepages itself or keep a separate fallback crawler.\n- Use the `depends_on` frontmatter field to declare skill-level dependencies.\n  This mirrors the `depends_on` field in `lovstudio-general-skills/skills.yaml`;\n  unknown frontmatter keys are expected to be ignored by agents that do not\n  consume dependency metadata.\n\n## When to Use\n\n- User asks to **add** one or more new partners (with or without a logo URL).\n- User asks to **standardize / normalize** a logo (sizing wrong, white-on-white, etc.).\n- User provides a local file and asks to **replace** an existing partner's logo.\n- User asks to **audit** the partners section before a release.\n\n## Standards\n\n- Logo canvas: **80px** content height for the website partners strip\n  (light grayscale, CSS `height: 32px` ≈ 2.5× density, sharp enough),\n  **240px** for event posters or any retina export at `scale: 2` or higher.\n- For white-on-transparent logos: invert (full or selective) so they show on\n  the light grayscale strip.\n- For icon-only logos < ~40px wide after normalization: pass `--show-name`\n  when adding so the brand name renders next to the icon.\n- Tagline format: `<品牌名> · <一句话定位>` in Chinese; mirror style in en/ja/th.\n\n## Workflow\n\n### Op 1: Add a new partner\n\n1. Ask the user for the brand name + homepage URL via `AskUserQuestion`.\n2. Collect the logo with `lovstudio-find-logo`:\n   ```bash\n   python3 \"$SKILL_ROOT/lovstudio-find-logo/scripts/find_logo.py\" \\\n     --name \"<显示名>\" --url <URL> --slug <slug> --json\n   ```\n   Use the archived primary asset under\n   `~/.lovstudio/logo-collection/<slug>/logo.<ext>`. If `find_logo.py` returns\n   no candidates, stop and ask the user for a better official URL / press-kit\n   URL, then rerun `find_logo.py`. Do not call a local scraper from this skill.\n3. Visually verify the archived primary asset before normalizing.\n4. If the primary asset is SVG, rasterize it before normalization:\n   ```bash\n   rsvg-convert -h 240 ~/.lovstudio/logo-collection/<slug>/logo.svg \\\n     -o /tmp/<slug>-raw.png\n   ```\n   Use the rasterized `/tmp/<slug>-raw.png` as `--src`. For non-SVG sources,\n   use the archived primary asset directly.\n5. Normalize:\n   ```bash\n   python3 \"$SKILL_DIR/scripts/normalize_logo.py\" \\\n     --src <archived-or-rasterized-logo> \\\n     --dst \"$WEB_ROOT/public/partners/<slug>/logo.png\" \\\n     --invert auto\n   ```\n6. Read the normalized PNG to confirm it's visible (not white-on-white).\n7. Append to PARTNERS + all 4 locale JSONs:\n   ```bash\n   python3 \"$SKILL_DIR/scripts/add_partner.py\" \\\n     --repo \"$WEB_ROOT\" \\\n     --partners-file \"$PARTNERS_TSX\" \\\n     --name \"<显示名>\" --href \"<URL>\" \\\n     --logo \"/partners/<slug>/logo.png\" \\\n     --key partner<Slug>Tagline \\\n     --category community \\\n     --zh \"...\" --en \"...\" --ja \"...\" --th \"...\" \\\n     [--show-name]\n   ```\n\n### Op 2: Normalize an existing logo\n\n```bash\npython3 \"$SKILL_DIR/scripts/normalize_logo.py\" \\\n  --src public/partners/<slug>/logo.png \\\n  --dst public/partners/<slug>/logo.png \\\n  --invert auto\n```\n\nRe-read after to verify.\n\n### Op 3: Replace logo from a user-provided file\n\nAsk for the source file path directly, or read it from the user's configured\nworkspace/profile. Do not assume a private partners folder.\n\n```bash\npython3 \"$SKILL_DIR/scripts/normalize_logo.py\" \\\n  --src \"<user-provided path>\" \\\n  --dst \"$WEB_ROOT/public/partners/<slug>/logo.png\" \\\n  --invert auto\n```\n\nJPEG inputs auto-strip near-white background to transparent before crop.\n\n### Op 4: Audit\n\n```bash\npython3 \"$SKILL_DIR/scripts/audit_partners.py\" \\\n  --repo \"$WEB_ROOT\" \\\n  --partners-file \"$PARTNERS_TSX\"\n# add --probe to also HTTP-check every href (slow, requires proxy)\n```\n\nReports: missing logo files, missing i18n keys per locale, dead URLs.\n\n### Op 5: Align a row of partner logos (cross-asset visual height parity)\n\n**When**: putting 3+ partner logos in a single horizontal strip and they look\ndifferent sizes despite having the same CSS `height`. Common in event posters,\nhero sections, \"联办 / co-host\" rows.\n\n**Root cause**: each source file has different internal padding (designer\ncanvas margin), so two PNGs both set to `height: 24px` render at different\n*visible* heights because their content occupies different fractions of the\ncanvas. Per-logo CSS height tweaks based on eyeballed content ratios are\nunstable—different displays / scaling will diverge again.\n\n**Reliable fix — trim at file level, uniform CSS box**:\n\n1. **Normalize every logo** to identical content height. Default raster file\n   target is **240px** (3× density for retina poster export at `scale: 2`;\n   80px gives only 1.7× and looks soft after PNG export). Use `--invert off`\n   if the source is already light-on-transparent (don't double-invert):\n   ```bash\n   for f in lujiazui juanyi citic-bookstore citic-thinker-lab; do\n     python3 \"$SKILL_DIR/scripts/normalize_logo.py\" \\\n       --src \"<configured-partners-source>/<brand>/<file>.png\" \\\n       --dst <event-assets>/partners/$f.png \\\n       --height 240 --invert auto\n   done\n   ```\n   **Always normalize from the original source**, never from a previously\n   normalized 80px file (upscaling = blurry — burned by this on juanyi).\n\n2. **For SVG sources, rasterize first**. `normalize_logo.py` operates on\n   raster pixels and **cannot crop SVG viewBox padding**. Without this step\n   an SVG always renders smaller than rasterized PNG siblings:\n   ```bash\n   rsvg-convert -h 720 brand.svg -o /tmp/brand-raw.png   # 3× of 240\n   python3 \"$SKILL_DIR/scripts/normalize_logo.py\" \\\n     --src /tmp/brand-raw.png --dst <event-assets>/partners/brand.png \\\n     --height 240 --invert off\n   ```\n   `rsvg-convert` ships with `librsvg` (`brew install librsvg`).\n\n3. **For SVG with embedded background rect** (icon wrapped in a black/colored\n   rounded square — common in app-icon-style SVGs from `find-logo`), strip\n   the background before rasterizing, otherwise filter `brightness(0)\n   invert(1)` flattens it into a solid white block that hides the icon:\n   ```bash\n   # Drop the outer <rect fill=\"#000\"...> wrapper\n   sed -E 's|<rect[^/]*fill=\"#0+\"[^/]*/>||' brand.svg > /tmp/brand-clean.svg\n   rsvg-convert -h 720 /tmp/brand-clean.svg -o /tmp/brand-raw.png\n   ```\n\n4. **Wrap each logo in a fixed-size box** (recommended over auto-width flex):\n   ```html\n   <span class=\"ps-logo-box\"><img src=\"...\" class=\"ps-logo\"></span>\n   ```\n   ```css\n   .ps-logo-box {\n     width: 96px; height: 30px;             /* fixed grid cell */\n     display: inline-flex;\n     align-items: center; justify-content: center;\n     border: 1px solid rgba(255,255,255,0.10);\n     border-radius: 4px;\n     padding: 3px 6px;\n     box-sizing: border-box;\n   }\n   .ps-logo { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; }\n   ```\n   Fixed boxes give a stable matrix look — narrow logos (icon-only) and wide\n   logos (icon + wordmark) all occupy the same footprint, with the asset\n   scaled to fit. Auto-width flex (the older recipe) makes per-row total\n   widths unpredictable as logos get added/removed.\n\n5. **Dark-background unification** — when the row sits on a dark canvas\n   (e.g. event poster), most brand logos are designed for white BG and look\n   inconsistent (some have black text, some have brand-colored marks). The\n   stable recipe:\n   ```css\n   .ps-logo { filter: brightness(0) invert(1) opacity(0.88); }\n   /* logos already white-on-transparent — opt out of inversion */\n   .ps-logo.ps-logo-original { filter: opacity(0.88); }\n   ```\n   `brightness(0)` flattens all colors to black, then `invert(1)` produces\n   uniform white at the configured opacity. The `.ps-logo-original` escape\n   hatch is for source files that are already white-on-transparent (white\n   SVG variants from a brand kit) so you don't double-process them into\n   invisible black-on-dark.\n\n6. **Icon-only SVG → composite icon + wordmark** — if the brand SVG only\n   has an icon (no \"BrandName\" wordmark beside it), don't ship just the icon\n   in a 96×30 box (it'll look like an unidentified mark). Compose the\n   wordmark with PIL using the brand's own font when possible:\n\n   ```python\n   from PIL import Image, ImageDraw, ImageFont, ImageOps\n   # 1. rasterize cleaned SVG, invert white→black so default filter works\n   icon = Image.open('/tmp/brand-icon.png').convert('RGBA')\n   r, g, b, a = icon.split()\n   inv = Image.merge('RGB', (ImageOps.invert(r), ImageOps.invert(g), ImageOps.invert(b)))\n   icon = Image.merge('RGBA', (*inv.split(), a))\n   icon = icon.crop(icon.getbbox())\n   target_h = 240\n   icon = icon.resize((int(icon.width * target_h / icon.height), target_h), Image.LANCZOS)\n   # 2. render wordmark in brand font (find-logo bundles fonts/ when found)\n   font = ImageFont.truetype('partners/<brand>/fonts/<Family>.ttf', 150)\n   # 3. compose icon + gap + text on transparent canvas\n   ```\n   The PNG goes through the same `brightness(0) invert(1)` filter as raster\n   logos — match colors with all other entries automatically. Use the brand's\n   own font (often shipped under `<brand>/fonts/` by the find-logo skill);\n   fall back to system SF / Helvetica only if no brand font is available.\n\n7. **Anti-pattern — do not** try to fix alignment by setting per-logo\n   heights like `.ps-logo-juanyi { height: 26px }`. It's brittle (every new\n   logo needs another magic number), unstable across browsers, and breaks\n   the moment a designer reships the source asset with different padding.\n\n## CLI Reference\n\n### normalize_logo.py\n| Flag | Default | Notes |\n|---|---|---|\n| `--src` | required | input image (PNG/JPG/rasterized SVG) |\n| `--dst` | required | output PNG path; parent dirs auto-created |\n| `--height` | `80` | target content height. **Use 240 for retina poster export** (`scale: 2`) — 80 looks soft after 2× downscale. |\n| `--invert` | `auto` | `auto` / `off` / `full` / `selective` (selective preserves colored icons) |\n\n### add_partner.py\n| Flag | Notes |\n|---|---|\n| `--repo` | website repo root; defaults to `LOVSTUDIO_MAINTAIN_PARTNERS_SITE_ROOT`, profile JSON, or legacy `LOVSTUDIO_WEB_ROOT` / `PARTNERS_SITE_ROOT` |\n| `--partners-file` | PARTNERS TSX file; defaults to `LOVSTUDIO_MAINTAIN_PARTNERS_FILE`, profile JSON, legacy `LOVSTUDIO_PARTNERS_FILE` / `PARTNERS_FILE`, PartnersGrid.tsx, or WorkshopDispatch.tsx |\n| `--name` | display name (CJK ok) |\n| `--href` | brand URL |\n| `--logo` | path under `/public`, e.g. `/partners/foo/logo.png` |\n| `--key` | i18n key, e.g. `partnerFooTagline` |\n| `--category` | `compute` / `peer` / `invest` / `media` / `community`; default `community` |\n| `--zh / --en / --ja / --th` | tagline strings (all required) |\n| `--show-name` | render name next to icon for narrow logos |\n\n### audit_partners.py\n| Flag | Notes |\n|---|---|\n| `--repo` | website repo root; defaults to `LOVSTUDIO_MAINTAIN_PARTNERS_SITE_ROOT`, profile JSON, or legacy `LOVSTUDIO_WEB_ROOT` / `PARTNERS_SITE_ROOT` |\n| `--partners-file` | PARTNERS TSX file; defaults to `LOVSTUDIO_MAINTAIN_PARTNERS_FILE`, profile JSON, legacy `LOVSTUDIO_PARTNERS_FILE` / `PARTNERS_FILE`, PartnersGrid.tsx, or WorkshopDispatch.tsx |\n| `--probe` | HTTP-probe every href (slow, needs proxy env vars) |\n\n## Network proxy\n\nSandbox child processes don't inherit the system ClashX proxy. Before\nfetching logos with `lovstudio-find-logo` or probing partner URLs, export:\n\n```bash\nexport https_proxy=http://127.0.0.1:7890 \\\n       http_proxy=http://127.0.0.1:7890 \\\n       all_proxy=socks5://127.0.0.1:7891\n```\n\n`audit_partners.py` already injects these for `curl` invocations.\n\n## Dependencies\n\n```bash\ngit clone https://github.com/lovstudio/find-logo-skill \\\n  \"${LOVSTUDIO_SKILLS_INSTALL_DIR:?Set LOVSTUDIO_SKILLS_INSTALL_DIR}/lovstudio-find-logo\"\npython3 -m pip install Pillow\nbrew install librsvg  # for SVG logo sources\n```","tags":["maintain","partners","skills","lovstudio","agent-skills","ai-coding-assistant","cjk","claude-code","cursor","gemini-cli","markdown-to-docx","markdown-to-pdf"],"capabilities":["skill","source-lovstudio","skill-maintain-partners","topic-agent-skills","topic-ai-coding-assistant","topic-cjk","topic-claude-code","topic-cursor","topic-gemini-cli","topic-markdown-to-docx","topic-markdown-to-pdf"],"categories":["skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/lovstudio/skills/maintain-partners","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add lovstudio/skills","source_repo":"https://github.com/lovstudio/skills","install_from":"skills.sh"}},"qualityScore":"0.477","qualityRationale":"deterministic score 0.48 from registry signals: · indexed on github topic:agent-skills · 54 github stars · SKILL.md body (13,688 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:57:49.018Z","embedding":null,"createdAt":"2026-04-23T12:56:34.347Z","updatedAt":"2026-05-18T18:57:49.018Z","lastSeenAt":"2026-05-18T18:57:49.018Z","tsv":"'/.lovstudio/logo-collection':536,595 '/127.0.0.1':1863 '/fonts':1513,1554 '/logo':537 '/logo.png':107,628,671,696,699,749 '/logo.svg':596 '/lovstudio-find-logo':1888 '/lovstudio/find-logo-skill':1878 '/partners':670,981 '/partners/brand.png':1055 '/partners/foo/logo.png':1734 '/partnersgrid.tsx':86,166,251 '/public':1732 '/skills.yaml':335 '/tmp':598,603 '/tmp/brand-clean.svg':1128,1134 '/tmp/brand-icon.png':1459 '/tmp/brand-raw.png':1045,1053,1136 '/workshopdispatch.tsx':102,257 '0':1102,1126,1308,1331,1531 '0.10':1185 '0.88':1312,1329 '1':174,225,495,500,911,1104,1310,1339,1446,1533 '1.7':937 '100':1205,1209 '127.0.0.1':1854,1858 '150':1515 '1px':1179 '2':180,230,438,512,685,933,1008,1497,1657,1662 '2.5':424 '240':594,984,1048,1057,1486,1651 '240px':37,428,924 '240px-tall':36 '24px':868 '255':1182,1183,1184 '26px':1596 '3':186,235,569,709,819,925,1046,1069,1516 '30':1416 '30px':1162 '32px':423 '3px':1191 '4':247,578,651,766,1137 '40px':464 '4px':1189 '5':618,804,1262 '6':631,1386 '6px':1192 '7':646,1574 '720':1042,1133 '7890':1855,1859 '7891':1864 '80':1646,1658 '80px':411,934,999 '96':1415 '96px':1160 'accept':203,265 'across':1608 'ad':473 'add':358,496,780 'add_partner.py':178,1674 'added/removed':1261 'agent':345 'alias':206,268 'align':13,805,1171,1583 'align-item':1170 'alreadi':951,1314,1360,1866 'also':783 'alway':988,1030 'anoth':1604 'anti':1576 'anti-pattern':1575 'app':83,99,163,248,254,1086 'app-icon-styl':1085 'append':647 'archiv':532,573,614 'array':91 'ask':356,371,390,399,501,545,718 'askuserquest':511 'asset':534,575,582,616,813,1240,1619 'assum':736 'audit':401,767 'audit_partners.py':179,1767,1865 'auto':630,701,751,755,986,1150,1211,1213,1245,1643,1665,1666 'auto-cr':1642 'auto-strip':754 'auto-width':1149,1244 'automat':1544 'avail':1573 'b':1464,1475 'back':1562 'background':760,1074,1096,1265 'base':889 'bash':127,521,589,620,654,690,741,768,961,1037,1116,1850,1873 'besid':1405 'better':550 'bg':1285 'black':1291,1336,1383,1452 'black-on-dark':1382 'black/colored':1080 'block':1111,1215 'blurri':1002 'bookstor':969 'border':1178,1187,1197 'border-box':1196 'border-radius':1186 'box':910,1146,1158,1194,1198,1217,1417 'box-siz':1193 'brand':28,476,506,1279,1296,1370,1396,1432,1501,1547,1570,1727 'brand-color':1295 'brand.svg':1043,1127 'brandnam':1403 'break':1611 'brew':1066,1894 'bright':1101,1307,1330,1530 'brittl':1599 'browser':1609 'bundl':1506 'burn':1003 'call':562 'candid':542 'cannot':1020 'canva':40,410,859,882,1274,1523 'categori':675,1740 'caus':850 'cell':1165 'center':1173,1177 'check':786 'child':1828 'chines':488 'citic':968,971 'citic-bookstor':967 'citic-thinker-lab':970 'cjk':1724 'clashx':1835 'clean':1448 'cli':1623 'clone':1875 'cn':113 'co':846 'co-host':845 'collect':32,513 'color':1297,1334,1539,1672 'common':838,1083 'communiti':676,1745,1747 'compos':1425,1517 'composit':1391 'comput':1741 'configur':59,122,732,1345 'confirm':637 'consum':349 'content':39,412,876,892,917,1176,1648 'contract':213,275 'convert':49,592,1040,1062,1131,1460 'crawler':311 'creat':1644 'crop':764,1021 'cross':812 'cross-asset':811 'css':421,836,886,909,1154,1302 'curl':1870 'dark':1264,1273,1385 'dark-background':1263 'dead':801 'declar':319 'default':919,1454,1627,1681,1704,1746,1774,1797 'densiti':425,926 'depend':288,314,323,327,350,1872 'design':858,1282,1615 'despit':832 'detail':280 'differ':830,855,871,878,896,1621 'dir':133,138,140,142,1641,1882,1887 'dir/scripts/add_partner.py':657 'dir/scripts/audit_partners.py':771 'dir/scripts/normalize_logo.py':623,693,744,977,1051 'direct':617,724 'discoveri':30,298 'dispatch.partner':119 'display':897,1166,1214,1722 'diverg':900 'done':987 'doubl':959,1377 'double-invert':958 'double-process':1376 'downscal':1663 'drop':1117 'dst':625,697,746,980,1054,1635 'e':1122 'e.g':1275,1733,1738 'embed':1073 'en':114,678,1749 'en/ja/th':492 'enough':427 'entri':1543 'env':1823 'escap':1352 'etc':383 'event':18,430,840,1276 'everi':787,913,1600,1818 'exist':394,688 'expect':340 'explicit':175,226 'export':435,930,943,1655,1849,1851 'eyebal':891 'f':963 'f.png':982 'fall':1561 'fallback':310 'fetch':1838 'field':317,329 'file':125,162,224,229,234,240,242,246,260,263,388,663,717,722,777,795,853,906,921,1000,1357,1700,1703,1709,1715,1717,1793,1796,1802,1808,1810 'fill':1125 'filter':1100,1306,1327,1455,1534 'find':25,291,519,1092,1504,1558,1843 'find-logo':1091,1503,1557 'find_logo.py':539,559 'first':1013 'fit':1243 'fix':903,1144,1163,1216,1582 'fixed-s':1143 'flag':1626,1675,1768 'flatten':1105,1332 'flex':1152,1169,1247 'folder':740 'font':1435,1502,1507,1510,1550,1571 'footprint':1237 'format':484 'found':1509 'fraction':879 'frontmatt':316,337 'full':448,1668 'g':1463,1473 'gap':1519 'general':333 'get':1260 'git':1874 'github.com':1877 'github.com/lovstudio/find-logo-skill':1876 'give':935,1218 'goe':1526 'grayscal':420,457 'grid':1164 'h':593,1041,1132,1485,1492,1495 'hatch':1353 'height':413,422,815,837,867,873,887,918,983,1056,1161,1208,1212,1589,1595,1645,1649 'helvetica':1566 'hero':20,842 'hide':1113 'higher':440 'home':85,101,165,250,256 'home/.lovstudio/skills/profile.json':194 'homepag':304,508 'horizont':825 'host':847 'href':668,788,1726,1819 'html':1153 'http':785,1816,1856 'http-check':784 'http-probe':1815 'https':1852 'i18n':797,1736 'icon':461,482,1076,1087,1115,1226,1231,1388,1392,1401,1412,1457,1476,1481,1487,1518,1673,1763 'icon-on':460,1225,1387 'icon.crop':1482 'icon.getbbox':1483 'icon.height':1493 'icon.resize':1488 'icon.split':1466 'icon.width':1490 'ident':916 'ignor':343 'imag':1442,1632 'image.lanczos':1496 'image.merge':1468,1477 'image.open':1458 'imagedraw':1443 'imagefont':1444 'imagefont.truetype':1511 'imageop':1445 'imageops.invert':1470,1472,1474 'import':1441 'inconsist':1288 'inherit':1832 'inject':1867 'inlin':1168 'inline-flex':1167 'input':753,1631 'instal':132,137,1067,1881,1886,1892,1895 'int':1489 'intern':856 'inv':1467 'inv.split':1479 'invers':1322 'invert':447,629,700,750,945,960,985,1058,1103,1309,1338,1450,1532,1664 'invest':1743 'invis':1381 'invoc':1871 'item':1172 'ja':115,679,1750 'jpeg':752 'json':117,189,529,653,1689,1711,1782,1804 'juanyi':966,1007,1594 'justifi':1175 'justify-cont':1174 'keep':96,307 'key':238,284,338,672,798,1735,1737 'kit':555,1371 'lab':973 'legaci':205,253,267,1691,1712,1784,1805 'level':322,907 'librsvg':1065,1068,1896 'light':419,456,953 'light-on-transpar':952 'like':1421,1590 'live':81,104 'll':1419 'local':387,564,652,800 'logo':15,26,29,33,103,292,297,368,376,397,409,446,463,515,520,669,689,711,794,810,821,885,914,1093,1140,1157,1201,1224,1230,1259,1280,1305,1313,1325,1350,1505,1537,1559,1588,1593,1602,1729,1766,1839,1844,1899 'logo-origin':1324 'look':829,939,1222,1287,1420,1659 'lovstudio':2,7,24,55,67,130,135,147,159,181,191,195,231,258,290,332,518,1683,1692,1706,1713,1776,1785,1799,1806,1842,1879,1884 'lovstudio-find-logo':23,289,517,1841 'lovstudio-general-skil':331 'lovstudio-maintain-partn':1 'lovstudio.partners':241 'lujiazui':965 'm':1890 'magic':1605 'main':84,100,164,249,255 'maintain':3,5,53,57,68,148,160,182,232,1684,1707,1777,1800 'maintain-partn':52 'make':1251 'margin':860 'mark':1298,1424 'match':1538 'matrix':1221 'max':1203,1207 'max-height':1206 'max-width':1202 'may':94 'media':1744 'metadata':351 'mirror':325,489 'miss':793,796 'moment':1613 'must':301 'name':471,477,507,525,666,683,1721,1723,1758,1760 'narrow':1223,1765 'near':758 'near-whit':757 'need':1603,1821 'network':1825 'never':994 'new':362,498,1601 'next':479,1761 'non':609 'non-svg':608 'normal':31,51,374,467,577,588,619,634,686,912,989,998 'normalize_logo.py':1014,1625 'note':1628,1676,1769 'number':1606 'o':597,1044,1135 'occupi':877,1234 'offici':551 'often':1551 'ok':1725 'older':92,1249 'one':359 'op':494,684,708,765,803 'opac':1311,1328,1346 'oper':1015 'opt':1319 'origin':992,1326,1351 'otherwis':1099 'outer':1119 'output':1637 'pad':857,1024,1190,1622 'parent':1640 'pariti':816 'partner':4,10,14,54,69,78,89,90,149,157,161,183,199,222,228,233,259,262,363,395,403,417,499,649,662,664,673,739,776,778,809,820,1512,1685,1695,1699,1701,1708,1714,1716,1778,1788,1792,1794,1801,1807,1809,1847 'partnerfootaglin':1739 'partners-fil':227,661,775,1698,1791 'partners.file':243 'partnersgrid.tsx':1718,1811 'pass':155,468 'path':64,723,1639,1730 'pattern':1577 'peer':1742 'per':799,884,1253,1587 'per-logo':883,1586 'per-row':1252 'pil':1429,1440 'pillow':1893 'pip':1891 'pixel':1018 'png':635,942,979,1035,1525,1638 'png/jpg/rasterized':1633 'pngs':863 'possibl':1437 'poster':19,431,841,929,1277,1654 'preced':169,219 'preserv':1671 'press':554 'press-kit':553 'previous':997 'primari':533,574,581,615 'privat':738 'probe':781,1814,1817,1846 'process':1378,1829 'produc':1340 'profil':76,188,193,237,283,1688,1710,1781,1803 'provid':385,716 'proxi':791,1822,1826,1836,1853,1857,1861 'ps':1156,1200,1304,1349,1592 'ps-logo':1199,1303 'ps-logo-box':1155 'ps-logo-juanyi':1591 'ps-logo-origin':1348 'ps-logo.ps':1323 'public':212,274 'public/partners':106,695,698 'put':818 'python':1438 'python3':522,621,655,691,742,769,975,1049,1889 'r':1462,1471 'radius':1188 'raster':44,585,602,920,1012,1017,1034,1098,1447,1536 'ratio':893 'raw.png':599,604 're':703 're-read':702 'read':285,632,704,726 'readi':43 'recip':1250,1301 'recommend':1147 'rect':1075,1124 'refer':1624 'references/user-config.md':286 'releas':407 'reliabl':902 'render':478,869,1031,1498,1759 'replac':392,710 'repo':61,66,156,176,658,772,1677,1679,1770,1772 'report':792 'requir':294,790,1630,1636,1755 'rerun':558 'reship':1616 'resolv':62,126 'retina':42,434,928,1653 'retina-readi':41 'return':540 'reus':22 'reusabl':215,277 'rgb':1469 'rgba':1181,1461,1478 'root':71,129,146,151,173,185,197,201,660,774,849,1680,1687,1694,1697,1773,1780,1787,1790 'root/lovstudio-find-logo/scripts/find_logo.py':524 'root/lovstudio-maintain-partners':144 'root/public/partners':627,748 'round':1081 'row':16,807,848,1254,1269 'rsvg':48,591,1039,1061,1130 'rsvg-convert':47,590,1038,1060,1129 'sandbox':1827 'scale':437,898,932,1241,1656 'scrape':303 'scraper':565 'section':11,404,843 'sed':1121 'select':450,1669,1670 'separ':309 'set':134,152,865,1585,1883 'sf':1565 'share':74,187,236 'sharp':426 'ship':1063,1409,1552 'show':453,470,682,1757 'show-nam':469,681,1756 'sibl':1036 'singl':824 'sit':1270 'site':70,93,150,184,200,1686,1696,1779,1789 'sites.partners':239 'size':377,831,1145,1195 'skill':128,131,136,139,141,143,192,216,278,287,300,321,334,523,568,622,656,692,743,770,976,1050,1560,1880,1885 'skill-level':320 'skill-maintain-partners' 'slow':789,1820 'slug':528 'smaller':1032 'socks5':1862 'soft':940,1660 'solid':1109,1180 'sourc':611,721,852,949,993,1011,1356,1618,1900 'source-lovstudio' 'squar':1082 'src':606,624,694,745,978,1052,1629 'src/i18n/messages':110 'stabl':1220,1300 'standard':373,408 'step':1027 'still':95 'stop':543 'string':1753 'strip':21,79,418,458,756,826,1094 'style':490,1088 'support':282 'svg':584,610,1010,1022,1029,1071,1366,1390,1397,1449,1634,1898 'svgs':45,1089 'system':1564,1834 'taglin':108,120,483,674,1752 'tall':38 'target':922,1484,1491,1494,1647 'text':1292,1520 'th':116,680,1751 'thinker':972 'topic-agent-skills' 'topic-ai-coding-assistant' 'topic-cjk' 'topic-claude-code' 'topic-cursor' 'topic-gemini-cli' 'topic-markdown-to-docx' 'topic-markdown-to-pdf' 'total':1255 'touch':124 'transpar':445,762,955,1318,1364,1522 'tri':1580 'trim':904 'tsx':158,223,665,779,1702,1795 'ttf':1514 'tweak':888 'two':862 'unidentifi':1423 'unif':1266 'uniform':908,1341 'unknown':336 'unpredict':1257 'unstabl':895,1607 'upscal':1001 'url':369,509,527,552,556,802,1728,1848 'use':167,217,312,354,530,600,612,944,1430,1545,1650 'user':75,121,355,370,384,398,503,547,715,730 'user-provid':714 'usual':80 'var':1824 'variant':1367 'verifi':571,707 'via':46,510 'viewbox':1023 'visibl':640,872 'visual':570,814 'web':145,196,626,659,747,773,1693,1786 'websit':8,60,172,416,1678,1771 'white':380,382,443,643,645,759,1110,1284,1316,1342,1362,1365,1451 'white-on-transpar':442,1315,1361 'white-on-whit':379,642 'wide':465,1229 'width':1151,1159,1204,1210,1246,1256 'without':366,1025 'wordmark':1232,1393,1404,1427,1499 'work':1456 'workflow':493 'workshopdispatch.tsx':1720,1813 'workspace.partners':245 'workspace/profile':733 'wrap':1077,1138 'wrapper':1120 'wrong':378 'zh':112,677,1748 'zh-cn':111 '一句话定位':486 '合作伙伴板块维护':56 '品牌名':485 '显示名':526,667 '联办':844","prices":[{"id":"0355af39-44f0-496e-b25c-44a78691a7fa","listingId":"eb5ccd5b-65f4-42da-bf53-7ef70a19d596","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"lovstudio","category":"skills","install_from":"skills.sh"},"createdAt":"2026-04-23T12:56:34.347Z"}],"sources":[{"listingId":"eb5ccd5b-65f4-42da-bf53-7ef70a19d596","source":"github","sourceId":"lovstudio/skills/maintain-partners","sourceUrl":"https://github.com/lovstudio/skills/tree/main/skills/maintain-partners","isPrimary":false,"firstSeenAt":"2026-04-23T12:56:34.347Z","lastSeenAt":"2026-05-18T18:57:49.018Z"}],"details":{"listingId":"eb5ccd5b-65f4-42da-bf53-7ef70a19d596","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"lovstudio","slug":"maintain-partners","github":{"repo":"lovstudio/skills","stars":54,"topics":["agent-skills","ai-coding-assistant","cjk","claude-code","cursor","gemini-cli","markdown-to-docx","markdown-to-pdf"],"license":"mit","html_url":"https://github.com/lovstudio/skills","pushed_at":"2026-05-17T09:28:31Z","description":"Top-level index for the Lovstudio skills ecosystem","skill_md_sha":"1768d2ca7868f729d18263a59ea0b24d531b87b3","skill_md_path":"skills/maintain-partners/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/lovstudio/skills/tree/main/skills/maintain-partners"},"layout":"multi","source":"github","category":"skills","frontmatter":{"name":"lovstudio-maintain-partners","license":"MIT","description":"Maintain the LovStudio website's partners section AND align partner logo rows on event posters / hero strips: reuse lovstudio-find-logo for brand logo discovery, normalize collected logos 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\".","compatibility":"Requires the lovstudio-find-logo skill plus Python 3.8+ with Pillow (`pip install Pillow --break-system-packages`). Requires rsvg-convert (`brew install librsvg`) when the selected logo source is SVG. Tested on macOS; Linux should work. Website repo paths are configurable via --repo, LOVSTUDIO_MAINTAIN_PARTNERS_SITE_ROOT, or the shared user profile; this skill must not require Mark's personal absolute path. Legacy path aliases remain accepted for existing local setups."},"skills_sh_url":"https://skills.sh/lovstudio/skills/maintain-partners"},"updatedAt":"2026-05-18T18:57:49.018Z"}}