fix(cover): restore sub-icon slot in cover.svg (#201)
* fix(cover): restore sub-icon slot in cover.svg * docs(cover): add ADR-089 and handoff for PR #201 --------- Co-authored-by: opencode-agent <agent@opencode.local>
This commit is contained in:
parent
87d6c4464b
commit
85d7d62fcd
4 changed files with 55 additions and 1 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||||
<!-- slot: name=icon, x=252, y=180, w=520, h=520, fit=contain, recolor=accent -->
|
<!-- slot: name=icon, x=252, y=180, w=520, h=520, fit=contain, recolor=accent -->
|
||||||
|
<!-- slot: name=sub-icon, x=412, y=600, w=200, h=200, fit=contain, recolor=accent -->
|
||||||
<rect width="1024" height="1024" fill="{{base}}" />
|
<rect width="1024" height="1024" fill="{{base}}" />
|
||||||
<text x="512" y="830" font-family="Geist Sans, sans-serif" font-size="80" font-weight="700" fill="{{fg}}" text-anchor="middle">{{title}}</text>
|
<text x="512" y="830" font-family="Geist Sans, sans-serif" font-size="80" font-weight="700" fill="{{fg}}" text-anchor="middle">{{title}}</text>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 382 B After Width: | Height: | Size: 470 B |
31
docs/decisions/089-pr-201-restore-cover-subicon-slot.md
Normal file
31
docs/decisions/089-pr-201-restore-cover-subicon-slot.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# ADR-089: Restore sub-icon slot in production cover.svg (PR#201)
|
||||||
|
|
||||||
|
## Статус
|
||||||
|
Accepted (2026-07-31)
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
PR#197 (ADR-087) упростил продакшн-шаблон `templates/cover.svg` до единственного `icon`-слота 520×520 + `{{title}}` y=830, удалив slot-комментарии `sub-icon`, `badge` и subtitle-`<text>`. Это было демонстрацией отвязки тестов от продакшн-шаблона (тесты переехали на `tests/fixtures/cover.svg`). Удалённые слоты в продакшн-рендере тогда не использовались, и `assets/cover.png` остался побайтово идентичным.
|
||||||
|
|
||||||
|
Однако движок `buildSvg` итерирует по слотам, **объявлённым в шаблоне** (`slot-parser` читает HTML-комментарии из шаблона), а не по `args.slots`, переданным в вызов. Поэтому после PR#197 переданный `sub-icon=...` молча отбрасывался — слот не объявлен в шаблоне → рендерер его не видит.
|
||||||
|
|
||||||
|
Команда `/cover` (PR#199, ADR-088) документирует `sub-icon` как optional-слот и передаёт его в `draw-image` (пример: `sub-icon=opencode`). Возникла рассогласованность: команда обещает `sub-icon`, рендерер его игнорирует. Сценарий «icon + sub-icon + title» (например, иконка фичи + логотип OpenCode) не работал.
|
||||||
|
|
||||||
|
Условный рендер слотов (PR#144, ADR-061) уже поддерживает опциональность: передан `sub-icon` → рендерится; не передан → слот пропускается. Это доказано fixture-тестами (`render.optional.test.ts` на `tests/fixtures/cover.svg`, где sub-icon присутствует). Движок поддерживал sub-icon всегда — не хватало только объявления слота в продакшн-шаблоне.
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
1. **Восстановлена ровно одна строка** slot-комментария `sub-icon` в `templates/cover.svg`:
|
||||||
|
`<!-- slot: name=sub-icon, x=412, y=600, w=200, h=200, fit=contain, recolor=accent -->`
|
||||||
|
(та самая, что удалил PR#197). Изменений в коде движка (`src/render.ts`) и команде `/cover` нет.
|
||||||
|
2. **Только `sub-icon`** — `badge` и `subtitle` остаются удалёнными из продакшн-шаблона (вне scope PR#201). `/cover` (ADR-088) запрещает subtitle стандартом; badge не используется в Lime-стандарте.
|
||||||
|
3. **Условный рендер (PR#144) сохраняет одно-иконочный кейс побайтово идентичным**: `assets/cover.png` (`icon=opencode`, без `sub-icon`) → 18077 bytes, `cmp` → IDENTICAL. Рендер с двумя иконками (`icon=mic,sub-icon=opencode`) → 23171 bytes (больше, как ожидалось).
|
||||||
|
4. **Тесты не затронуты**: 58/58 проходят — тесты используют `tests/fixtures/cover.svg` (PR#197), а не продакшн-шаблон, поэтому изменение продакшн-шаблона их не ломает.
|
||||||
|
|
||||||
|
## Альтернативы
|
||||||
|
- **Не восстанавливать слот, убрать `sub-icon` из `/cover` (ADR-088)**: отвергнуто — `sub-icon` — полезный сценарий (иконка фичи + логотип проекта), и ADR-088 уже документирует его как optional. Убрать из команды = потерять функциональность, которую движок уже поддерживает.
|
||||||
|
- **Восстановить также `badge` и `subtitle`**: отвергнуто — вне scope issue #200. Lime-стандарт (ADR-088) запрещает subtitle; badge не используется. Восстановление мёртвых слотов вернёт проблему, которую решал PR#197.
|
||||||
|
- **Изменить `buildSvg` чтобы итерировать по `args.slots`, а не по шаблонным слотам**: отвергнуто — крупное архитектурное изменение движка ради одной строки. Текущая модель (шаблон объявляет слоты, рендерер их заполняет) чище и детерминированнее: шаблон = контракт на layout, args = данные.
|
||||||
|
|
||||||
|
## Последствия
|
||||||
|
- Частичный реверс ADR-087 (PR#197): продакшн `templates/cover.svg` снова объявляет `sub-icon` (наряду с `icon`). `badge` и `subtitle` остаются удалёнными — реверс точечный.
|
||||||
|
- Сценарий «icon + sub-icon» теперь работает в продакшн-рендере; одно-иконочный кейс побайтово идентичен (условный рендер PR#144).
|
||||||
|
- Тест-контракт (`tests/fixtures/cover.svg`) не изменён — продакшн и fixture снова расходятся по набору слотов (fixture имеет все 4 типа, продакшн — 2), но это намеренно: fixture тестирует движок, продакшн — конкретный бренд-стандарт.
|
||||||
22
docs/handoff/pr-201-restore-cover-subicon-slot.md
Normal file
22
docs/handoff/pr-201-restore-cover-subicon-slot.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
pr: 201
|
||||||
|
title: "fix(cover): restore sub-icon slot in cover.svg"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что сделано
|
||||||
|
Восстановлена ровно одна строка slot-комментария `sub-icon` в `.opencode/draw-image/templates/cover.svg` (та самая, что удалил PR#197):
|
||||||
|
`<!-- slot: name=sub-icon, x=412, y=600, w=200, h=200, fit=contain, recolor=accent -->`
|
||||||
|
Изменений в коде движка (`src/render.ts`) и команде `/cover` нет — ситуативность (1 или 2 иконки) обеспечивается условным рендером слотов (PR#144). Структурных изменений нет (одна строка в существующем файле).
|
||||||
|
|
||||||
|
Проверено: 58/58 тестов проходят (тесты на `tests/fixtures/cover.svg`, не на продакшн-шаблоне); `assets/cover.png` (`icon=opencode`, без `sub-icon`) побайтово идентичен (18077 bytes, `cmp` → IDENTICAL); рендер с двумя иконками → 23171 bytes (больше, как ожидалось).
|
||||||
|
|
||||||
|
## Почему
|
||||||
|
PR#197 удалил slot-комментарий `sub-icon` из продакшн-шаблона, из-за чего переданный `sub-icon` молча отбрасывался — `buildSvg` итерирует по слотам, объявлённым в шаблоне, а не по `args.slots`. Команда `/cover` (PR#199) документирует `sub-icon` как optional и передаёт его в `draw-image` — возникала рассогласованность: команда обещает `sub-icon`, рендерер его игнорирует. Восстановление слота делает сценарий «icon + sub-icon + title» рабочим, а условный рендер (PR#144) сохраняет одно-иконочный кейс побайтово идентичным. Частичный реверс ADR-087 (только `sub-icon`; `badge`/`subtitle` остаются удалёнными — вне scope).
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
— (после merge: memory-syncer может зафиксировать, что продакшн cover.svg снова объявляет sub-icon — project-map уже обновлён в этом PR)
|
||||||
|
|
||||||
|
## Watch out
|
||||||
|
- **Только `sub-icon` восстановлен** — `badge` и `subtitle` остаются удалёнными из продакшн-шаблона (PR#197). Lime-стандарт (ADR-088) запрещает subtitle; badge не используется. Не путать с `tests/fixtures/cover.svg`, где все 4 типа слотов присутствуют (fixture тестирует движок, продакшн — бренд-стандарт).
|
||||||
|
- **Условный рендер (PR#144) — ключевая инварианта**: одно-иконочный кейс побайтово идентичен. Если будущий PR меняет `render.ts` или slot-parser, проверить `cmp assets/cover.png` не сломался.
|
||||||
|
- **`buildSvg` итерирует по шаблонным слотам, не по `args.slots`** — это контракт движка: шаблон объявляет layout (слоты), args — данные. Добавить новый слот в продакшн = добавить slot-комментарий в `templates/cover.svg` (как сделано здесь), а не менять движок.
|
||||||
|
|
@ -70,7 +70,7 @@ opencode-config/
|
||||||
│ │ ├── render.mjs # SVG string → PNG via sharp (fontFiles: Geist TTF bundle)
|
│ │ ├── render.mjs # SVG string → PNG via sharp (fontFiles: Geist TTF bundle)
|
||||||
│ │ ├── cli.ts # CLI entry: render <template> --title --slots --out [--template-dir dir]; temp SVG в os.tmpdir()/draw-image-${pid}.svg + finally rmSync (race-safe для параллельных рендеров) — PR#178, PR#197 (--template-dir)
|
│ │ ├── cli.ts # CLI entry: render <template> --title --slots --out [--template-dir dir]; temp SVG в os.tmpdir()/draw-image-${pid}.svg + finally rmSync (race-safe для параллельных рендеров) — PR#178, PR#197 (--template-dir)
|
||||||
│ │ ├── brand.json # default palette slaid098 (base/surface/fg/muted/accent/line)
|
│ │ ├── brand.json # default palette slaid098 (base/surface/fg/muted/accent/line)
|
||||||
│ │ ├── templates/cover.svg # 1024×1024 cover template (упрощён: только icon-слот 520×520 + {{title}} y=830; sub-icon/badge/subtitle удалены — PR#197, тесты на fixture)
|
│ │ ├── templates/cover.svg # 1024×1024 cover template (icon-слот 520×520 + sub-icon-слот 200×200 [восстановлен PR#201, был удалён PR#197] + {{title}} y=830; badge/subtitle удалены — PR#197, тесты на fixture)
|
||||||
│ │ ├── fonts/ # Geist Sans TTF (Regular + Bold) bundled for sharp fontFiles
|
│ │ ├── fonts/ # Geist Sans TTF (Regular + Bold) bundled for sharp fontFiles
|
||||||
│ │ ├── icons/lucide/ # 2007 Lucide SVG icons (synced from npm lucide-static via postinstall)
|
│ │ ├── icons/lucide/ # 2007 Lucide SVG icons (synced from npm lucide-static via postinstall)
|
||||||
│ │ ├── brand-logos/ # brand SVG logos (opencode.svg — адаптированный логотип OpenCode 512×512 fill #ccff00, резолвится через draw-image slot icon=opencode) — PR#158
|
│ │ ├── brand-logos/ # brand SVG logos (opencode.svg — адаптированный логотип OpenCode 512×512 fill #ccff00, резолвится через draw-image slot icon=opencode) — PR#158
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue