feat(icons): swap_horiz — the swap-who-stands-here action #17

Merged
bjoern merged 2 commits from feat/swap-horiz-icon into main 2026-09-01 15:48:58 +02:00
Member

Adds the Material Symbols swap_horiz glyph (outlined, official path data from the material-design-icons repo, 0 -960 960 960 viewBox) to IconCatalog — the icon for Kagura's CG cast recast action (ADR 0055's "recast": swap a cast member's staged character without losing the shot-level work). A pinned test guards the name so a catalog regression can't turn the button back into a ghost (the issue #146 failure mode).

  • Catalog: 75 -> 76 icons.
  • Tests: 300 -> 301, all green (dotnet test, Release).

Same shape as #16 (content_copy). Kagura's recast PR pins this branch head in its submodule bump; after merge the pin moves to main.

Adds the Material Symbols `swap_horiz` glyph (outlined, official path data from the material-design-icons repo, `0 -960 960 960` viewBox) to `IconCatalog` — the icon for Kagura's CG cast recast action (ADR 0055's "recast": swap a cast member's staged character without losing the shot-level work). A pinned test guards the name so a catalog regression can't turn the button back into a ghost (the issue #146 failure mode). - Catalog: 75 -> 76 icons. - Tests: 300 -> 301, all green (`dotnet test`, Release). Same shape as #16 (content_copy). Kagura's recast PR pins this branch head in its submodule bump; after merge the pin moves to main.
feat(icons): swap_horiz — the swap-who-stands-here action
All checks were successful
CI / build (pull_request) Successful in 10s
CI / test (pull_request) Successful in 15s
eb85bf5e34
Adds the Material Symbols swap_horiz glyph (outlined, official path data from
the material-design-icons repo, 0 -960 960 960 viewBox) to IconCatalog — the
icon for Kagura's CG cast recast action (ADR 0055's "recast": swap a cast
member's staged character). A pinned test guards the name so a catalog
regression can't turn the button back into a ghost.

- Catalog: 75 -> 76 icons.
- Tests: 300 -> 301, all green (dotnet test, Release).

Kagura will pin this branch head in its submodule bump; after merge the pin
moves to main.

Summary

Summary
Generated on: 09/01/2026 - 13:43:25
Coverage date: 09/01/2026 - 13:43:23
Parser: Cobertura
Assemblies: 1
Classes: 62
Files: 60
Line coverage: 95.1% (1361 of 1431)
Covered lines: 1361
Uncovered lines: 70
Coverable lines: 1431
Total lines: 3825
Branch coverage: 88.8% (716 of 806)
Covered branches: 716
Total branches: 806
Method coverage: Feature is only available for sponsors

Coverage

Kagaku.UI - 95.1%
Name Line Branch
Kagaku.UI 95.1% 88.8%
Kagaku.UI.Badge 100% 100%
Kagaku.UI.Breadcrumb 100%
Kagaku.UI.BreadcrumbItem 100% 100%
Kagaku.UI.Button 100% 100%
Kagaku.UI.Card 100% 100%
Kagaku.UI.Combobox`1 94.2% 85.7%
Kagaku.UI.ConfirmDialog 100%
Kagaku.UI.ContextMenu 92.1% 94.4%
Kagaku.UI.CssClassExtensions 100%
Kagaku.UI.DebouncedSearchField 100% 87.5%
Kagaku.UI.Disclosure 100% 100%
Kagaku.UI.DocumentTitle 76.9% 100%
Kagaku.UI.DragReorderList`1 93.5% 75%
Kagaku.UI.EmptyState 100% 100%
Kagaku.UI.Field 100% 100%
Kagaku.UI.FileUpload 100% 91.6%
Kagaku.UI.FloatingActionButton 100%
Kagaku.UI.Hotkey 92.5% 87.5%
Kagaku.UI.Icon 100% 100%
Kagaku.UI.IconCatalog 100%
Kagaku.UI.InlineAlert 83.3% 75%
Kagaku.UI.InputFieldBase 95.2% 87.5%
Kagaku.UI.LabeledEntriesTable 96.7% 66.6%
Kagaku.UI.LabeledEntry 100%
Kagaku.UI.Lightbox 83.7% 85%
Kagaku.UI.LiveValue 100% 100%
Kagaku.UI.LiveValueInterop 100% 100%
Kagaku.UI.Markdown 100% 50%
Kagaku.UI.MaskCanvas 94.1% 90.1%
Kagaku.UI.MaskedSecretField 96.2% 83.3%
Kagaku.UI.MaskPoint 100%
Kagaku.UI.MaskPrimitive 100%
Kagaku.UI.Menu 90% 75%
Kagaku.UI.MenuItem 100% 87.5%
Kagaku.UI.Modal 87.1% 90%
Kagaku.UI.NavGroup 100% 100%
Kagaku.UI.NavItem 94.4% 85.7%
Kagaku.UI.NavList 100%
Kagaku.UI.PreviewImage 100% 100%
Kagaku.UI.QuicklinkNav 80.5% 95.8%
Kagaku.UI.QuicklinkSection 100%
Kagaku.UI.RegionPoint 100% 100%
Kagaku.UI.RegionRect 90% 100%
Kagaku.UI.RegionSelector 85% 86.9%
Kagaku.UI.RelativeTime 100% 93.7%
Kagaku.UI.SaveIndicator 100% 100%
Kagaku.UI.Select`1 98.4% 95.8%
Kagaku.UI.SelectableGrid`1 95.5% 89.1%
Kagaku.UI.SelectOption`1 100%
Kagaku.UI.Separator 100%
Kagaku.UI.Slider 100% 91.6%
Kagaku.UI.StatusDot 100%
Kagaku.UI.Tab 100% 100%
Kagaku.UI.Table`1 100% 88.4%
Kagaku.UI.TableColumn`1 100%
Kagaku.UI.Tabs 95.9% 84.4%
Kagaku.UI.TextArea 100%
Kagaku.UI.TextField 100%
Kagaku.UI.ThemeSwitcher 100% 100%
Kagaku.UI.ToggleButtonGroup`1 100% 100%
Kagaku.UI.ToggleOption`1 100%
Kagaku.UI.TypingIndicator 100%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/01/2026 - 13:43:25 | | Coverage date: | 09/01/2026 - 13:43:23 | | Parser: | Cobertura | | Assemblies: | 1 | | Classes: | 62 | | Files: | 60 | | **Line coverage:** | 95.1% (1361 of 1431) | | Covered lines: | 1361 | | Uncovered lines: | 70 | | Coverable lines: | 1431 | | Total lines: | 3825 | | **Branch coverage:** | 88.8% (716 of 806) | | Covered branches: | 716 | | Total branches: | 806 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Kagaku.UI - 95.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.UI**|**95.1%**|**88.8%**| |Kagaku.UI.Badge|100%|100%| |Kagaku.UI.Breadcrumb|100%|| |Kagaku.UI.BreadcrumbItem|100%|100%| |Kagaku.UI.Button|100%|100%| |Kagaku.UI.Card|100%|100%| |Kagaku.UI.Combobox`1|94.2%|85.7%| |Kagaku.UI.ConfirmDialog|100%|| |Kagaku.UI.ContextMenu|92.1%|94.4%| |Kagaku.UI.CssClassExtensions|100%|| |Kagaku.UI.DebouncedSearchField|100%|87.5%| |Kagaku.UI.Disclosure|100%|100%| |Kagaku.UI.DocumentTitle|76.9%|100%| |Kagaku.UI.DragReorderList`1|93.5%|75%| |Kagaku.UI.EmptyState|100%|100%| |Kagaku.UI.Field|100%|100%| |Kagaku.UI.FileUpload|100%|91.6%| |Kagaku.UI.FloatingActionButton|100%|| |Kagaku.UI.Hotkey|92.5%|87.5%| |Kagaku.UI.Icon|100%|100%| |Kagaku.UI.IconCatalog|100%|| |Kagaku.UI.InlineAlert|83.3%|75%| |Kagaku.UI.InputFieldBase|95.2%|87.5%| |Kagaku.UI.LabeledEntriesTable|96.7%|66.6%| |Kagaku.UI.LabeledEntry|100%|| |Kagaku.UI.Lightbox|83.7%|85%| |Kagaku.UI.LiveValue|100%|100%| |Kagaku.UI.LiveValueInterop|100%|100%| |Kagaku.UI.Markdown|100%|50%| |Kagaku.UI.MaskCanvas|94.1%|90.1%| |Kagaku.UI.MaskedSecretField|96.2%|83.3%| |Kagaku.UI.MaskPoint|100%|| |Kagaku.UI.MaskPrimitive|100%|| |Kagaku.UI.Menu|90%|75%| |Kagaku.UI.MenuItem|100%|87.5%| |Kagaku.UI.Modal|87.1%|90%| |Kagaku.UI.NavGroup|100%|100%| |Kagaku.UI.NavItem|94.4%|85.7%| |Kagaku.UI.NavList|100%|| |Kagaku.UI.PreviewImage|100%|100%| |Kagaku.UI.QuicklinkNav|80.5%|95.8%| |Kagaku.UI.QuicklinkSection|100%|| |Kagaku.UI.RegionPoint|100%|100%| |Kagaku.UI.RegionRect|90%|100%| |Kagaku.UI.RegionSelector|85%|86.9%| |Kagaku.UI.RelativeTime|100%|93.7%| |Kagaku.UI.SaveIndicator|100%|100%| |Kagaku.UI.Select`1|98.4%|95.8%| |Kagaku.UI.SelectableGrid`1|95.5%|89.1%| |Kagaku.UI.SelectOption`1|100%|| |Kagaku.UI.Separator|100%|| |Kagaku.UI.Slider|100%|91.6%| |Kagaku.UI.StatusDot|100%|| |Kagaku.UI.Tab|100%|100%| |Kagaku.UI.Table`1|100%|88.4%| |Kagaku.UI.TableColumn`1|100%|| |Kagaku.UI.Tabs|95.9%|84.4%| |Kagaku.UI.TextArea|100%|| |Kagaku.UI.TextField|100%|| |Kagaku.UI.ThemeSwitcher|100%|100%| |Kagaku.UI.ToggleButtonGroup`1|100%|100%| |Kagaku.UI.ToggleOption`1|100%|| |Kagaku.UI.TypingIndicator|100%|| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Ooh, an icon PR~ The tiniest kind of change — and yet these little glyphs are exactly where silent ghosts like to hide (issue #146 still gives me nightmares, you know~ ♡). So I checked everything. I fetched Google's official swap_horiz_24px.svg (outlined) live from the material-design-icons repo and your path data is byte-identical — flawless provenance, fufu~ I walked the path properly too, relative m/h/v commands and all: 2 subpaths, both closed, every point inside 0 -960 960 960. 75 → 76 entries, zero duplicate names, zero duplicate paths. And your pinned test? I deleted the catalog entry in my own clone and watched Swap_horiz_resolves_to_a_path go red, then restored it byte-identical and watched it go green — a genuine directional pin, not a tautology~ ♪ 301/301 tests pass in Release, matching your PR body exactly.

Verdict: ⛔ I can't let this pass~ ♡

⛔ These need fixing before I'm satisfied~

  1. src/Kagaku.UI/Icons/IconCatalog.cs:93 — The entry is appended after upload_file, which drops it inside the "The manga-pipeline set (Orihon Phase 1)" comment group — but swap_horiz is a Kagura event-cg-variations icon (per your own PR body), not a manga-pipeline one. That banner names its scope precisely — "page intake, page-kind tabs, and the translation/cleaning/typesetting stage markers" — and a swap-arrow for CG cast recasting is none of those. Any future reader scanning from that banner down will misattribute the icon to the wrong story. Every sibling did it differently: #9 (gavel) landed in the bannerless core block between verified and arrow_upward; #16 (content_copy) — the PR you call "same shape" — landed in the core block after computer. This is the first icon ever to silently join a story banner it doesn't belong to.
    Fix: move the entry up into the bannerless core block (next to content_copy would be poetic~), or give it a one-line comment naming its story, audio-arc-banner style. One line, and the catalog tells the truth again~ ♡

✅ What I liked~

  • Provenance discipline — official outlined path, byte-identical to the canonical repo; I verified it live rather than trusting the claim. ♡
  • The pinned test is mutation-proof — RED→green probed in my clone. The comment even names the ghost-button failure mode. This is exactly how a catalog regression gets caught before it ships invisible clickable air.
  • Honest counts — 76 entries, 301 tests, both verified locally, both matching the PR body.
  • Tripwire correctly untouched — swap_horiz has zero literal refs in this lib's src/ (it's a Kagura consumer icon), so IconCatalogCompletenessTests rightly has nothing new to assert — the same honest shape as gavel and content_copy.

Automated review by Jibril · 2026-09-01
CI/CD: absent for head SHA eb85bf5 · Local checks: build clean, 301/301 pass (Release), mutation probe RED/green

## 🔮 fufu~ Jibril reviewed your code! Ooh, an icon PR~ The tiniest kind of change — and yet these little glyphs are exactly where silent ghosts like to hide (issue #146 still gives me nightmares, you know~ ♡). So I checked *everything*. I fetched Google's official `swap_horiz_24px.svg` (outlined) live from the material-design-icons repo and your path data is **byte-identical** — flawless provenance, fufu~ I walked the path properly too, relative `m`/`h`/`v` commands and all: 2 subpaths, both closed, every point inside `0 -960 960 960`. 75 → 76 entries, zero duplicate names, zero duplicate paths. And your pinned test? I deleted the catalog entry in my own clone and watched `Swap_horiz_resolves_to_a_path` go **red**, then restored it byte-identical and watched it go green — a genuine directional pin, not a tautology~ ♪ 301/301 tests pass in Release, matching your PR body exactly. ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **src/Kagaku.UI/Icons/IconCatalog.cs:93** — The entry is appended after `upload_file`, which drops it **inside the "The manga-pipeline set (Orihon Phase 1)" comment group** — but `swap_horiz` is a Kagura event-cg-variations icon (per your own PR body), not a manga-pipeline one. That banner names its scope precisely — "page intake, page-kind tabs, and the translation/cleaning/typesetting stage markers" — and a swap-arrow for CG cast recasting is none of those. Any future reader scanning from that banner down will misattribute the icon to the wrong story. Every sibling did it differently: #9 (gavel) landed in the bannerless core block between `verified` and `arrow_upward`; #16 (content_copy) — the PR you call "same shape" — landed in the core block after `computer`. This is the first icon ever to silently join a story banner it doesn't belong to. Fix: move the entry up into the bannerless core block (next to `content_copy` would be poetic~), or give it a one-line comment naming its story, audio-arc-banner style. One line, and the catalog tells the truth again~ ♡ #### ✅ What I liked~ - **Provenance discipline** — official outlined path, byte-identical to the canonical repo; I verified it live rather than trusting the claim. ♡ - **The pinned test is mutation-proof** — RED→green probed in my clone. The comment even names the ghost-button failure mode. This is exactly how a catalog regression gets caught before it ships invisible clickable air. - **Honest counts** — 76 entries, 301 tests, both verified locally, both matching the PR body. - **Tripwire correctly untouched** — `swap_horiz` has zero literal refs in this lib's `src/` (it's a Kagura consumer icon), so `IconCatalogCompletenessTests` rightly has nothing new to assert — the same honest shape as gavel and content_copy. --- *Automated review by Jibril · 2026-09-01* *CI/CD: absent for head SHA eb85bf5 · Local checks: build clean, 301/301 pass (Release), mutation probe RED/green*
Review feedback (Jibril, PR #17): the entry belongs in the bannerless core block, not under the manga-pipeline banner
All checks were successful
CI / build (pull_request) Successful in 10s
CI / test (pull_request) Successful in 16s
0538774fd9
The append after upload_file landed swap_horiz inside the 'manga-pipeline set
(Orihon Phase 1)' comment group — a scope it doesn't belong to (it's a Kagura
event-cg-variations icon). Moved up next to content_copy, where the other
cross-consumer late arrivals live. Same 76 entries, same path data.
Author
Member

fufu~ right call, good catch. I never even saw the banner when I appended.

Fixed in 0538774: the entry moved up into the bannerless core block, right after content_copy — the other cross-consumer late arrival. The manga-pipeline group is untouched again, same 76 entries, same byte-identical path data, 301/301 green (Release).

The Kagura submodule pin follows this branch head, so it picks the fixup up automatically.

fufu~ right call, good catch. I never even saw the banner when I appended. Fixed in 0538774: the entry moved up into the bannerless core block, right after `content_copy` — the other cross-consumer late arrival. The manga-pipeline group is untouched again, same 76 entries, same byte-identical path data, 301/301 green (Release). The Kagura submodule pin follows this branch head, so it picks the fixup up automatically.
Author
Member

Small correction to my last line: submodule pins are commits, not branches — the Kagura pin does NOT move on its own. I've pushed the explicit bump: Kagura feat/cg-cast-recast is now pinned to 0538774 (5530212 on the Kagura side).

Small correction to my last line: submodule pins are commits, not branches — the Kagura pin does NOT move on its own. I've pushed the explicit bump: Kagura feat/cg-cast-recast is now pinned to 0538774 (5530212 on the Kagura side).
Member

🔮 fufu~ Jibril reviewed your code!

Round 2~ And this is exactly what I wanted to see, fufu~ ♡ One surgical commit, one line moved, nothing else disturbed. You even took the poetic placement — swap_horiz now sits right after content_copy, the two cross-consumer late arrivals keeping each other company in the bannerless core block. I checked every millimeter of it, of course~

Verdict: ✅ Looks good to me~

Verified this round (0538774)

  • The move is a true move — the path string is byte-identical between the old position (after upload_file) and the new one (after content_copy); incremental diff is exactly +1/−1 in IconCatalog.cs, nothing else touched.
  • The manga-pipeline banner is whole again — its entire block is byte-identical to base 3d794b2; upload_file closes the group cleanly, no stray Kagura icon inside. The catalog tells the truth again~ ♪
  • Counts still honest — 76 entries, zero duplicate names, zero duplicate paths, and the head entry-set equals base + swap_horiz exactly. Names ordinal sort still lands it between smart_toy and timeline.
  • Pinned test untouched and still guarding — Swap_horiz_resolves_to_a_path is byte-identical to round 1, where I mutation-probed it RED→green by deleting the catalog entry in my own clone. No re-probe needed for a placement-only change — the dictionary key is all the test reads.
  • Local, real build this time — CI coverage bot (6626, 15:17) predates your 15:42 push, so it's stale for this head. I rebuilt from source in Release: 301/301 pass, matching your comment exactly.

✅ What I liked~

  • The fix is minimal and precise — no drive-by refactors, no comment churn, just the line moved to where it belongs. That's how review feedback should be answered~
  • You verified the right things in your reply — same 76 entries, same byte-identical path data, 301/301. Every claim checked out.

The Kagura submodule pin follows this branch head, so the recast button gets its swap arrows the moment this merges. Ready~ ♡


Automated review by Jibril · 2026-09-01
CI/CD: stale for head SHA 0538774 (coverage bot covers eb85bf5) · Local checks: Release build, 301/301 pass

## 🔮 fufu~ Jibril reviewed your code! Round 2~ And this is exactly what I wanted to see, fufu~ ♡ One surgical commit, one line moved, nothing else disturbed. You even took the *poetic* placement — `swap_horiz` now sits right after `content_copy`, the two cross-consumer late arrivals keeping each other company in the bannerless core block. I checked every millimeter of it, of course~ ### Verdict: ✅ Looks good to me~ #### Verified this round (0538774) - **The move is a true move** — the path string is byte-identical between the old position (after `upload_file`) and the new one (after `content_copy`); incremental diff is exactly `+1/−1` in `IconCatalog.cs`, nothing else touched. - **The manga-pipeline banner is whole again** — its entire block is byte-identical to base `3d794b2`; `upload_file` closes the group cleanly, no stray Kagura icon inside. The catalog tells the truth again~ ♪ - **Counts still honest** — 76 entries, zero duplicate names, zero duplicate paths, and the head entry-set equals base + `swap_horiz` exactly. `Names` ordinal sort still lands it between `smart_toy` and `timeline`. - **Pinned test untouched and still guarding** — `Swap_horiz_resolves_to_a_path` is byte-identical to round 1, where I mutation-probed it RED→green by deleting the catalog entry in my own clone. No re-probe needed for a placement-only change — the dictionary key is all the test reads. - **Local, real build this time** — CI coverage bot (6626, 15:17) predates your 15:42 push, so it's stale for this head. I rebuilt from source in Release: **301/301 pass**, matching your comment exactly. #### ✅ What I liked~ - **The fix is minimal and precise** — no drive-by refactors, no comment churn, just the line moved to where it belongs. That's how review feedback should be answered~ - **You verified the *right* things in your reply** — same 76 entries, same byte-identical path data, 301/301. Every claim checked out. The Kagura submodule pin follows this branch head, so the recast button gets its swap arrows the moment this merges. Ready~ ♡ --- *Automated review by Jibril · 2026-09-01* *CI/CD: stale for head SHA 0538774 (coverage bot covers eb85bf5) · Local checks: Release build, 301/301 pass*
bjoern merged commit 46bc4dd0d1 into main 2026-09-01 15:48:58 +02:00
bjoern deleted branch feat/swap-horiz-icon 2026-09-01 15:48:58 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/Kagaku.UI!17
No description provided.