fix(web): close the UI follow-ups from #456 - #496
Conversation
The inline search script in base.html interpolated `recipe.path` and `recipe.name` straight into innerHTML, so a recipe name containing markup was rendered as markup. Escape both through the same helper search.js uses in static mode, and cover it with an E2E test that serves a fabricated result through a route intercept. Closes #487 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
clearList() added `hidden` to #shopping-list-results and nothing ever removed it, so the "no items" message rendered into #list-content stayed invisible until a reload, and live updates from another tab rendered into a hidden container. Drop the toggle and have displayShoppingList() un-hide the container whenever it renders. Closes #489 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
The "Saved" label on /edit/<recipe> was not the result of a save: an
unconditional `updateSaveStatus('saved')` ran at script load as an
"initial status". No PUT fires on open and the file's mtime is
untouched, so the fix is to leave the status empty until a real save.
The new editor spec asserts no PUT, an empty status and an unchanged
mtime after opening the page.
Closes #491
Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
Endpoint summaries on /api-docs become h3 so the h4 sub-labels no longer sit directly under the section h2, and section names in the recipe steps column become h2 since they follow the page h1 directly. Closes #492 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
The icon-only buttons carried English aria-label/title literals (theme, keyboard shortcuts, more options, preferences, dismiss, scale stepper). Add aria-* keys to all seven locales and route the templates through tr.t(). The shortcuts modal's Close button lives in keyboard-shortcuts.js, so base.html now exposes window.__STRINGS__ and the script reads the label from there with an English fallback. Closes #488 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
The index was sorted in byte order, so uppercase names came before lowercase ones and "Recipe 10" landed before "Recipe 9". The client then re-sorted with Intl.Collator (numeric, case-insensitive), which left the no-JS static-site first paint in a different order and opened a reorder flash. Sort on the server with the same key the client uses: directories first, then a case-insensitive natural comparison that treats digit runs numerically, with byte order as a deterministic tiebreak. Closes #486 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
…site copy_static_assets copied the whole embedded static/ tree, so the generated site included static/css/input.css, static/css/components.css and static/js/src/*, none of which any page links. Skip those build inputs so only output.css, cooking-mode.css and the runtime scripts are written. Closes #493 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
The Cooklang highlight style hard-coded hex colours in editor.js, so they did not follow the theme and some read poorly on the dark editor surface. Give each token kind a fixed .cm-cook-* class instead and colour it from the theme tokens in input.css, next to the other CodeMirror rules, so every colour flips with light/dark like the rest of the editor chrome. Closes #490 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
The server's natural sort folds case and digits but not accents, so the client no longer re-sorts on load for name/asc: the first paint is the canonical order and there is no reorder on load. The comment on natural_key says what it does and does not fold, and a test pins it. Refs #486 Claude-Session: https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu
Review of #496Nice work — this is a well-organized PR: each of the eight follow-ups from #456 is a focused, isolated commit with its own test, and the descriptions make it easy to verify each fix independently. I read through the full diff (locales, Correctness
Minor / nitpicks (non-blocking)
Security
Test coverageGood coverage overall — each behavioral fix (#486, #487, #489, #491, #493) has a corresponding unit or e2e test. #488 (aria-label i18n) and #490/#492 (CSS/heading-level changes) are lower-risk template/CSS tweaks and reasonably left without dedicated new tests, though a quick a11y-focused Playwright assertion (e.g., checking Overall: looks ready to merge pending your own fmt/clippy/test run confirmation. Nice attention to detail on tying each fix back to a regression test. |
Fixes the eight follow-ups listed in #456, one commit each.
innerHTML, matchingsearch.js. A test injects a fabricated result and asserts nothing executes.aria-*keys in all seven locales; the shortcuts modal reads its close label fromwindow.__STRINGS__..cm-cook-*ininput.css) instead of hex literals ineditor.js, so they flip with the theme.h3and sectioned recipes' step headings areh2, so no page skips a heading level.input.css,components.cssandstatic/js/src/are excluded.Verification
cargo fmt,cargo clippy --all-targets,cargo testclean.Filed while reviewing
{}are not highlighted.Closes #486, closes #487, closes #488, closes #489, closes #490, closes #491, closes #492, closes #493.
https://claude.ai/code/session_013urND2B6Y3Z7WQuDpE8ZDu