Rework the global protected terms UI, and fix the header controls - #94
Merged
Merged
Conversation
Record durable product truth for the Tracker web UI: primary and secondary users, positioning, operating context, capabilities and constraints, the binding watercolor brand identity, and the evidence on hand. Also records the Impeccable build-path workflow default and gitignores the skill's machine-local state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
Nx 22 wrote `analytics: false` and reformatted the `translationFixtures` named input; pnpm recorded `@swc/core` and `nx` as ignored built dependencies. Committing so the settings stop reappearing as local noise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
Replace the chip multi-select on /settings with a list that supports adding, deleting and renaming terms, with an explicit save. Interaction: - Deleting keeps the row visible, struck through and marked, with Undo; it is written only on Save, so a pending removal is always reviewable and reversible. - Renaming happens inline on the row; Enter commits, Escape cancels, and a collision is reported in place. - Re-adding a term that is marked for removal takes the removal back instead of reporting a duplicate. - A filter appears past eight terms, mounted inside the panel so it survives a no-match state. Adding or renaming a term the filter would hide drops the filter, and the affected row is scrolled into view. Layout: - The save controls live in the page header, so they cannot scroll out of view. - The page is a fixed-height column: the list grows with its rows until the viewport runs out, then scrolls internally, keeping the add field and the filter on screen. - Below 500px of viewport height the hint and the file path give up their rows. Terms are listed and saved alphabetically: the list is a set, and a sorted file keeps its Git diffs minimal. Fixes found while building this: - Angular Material's typography painted the page title black in both themes, making it invisible in dark mode. - Six text and control colours fell below their contrast floors; badge, error and icon colours are now mixed toward the foreground so they invert with the theme. - Material's 48px icon-button touch target overflowed the 45px rows and made the scroller report phantom overflow. It is dropped on fine pointers and restored at 44px under `pointer: coarse`. - `.settings-page` needs an explicit `box-sizing: border-box`; the app has no global reset, so `height: 100%` plus padding overflowed the viewport. Adds 20 translated resources across all six locales; the two ICU plurals carry per-locale plural categories. The protected-terms hint changed because the "press Enter or comma" mechanic no longer exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
The settings hint credited auto-translation with keeping protected terms verbatim. Auto-translation does not read the list at all — the protection it applies is placeholder protection, which swaps ICU placeholders for markers and is unrelated. The list is enforced in export and import instead: export marks each string with the protected terms found in its source so translators and MT services know what to leave alone, and import rejects an incoming translation that altered one. The hint now names both, matching docs/features/protected-terms.md. The Russian translation is hand-corrected: the machine rendered "exports" and "imports" as "export systems" and "import systems", which are not things. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
…reens The save controls moved into the page header, where a single non-wrapping row put German's "Alle zurücksetzen" and "Speichern" 84px past a 390px viewport. `.settings-page` clips its overflow, so the Save button was invisible and unreachable rather than merely off-grid — and a horizontal-overflow check on the document reported clean. The row now wraps and the buttons never shrink; the status pill gives up width instead and truncates its label. A flex child defaults to `min-width: auto`, so the pill needed that cleared on both itself and its span before it would yield. Verified with both controls enabled across en, de, es, fr-ca, ja and ru at 390, 768 and 1440px. Below roughly 397px the app shell itself overflows horizontally, on /collections equally, which is a pre-existing limit and untouched here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
…lout The hint was a loose paragraph between the heading and the file path, reading as overflow from the heading rather than as its own thing. It becomes a bordered, tinted block with an info icon. Built from the existing `--color-info` token, so it inverts with the theme. The text takes its cast from that hue rather than staying grey, which a coloured surface calls for. No accent rule down the side: the tint and the icon already say what kind of block this is. Measured at 8.8:1 light and 11.4:1 dark for the text, 4.0:1 and 7.7:1 for the icon. The short-viewport rule that reclaims these rows below 500px now targets the callout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
The three app controls read as three floating items rather than one group, and two of them wore each other's icon. - Gaps were 16px / 8px / 16px between peers. They are now one 4px gap set once on a `.toolbar-actions` container, with the per-button margins removed. - The gear opened the theme menu and a sliders icon went to Settings. The gear now goes to Settings, which is what a gear means everywhere else. - The theme switcher wears the theme you are looking at — light_mode or dark_mode — instead of a fixed glyph. On `system` it shows the resolved theme; which mode is selected stays legible through the label and the menu check. - The locale picker was a mat-icon-button holding an icon *and* a text label, so the globe was clipped to a sliver by the button's fixed square. It is a text button sized to its content, matched to the icon buttons' 40px height. - Order is language, appearance, settings, with Settings as the right anchor and the only route among the three. DOM order follows, so focus order does too. The label resolves through the transloco pipe rather than a computed calling `translate`: a computed caches whatever it reads first, and first read happens before the translation file loads, so it served the raw key forever after. Not fixed here, and pre-existing: on /browser the app shell renders wider than the viewport below about 1050px — `.app-header` measures 983px inside a 900px `.app-container` — because that route's content forces the grid column open and drags the header with it. It pushes Settings off the right edge. The control group itself is byte-identical in width to before (172px from the divider), so this is a shell issue, not a header one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
Clicking it there did nothing, with no sign that it would. It uses `disabledInteractive` rather than a plain `disabled`, so the control keeps its place in the tab order and announces `aria-disabled` instead of disappearing for keyboard users. `aria-current="page"` says why it is inert, which a bare disabled state does not. Active state comes from routerLinkActive with exact matching, so it recovers on navigating away. Adds the first spec for this component, covering the inert/live transition, its focusability while inert, and the theme button's icon across light, dark and system. The shared transloco testing fixture gains the header, locale, settings and theme keys it was missing. Material's disabled colour reaches 2.3:1 in light and 3.6:1 in dark. Left as it is: WCAG exempts inactive controls, and matching every other disabled control in the app is worth more here than singling this one out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
…spec The CDK BreakpointObserver subscribes through mql.addListener, which the spec's matchMedia stub did not provide, so every AppHeader test emitted an unhandled TypeError and failed the suite in CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RifJr7xyhZkaTWn4ssV1b4
The collections page was a launcher with a lot of dead space: cards were a fixed 160px tall with an empty band between the folder path and the locale pills, the grid used hard 1/2/3-column breakpoints that left an empty column, collections came out in arbitrary config order, and the base locale — the one distinction that matters when reading a locale list — was rendered the same as every other pill. - Rebuild the card as a single element (no nested accent/inner elements, no decorative backdrop-filter), sized by its content - Sort collections by name, case-insensitively - Put the base locale first and mark it; cap the chip row at four chips so cards in a row stay flush, with the rest behind a "+n" chip and a tooltip - Switch the grid to auto-fill minmax(300px, 1fr) - Shrink the page title to an operate-appropriate size and add a collection count - Make read-only collections unmistakable: an amber lock icon plus an explanatory note on the card - Move the folder path and count text off --color-text-tertiary, which failed AA at 2.2:1, and clamp the path to two lines with a tooltip - Make the open-affordance arrow persistent rather than hover-only - Add a name/folder filter above six collections, mirroring the settings page, with a no-matches state Also theme ::selection, caret-color, accent-color and scrollbar-color app-wide; they were browser defaults. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
readOnlyBadge shipped the English "Read-only" in all five non-base locales. The four fr-ca entries already held correct French but carried no metadata, so they still counted as untranslated. - Translate collections.card.readOnlyBadge into es, fr-ca, ru, de and ja - Re-apply the existing fr-ca values for collections.title, addButton, card.edit and card.delete so the CLI records their checksums and status No collection resource under collections/ is left new or stale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The collections card redesign replaced the read-only badge with a note row that uses readOnlyTooltip, leaving readOnlyBadge referenced nowhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The theme button previously wore the *resolved* theme, so selecting System looked identical to pinning light or dark. It now wears the selected mode, giving System its own monitor icon — matching the glyph in the menu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 0.18.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The protected-terms chip multi-select on
/settingsbecomes a real list, and the app header's three controls get sorted out.Protected terms
REMOVING, with Undo. It reaches the file only on Save, so a pending removal is reviewable and reversible without a confirm dialog.docs/features/protected-terms.mdalready promises.Header
systemit shows the resolved theme; which mode is selected stays legible through the label and the menu check.mat-icon-buttonholding an icon and a text label, so the globe was clipped to a sliver by the button's fixed square./settings, where clicking it did nothing.Worth a reviewer's attention
The explanation text was wrong, and not in the way it looked. It credited auto-translation with keeping terms verbatim. Auto-translation doesn't read the list at all — the "protection" in that pipeline is placeholder protection, swapping ICU placeholders for markers. The list is enforced in export (marks terms found in each source string for translators) and import (rejects a translation that altered one). The copy now names both.
Two Angular traps that are worth knowing because the same shapes exist elsewhere in this app:
computed()callingtransloco.translate()caches whatever it reads first, and first read happens before the translation file loads — so it serves the raw key forever after. The theme label now resolves through the pipe.keysTextin the same component has the same shape and only escapes it because another dependency changes after load..settings-pageneeded an explicitbox-sizing: border-box. There's no global reset, soheight: 100%plus padding overflowed the viewport by exactly the padding.Settings goes inert via
disabledInteractive, not a plaindisabled. A plaindisableddrops the control out of the tab order with no explanation; this keeps it focusable, announcesaria-disabled, and addsaria-current="page"to say why. Easy to change if you'd rather it be fully removed.Found but deliberately not fixed
The app shell renders wider than the viewport on
/browser..app-headermeasures 983px inside a 900px.app-containerbelow about 1050px, because that route's content forces the grid column open and drags the header with it — pushing Settings off the right edge./collectionsat the same width is clean, and the control group is byte-identical in width to before. Same mechanism puts a hard ~397px floor under the whole app. The candidate fix isgrid-template-columns: minmax(0, 1fr)on.app-container, but.app-contentisoverflow-x: hidden, so that would clip the browser route rather than scroll it. Needs a decision about that surface.Minor, untouched: the theme menu renders its check between the icon and the label ("☀ ✓ Light") rather than trailing it, and the
.activerow styling doesn't appear to take.Accessibility
Six contrast failures on the settings page were measured and fixed, including the page title rendering black-on-black in dark mode — Angular Material's typography was overriding the theme token, and that predates this branch. Badge, error and icon colours are now mixed toward the foreground so they invert with the theme.
Also fixed: Material's 48px icon-button touch target overflowed the 45px rows and made the scroller report phantom overflow, so a scrollbar appeared on lists that fit. Dropped on fine pointers, restored at 44px under
pointer: coarse.i18n
21 new resources across all six shipped locales, added through the CLI. The two ICU plurals were written by hand with correct per-locale plural categories (Russian gets one/few/many). One Russian string was hand-corrected where the machine rendered "exports"/"imports" as "export systems"/"import systems".
settings.protectedTermsHintchanged twice: once because the "press Enter or comma" mechanic no longer exists, once for the accuracy fix above.Testing
523 tests pass, lint clean, production build succeeds.
settings.spec.tsgoes from 6 tests to 28.app-header.spec.tsis new — the component had no spec at all.en,de,es,fr-ca,ja,ruat 320–1440px, light and dark, including the save round-trip actually writing the file sorted.Note
Two commits on this branch predate this work and are included here:
0f8542c(nx/pnpm settings) and2f759ed(PRODUCT.md). Split them out if you'd rather they land separately.🤖 Generated with Claude Code