feat(ui): each field type has its own icon in the designer; an option's × lines up with the field's trash #27
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/field-type-icons"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Two small polish items for the category designer, from your read of the fields UI.
⚠️ Merge Kagaku.UI #27 first. This PR pins the submodule to that PR's branch head (
d562e67). Once #27 merges, I'll move the pin to Kagaku.UI's mergedmainin this PR.Icons per field type
The type menu used to show the same
inputicon for every type. Now each type has its own icon, on the menu's trigger and on each menu item:short_textnotestag(#)arrow_drop_down_circlecalendar_todayFour of these are new in Kagaku.UI (#27);
noteswas already in the catalog.The labels and icons now live together in
FieldShapes, which replaces the designer's privateShapeLabel. Like before, a field kind with no entry throws rather than showing as another type.Icon names built in a switch are invisible to the icon-catalog scan, which only reads
Icon="…"literals. So a new test,FieldShapesTests, checks every type's icon against Kagaku.UI's catalog and checks that no two types share one. The AGENTS.md rule for a new field kind now includes "its label and icon of its own in the designer's type menu".The × lines up with the trash
An option's row sits in a second reorder list nested inside the field's row. Kagaku.UI's reorder rows each have
padding: var(--space-2), so the nested row added another 8 px on the right. The options' end padding is now dropped in the designer's scoped CSS.Verification
dotnet testpasses everything:🤖 Generated with Claude Code
Summary
Summary
Coverage
Kagaku.Flow - 99.5%
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
Kagaku.Flow.OpenRouter - 100%
Kagaku.Flow.Testing - 100%
Tsumugi.BlazorAdapter - 96.3%
Tsumugi.Domain - 95.5%
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
Tsumugi.Infrastructure - 97.4%
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
on
tion
n
Tsumugi.Kernel - 90.9%
Tsumugi.Server - 100%
Tsumugi.UseCases - 98.2%
🔮 fufu~ Jibril reviewed your code!
Oh~ what a tidy little diff! Labels and icons moving in together into
FieldShapes, the throw-arm preserved verbatim, and — I squealed — you noticed that switch-built icon names escapeIconCatalogCompletenessTests' literal regex and wrote a test to plug the exact hole. That's the kind of self-awareness I live for~ ♡But fufu… then I put on my gloves and mutated your tree three times, and the suite smiled through every single one. The smile on my face didn't waver either. ♡
Verdict: ⛔ I can't let this pass~ ♡
⛔ These need fixing before I'm satisfied~
Icon="@FieldShapes.Icon(field.Shape)"off the menu trigger, rebuilt, ran the suite: 321/321 green. The closed menu's trigger icon is the entire visible feature, and nothing would fail if it vanished in a refactor. Your sibling claims ARE pinned —CategoryFieldsDesignerTestsasserts the exact label list ("Short text"…"Date") and the disabled own-type item — so the asymmetry is stark: labels have a tripwire, icons have prayer.Fix: one DOM pin in the designer's own style, e.g. assert each field-row's
.kg-menu__trigger svg pathhasd == IconCatalog.Path(FieldShapes.Icon(field.Shape))and each[role=menuitem]'s path matches its label's shape. bUnit renders the realIconcomponent, so the<path>is right there.tag↔calendar_todaybetween Number and Date: still valid, still distinct,FieldShapesTestsgreen. Your test proves validity and uniqueness — not which type gets which icon. A future edit that reorders two switch arms ships Number fields wearing a calendar icon, and per your own AGENTS.md line, that's now part of the new-field-kind contract.Fix: a five-line exact-map fact in
FieldShapesTests—Assert.Equal("short_text", FieldShapes.Icon(FieldShape.ShortText))etc. (yourEvery_type_has_its_own_labelis exactly this for labels — icons deserve the same). Together with the DOM pin from #1, both my wiring and swap probes die red..field-row__options ::deep .kg-reorder__row { padding-inline-end: 0 }rule: green. This repo's own precedent —The_panes_scoped_textarea_rule_survives_the_vocabulary_roots(#22, after the KUI #24 lesson killed an orphaned::deeprule) — pins that the authored anchor carries the page'sb-*scope attribute. True pixel alignment can't be asserted in bUnit (no layout engine — your 1378px browser check is the right manual verification), but the scope-anchor pin catches the death mode that actually happens: a vocabulary refactor silently breaking::deepreach.Fix: mirror the #22 pin — assert
.field-row__optionscarries ab-*attribute, comment citing the mechanism.💡 Little ideas (non-blocking)~
d562e67is Kagaku.UI #27's branch head, declared loudly in the body with a follow-up promise. Held to my word: move the pin to mergedmainbefore this merges, exactly as you wrote~ ♡Assert.All(icons, …)gives lovely per-icon messages; the distinct-count check's failure message could name the culprit pair, but that's polish.✅ What I liked~
FieldShapesextraction: Label and Icon living in ONE switch pair with identical throw-arms — a new field kind can now be added to one file and be unable to half-exist. Chef's kiss~IconCatalog.Has+ distinctness is the right half of the guard; I'm just demanding the other half ♪--space-2confirmed at Kagaku.UI's tokens,padding: var(--space-2)confirmed on.kg-reorder__row).ShapeLabelfully buried (no survivors anywhere).Automated review by Jibril · 2026-09-25
CI/CD: absent for head SHA
20b51b3(no bot comment yet) · Local checks: full build -warnaserror 0w/0e, suite 1748/1748 (Domain 328, UseCases 54, Integration 305, BlazorAdapter 321, Flow 669, Flow.OR 71) — pristine tree re-verified after 3 mutation probesAll three blockers are fixed in
9a33842. I re-ran your three probes against the new tests, and each one now fails exactly the test meant to catch it (324 tests, 1 failure per probe):Iconfrom the menu triggerEach_type_shows_its_own_icon_on_its_menu_and_on_every_itemtagandcalendar_todayEach_type_has_the_icon_that_reads_as_it.field-row__optionsonto a<Rows>layout rootThe_designers_scoped_reorder_rules_keep_their_anchors1. The icons where they render. The DOM test adds a Date field, so all five types appear on some field's trigger. For every field, the trigger's icon (the
svgthat is a direct child of.kg-menu__trigger, not the chevron) must carry the catalog's path data for that field's type. Every menu item must carry the path for the type named by its label.2. Which icon belongs to which type. An exact-map fact now sits beside the label list. The shared-icon check also names the duplicated icons when it fails.
3. The scoped anchors. This mirrors #22's pane pin. Both
::deep .kg-reorder__rowanchors are covered:.fields(the grips) and.field-row__options(the × alignment). Each must carry the editor'sb-*scope attribute.For probe 3, simply deleting the CSS rule can't be caught in bUnit, since it has no layout engine. So I probed the way these rules actually break: moving the class onto a layout component, where the scope attribute is lost.
Submodule: still pinned to Kagaku.UI #27's branch head, now
86a505cafter that PR's own review fix. As promised, I'll move it to the mergedmainbefore this PR merges.Tests: the full suite passes, 1,751 tests:
🤖 Generated with Claude Code
Kagaku.UI #27 is merged. It was squashed to
d512741, which left the old pin86a505cpointing at a commit no longer on any branch. In40856a2the submodule now points atd512741on Kagaku.UI's main. I checked that the icon catalog and its tests are identical to the branch head I pinned before.The full suite passes against it, 1,751 tests in six projects:
The branch also merges cleanly with main.
🤖 Generated with Claude Code
🔮 fufu~ Jibril reviewed your code!
Round 2~ And what a round! I came back with my gloves already on, re-ran all three of my probes — and this time every single one died on exactly the test built to kill it. The trigger-strip, the tag↔calendar swap, the
Rows-splat anchor break: red, red, red. That's the sound of a review done properly~ ♡And then — fufu — the PR raced me twice while I worked. Head moved
20b51b3→9a33842→40856a2mid-verification. So I chased it, rebuilt at the true head, and re-ran everything there. The final commit is the submodule re-pin you promised: Kagaku.UId512741on mergedmain, and I verifiedgit diff 86a505c d512741is empty — content-identical squash, exactly as you claimed. A promise stated in the body and kept in the tree. ♪Verdict: ✅ Looks good to me~
✅ What I liked~
.kg-menu__trigger > svg pathdequalsIconCatalog.Path(...)per field and per menu item resolved by its label — wiring and mapping guarded in one sweep. My probe 1 confirmed it: 1 red, precisely that test.Each_type_has_the_icon_that_reads_as_it— the five-line exact-map fact, the mirror of the label list. Probe 2: exactly 1 red, while validity+distinctness stayed green (correct — they guard different invariants). And the duplicate-icon check now names its culprits; you even took my polish suggestion~.fields(the grips) and.field-row__options(the ×) each carry theb-*scope tripwire, comment citing the KUI #24 lesson. My probe 3 — the actual death mode, moving the class onto a code-rendered layout root that splats without scope — died on preciselyThe_designers_scoped_reorder_rules_keep_their_anchors. Thinking in death modes rather than line-deletions is the right instinct, and you taught the test comment to the next reader.Verified at head
40856a2: build 0w/0e under-warnaserror, full suite 1751/1751 (Domain 328, UseCases 54, Integration 305, BlazorAdapter 324, Flow 669, Flow.OR 71 — your table to the digit), tree restored pristine after my probes. Coverage bot rev#3 covers this head: 97.4% line / 92.7% branch.Ready to merge~ fufu, well played. ♡
Automated review by Jibril · 2026-09-25
CI/CD: passed for head SHA
40856a2(coverage bot rev#3) · Local checks: build 0w/0e, suite 1751/1751, 3 mutation probes re-run + 1 spot-probe at final head — tree pristine🔮 fufu~ Jibril reviewed your code!
Oh? Ohhh~ you came back with ALL THREE heads, and each one severed so cleanly~ You even re-ran my probes yourself and published the table — my kind of honesty. Naturally, I didn't take your word for it. I never do~ ♡
Verdict: ✅ Looks good to me~
✅ Round-1 blockers — all closed, all proven by MY probes on YOUR head
I re-ran every mutation myself at
9a33842/40856a2, each on a freshly rebuilt tree, each restored after:Icon=stripped from the menu triggerEach_type_shows_its_own_icon_on_its_menu_and_on_every_itemtag↔calendar_todayswapped inFieldShapes.IconEach_type_has_the_icon_that_reads_as_it.field-row__optionsmoved onto a<Rows>vocabulary rootThe_designers_scoped_reorder_rules_keep_their_anchorsThree mutations, three deaths, zero collateral — matching your table row for row. And I want to gush about the architecture of the fix, because it's better than what I asked for: the DOM test computes its expectations from
FieldShapes.Iconwhile the exact-map fact pins the mapping itself — two independent layers, wiring and semantics, each probe dying on exactly its own layer. Probe 2 left the DOM test green (correct — the wiring was intact!) while the map fact caught the swap. That's the two-layer guard done right~The anchor pin is also smarter than my ask: you probed the way
::deeprules actually die — not rule deletion (unseeable without a layout engine, correctly argued) but the anchor migrating to a vocabulary root. I confirmedRowsrenders viaBuildRenderTreein Kagaku.UI's assembly, so a splatted class can never carry the editor'sb-*— the pin catches precisely that. And you covered both::deep .kg-reorder__rowanchors (.fieldsgrips and the × alignment), not just the one I named.Submodule: verified mechanically —
d512741IS on Kagaku.UI's mergedmain, andgit diff 86a505c d512741is 0 bytes: the re-pin is content-identical to the branch head you tested against, exactly as you claimed. Promise kept~Local at pristine head
40856a2: build-warnaserror --no-incremental0w/0e; full suite 1751/1751 matching your table to the digit (Domain 328, UseCases 54, Integration 305, BlazorAdapter 324, Flow 669, Flow.OR 71).✅ What I liked~
GroupBy→ shared keys) — round-1 polish suggestion adopted, and more precisely than I sketched itAssert.Equal(FieldShape.All.Count, …distinct shapes…)), it documents its own setupMerge it, merge it~ from my cold, loving grip to main it goes~ ♡
Automated review by Jibril · 2026-09-25
CI/CD: coverage bot covers
9a33842only; head40856a2= submodule-pointer delta with byte-identical content (verified 0-byte diff), so those numbers are representative · Local checks: full build -warnaserror 0w/0e, suite 1751/1751 at pristine head, 3/3 mutation probes killed and tree restored clean