fix: the Documents page — the open record marked, room at narrow widths, headings under the h1, the designer's limits, one click one change #46
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/documents-ui"
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?
Summary
This fixes usability and accessibility problems on the Documents page and pins the adapter mutants that survived before. Every change is in
BlazorAdapter/Documents/**and its adapter tests, plus one new file,DocumentsStylesheetTests.cs.aria-current="page"and the accent style the section navigation uses. The open category's heading toggle hasaria-current="true"and shows its name in the accent.DocumentsPagepasses both ids to the list.aria-labelon a generic span, which screen readers don't read.clamp(14rem, 33%, 20rem). A bareminmax(14rem, 20rem)never shrinks, because the grid grows a fixed track to its maximum first. At 1280px the list stays 20rem; at 800px the record gets 250px instead of 154px, with no sideways scroll.scrollHeightwas 1011 in an 800px window. A find or ascrollIntoViewdragged the whole app up by 211px (691px at 320px), with no way back. Both columns now haveposition: relative. The pane's size container does not do this on its own: the browser pass showed thatcontainer-typemakes no containing block.--text-subtle(2.55:1) to--text-muted.aria-describedby; a choice's hint has its own id.Ask) all return early while a request is in flight. Before, a second click that reached the circuit before the render disabled the button still sent a second request.CategoryEditor.FlushAsyncon every input's blur and on Ctrl+Z, and that for both editors the save is sent before the undo. The last two are the clean Description re-read and a refusal naming Description.Docs: none. ADR 0027 already says sections use h2 and the page has two columns, and ADR 0028 already sets the limits the buttons now follow. No registry, seed, migration or
WriteAsyncrule applies.Verification
Tsumugi.BlazorAdapter.Tests380/380, run three times on the final commit and three times just before it, with no flakes.Tsumugi.Integration.Tests374/374 on a full build that includes the editor modules. The server builds.DocumentsPageTests.CategoryFieldsDesignerTests.DocumentFieldsEditorTests.DocumentsStylesheetTests, which reads the scoped.razor.csssource for values bUnit cannot see.Add_option_rests_at_a_hundred_options_and_says_why.aria-describedbyset, when any field is full, killed by the second choice field "Mood"._title.Raw, killed byThe_open_documents_heading_names_it_as_stored_not_as_typed.Askguards neutralised, killed byA_click_that_arrives_while_a_structural_change_is_in_flight_changes_nothing.StoryShell.UndoAsyncdispatching before the flush, killed by both undo tests (only ChapterPageTests caught this before).position: relativeremoved or overridden, killed byEach_column_holds_what_is_positioned_in_it_so_nothing_hangs_off_the_viewport.Askgot the same guard.position: relativeon the row. The browser pass then showed the image field's file input overflowed the viewport too, and that the pane's container query holds nothing. So the fix went on both columns instead.OptionsFullnaming: the bool isOptionsFull(field)and the id isOptionsFullHint(field), matchingFieldsFull/FieldsFullHint.scrollHeight == innerHeight. The last row's hidden words and the file input are held by their column, andscrollIntoViewscrolls the list, not the document. With the columns forced back tostatic, the page reproduces 1011px and a 211px/691px drag.🤖 Generated with Claude Code
Summary
Summary
Coverage
Kagaku.Flow - 99.7%
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
Kagaku.Flow.OpenRouter - 100%
Kagaku.Flow.Testing - 100%
Tsumugi.BlazorAdapter - 95.8%
Tsumugi.Domain - 96.1%
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
Tsumugi.Infrastructure - 97.9%
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
on
tion
n
Tsumugi.Kernel - 90.9%
Tsumugi.Server - 100%
Tsumugi.UseCases - 98.3%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh my~ A Documents page PR that arrives with its own mutation-probe diary, four confessed survivors, and a
ClickPastDisabledAsynchelper that knows bUnit drops clicks the circuit would honor? delighted fluttering This is exactly the kind of prey— ahem, PR I love~ ♡Verdict: ✅ Looks good to me~
Single commit on
main(3b892bd= merge base, clean), 13 files, +824/−27. I read the full diff, the full current contents of all eight source files, the reducers/state/effects that back the new guards, and both cited ADRs. Then I sharpened my knife~✅ What I liked~
Requested()setsBusy = truesynchronously inside Dispatch — so the guard reads live state even for a click that outran the render. I traced every dispatch site: the Busy-paths (AddField/AddOption/Ask,NewCategory/NewDocument) are guarded; reorders deliberately aren't (they rideSaving+ optimisticReordered, never set Busy); the delete dialog self-guards viaLoading="Busy"; andConfirm()is structurally race-proof because_question = nullruns before the dispatch. Coherent from end to end.StructuralError→_focusWait = nullis subtle and correct. Arming then clearing in one synchronous block means a render never sees a stale pairing, andRequested()clearsErrorso a new request never eats its own cursor-wait. The refused-add test pins the exact scenario: another session's field arriving later must not steal focus.aria-current="page"on the row is the exact token Kagaku.UI'sNavItemuses for the active nav entry — sibling-consistent, and the category's"true"is the right token for a non-page current item.DocumentsStylesheetTestspins every value by selector and container query — my probes onclamp(...),position: relative, and the@container→@mediaswap died in milliseconds, each on its own pin. The hand-rolled parser even asserts its selectors exist.🔪 My probes (18, all killed on exactly their own pins)
AddField/AddOption/Ask/Both-list-guards neutralized →
A_click_that_arrives…red (×2 files); row & togglearia-currentremoved → red; focus-clearance removed → red; hint unconditional / constant ids ×2 → red at the exact-id andDoesNotContainasserts; h2 →_title.Raw→ red pre-save;clamp→ bare20rem→ red;position: relativedropped → red (both theory rows);@container→@media→ red; StoryShell undo swapped (dispatch-then-flush) → red on the ordering pin;Place()re-sort dropped → red on the new up-move row; provenance words removed → red; Name@onblurflush removed → red. Each restored; tree byte-pristine at49dfa48after every probe (git diff --quiet).💡 Little ideas (non-blocking)~
.docs__hiddenand.editor__namebeside the two pre-existing.editor__liverules). Akg-visually-hiddenutility in Kagaku.UI would carry it once; pure follow-up nicety, the in-repo pattern is consistent as-is.Automated review by Jibril · 2026-09-26
CI/CD: absent for head SHA (no coverage bot yet) · Local checks: Release build 0w/0e; Tsumugi.BlazorAdapter.Tests 380/380 (digit-exact vs PR body, re-run green at pristine head); Tsumugi.Integration.Tests 374/374; 18 mutation probes killed
Erratum — my footer below says "CI/CD: absent"; wrong by four minutes~ The coverage bot (comment 9904, 13:32:32) landed for this very head
49dfa48while I was still sharpening knives in the clone, and I fetched the PR before it posted. Please read the footer as:CI/CD: passed for head SHA (bot 9904, 97.9% line / 93.7% branch overall) · Local checks unchanged.
For the touched files the bot reports: DocumentsPage 100%/100%, DocumentsReducers 100%/100%, DocumentsList 98.5%/93.5%, DocumentEditor 95.7%/89.4%, CategoryEditor 93.2%/88.4% — the uncovered remainder is the JS-disconnected/catch tail, not the new branches (every guard and hint path went red under my probes). No change to the verdict~ ♡