From f83005bf70e34290ede1a3f115f9d0f8f35d278b Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 2 May 2026 20:57:14 -0700 Subject: [PATCH] fix: preserve jlpt underline color after lookup --- ...line-color-fixed-after-lookup-selection.md | 53 +++++++++++++++++++ changes/318-jlpt-underline-selection.md | 4 ++ src/renderer/style.css | 25 +++++++++ src/renderer/subtitle-render.test.ts | 18 +++++++ 4 files changed, 100 insertions(+) create mode 100644 backlog/tasks/task-318 - Keep-JLPT-underline-color-fixed-after-lookup-selection.md create mode 100644 changes/318-jlpt-underline-selection.md diff --git a/backlog/tasks/task-318 - Keep-JLPT-underline-color-fixed-after-lookup-selection.md b/backlog/tasks/task-318 - Keep-JLPT-underline-color-fixed-after-lookup-selection.md new file mode 100644 index 00000000..15a8d5e3 --- /dev/null +++ b/backlog/tasks/task-318 - Keep-JLPT-underline-color-fixed-after-lookup-selection.md @@ -0,0 +1,53 @@ +--- +id: TASK-318 +title: Keep JLPT underline color fixed after lookup selection +status: Done +assignee: + - '@Codex' +created_date: '2026-05-03 03:17' +updated_date: '2026-05-03 03:19' +labels: + - overlay + - jlpt + - renderer +dependencies: [] +priority: medium +--- + +## Description + + +Looking up a subtitle token can leave browser/Yomitan selection styling active. If that token has a JLPT class and another annotation class, the underline must remain the JLPT level color because underline color represents static JLPT classification, not the currently active annotation or lookup state. + + +## Acceptance Criteria + +- [x] #1 JLPT subtitle underlines retain their configured N1-N5 color after lookup/selection styling is applied. +- [x] #2 JLPT tokens that also have known, N+1, name, or frequency annotation classes keep their annotation text color behavior without changing the JLPT underline color. +- [x] #3 Renderer regression coverage verifies the CSS contract for the combined JLPT plus annotation case. + + +## Implementation Plan + + +1. Add a focused CSS regression in `src/renderer/subtitle-render.test.ts` for JLPT tokens combined with higher-priority annotation classes and lookup/selection styling. +2. Run the focused renderer test and confirm it fails because selection rules do not lock `text-decoration-color`. +3. Update `src/renderer/style.css` to explicitly preserve JLPT underline decoration color in lookup/selection state selectors without changing text color priority. +4. Re-run the focused renderer test, then run the smallest relevant verification gate. + + +## Implementation Notes + + +Verified TDD red/green for renderer CSS contract: `bun test src/renderer/subtitle-render.test.ts` first failed because `word-jlpt-n1::selection` lock was missing, then passed after adding explicit JLPT `text-decoration-color` selection rules. Also ran `bun run changelog:lint` and `bun run typecheck` successfully. + + +## Final Summary + + +Fixed JLPT subtitle underline color drift after dictionary lookup/selection by adding explicit `::selection` decoration-color locks for N1-N5 token classes in `src/renderer/style.css`. This preserves the JLPT underline as static classification while leaving known/N+1/name/frequency text color priority intact. + +Added renderer CSS regression coverage for the JLPT selection lock and a user-visible changelog fragment. + +Checks: `bun test src/renderer/subtitle-render.test.ts`; `bun run changelog:lint`; `bun run typecheck`. + diff --git a/changes/318-jlpt-underline-selection.md b/changes/318-jlpt-underline-selection.md new file mode 100644 index 00000000..75ec64c1 --- /dev/null +++ b/changes/318-jlpt-underline-selection.md @@ -0,0 +1,4 @@ +type: fixed +area: overlay + +- Overlay: Kept JLPT subtitle underlines at their JLPT color after dictionary lookups, even when the token also has another annotation color. diff --git a/src/renderer/style.css b/src/renderer/style.css index 80dfca07..944eb7f6 100644 --- a/src/renderer/style.css +++ b/src/renderer/style.css @@ -997,6 +997,31 @@ body.settings-modal-open [data-subminer-yomitan-popup-host='true'] { -webkit-text-fill-color: var(--subtitle-frequency-band-5-color, #8aadf4) !important; } +#subtitleRoot .word.word-jlpt-n1::selection, +#subtitleRoot .word.word-jlpt-n1 .c::selection { + text-decoration-color: var(--subtitle-jlpt-n1-color, #ed8796) !important; +} + +#subtitleRoot .word.word-jlpt-n2::selection, +#subtitleRoot .word.word-jlpt-n2 .c::selection { + text-decoration-color: var(--subtitle-jlpt-n2-color, #f5a97f) !important; +} + +#subtitleRoot .word.word-jlpt-n3::selection, +#subtitleRoot .word.word-jlpt-n3 .c::selection { + text-decoration-color: var(--subtitle-jlpt-n3-color, #f9e2af) !important; +} + +#subtitleRoot .word.word-jlpt-n4::selection, +#subtitleRoot .word.word-jlpt-n4 .c::selection { + text-decoration-color: var(--subtitle-jlpt-n4-color, #a6e3a1) !important; +} + +#subtitleRoot .word.word-jlpt-n5::selection, +#subtitleRoot .word.word-jlpt-n5 .c::selection { + text-decoration-color: var(--subtitle-jlpt-n5-color, #8aadf4) !important; +} + #subtitleRoot .word:is(.word-jlpt-n1, .word-jlpt-n2, .word-jlpt-n3, .word-jlpt-n4, .word-jlpt-n5):not( .word-known diff --git a/src/renderer/subtitle-render.test.ts b/src/renderer/subtitle-render.test.ts index 323b19ba..2721cea7 100644 --- a/src/renderer/subtitle-render.test.ts +++ b/src/renderer/subtitle-render.test.ts @@ -1064,6 +1064,24 @@ test('subtitle annotation CSS underlines JLPT tokens without changing token colo /-webkit-text-fill-color:\s*var\(--subtitle-hover-token-color,\s*#f4dbd6\)\s*!important;/, ); + for (let level = 1; level <= 5; level += 1) { + const jlptSelectionLockBlock = extractClassBlock( + cssText, + `#subtitleRoot .word.word-jlpt-n${level}::selection`, + ); + assert.ok( + jlptSelectionLockBlock.length > 0, + `word-jlpt-n${level} selection lock should exist`, + ); + assert.match( + jlptSelectionLockBlock, + new RegExp( + `text-decoration-color:\\s*var\\(--subtitle-jlpt-n${level}-color,\\s*#[0-9a-f]{6}\\)\\s*!important;`, + 'i', + ), + ); + } + const selectionBlock = extractClassBlock(cssText, '#subtitleRoot::selection'); assert.match( selectionBlock,