From 75566a49bf5c9506a039ac10cf27e998c0f66c1a Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 26 Jul 2026 01:33:04 -0700 Subject: [PATCH] fix(overlay): gate the maturity legend on known-word highlighting The session help color legend showed the four tier rows whenever maturityEnabled was on, even with known-word highlighting off, where no token is ever tinted. Extract isKnownWordMaturityLegendEnabled so the legend requires both toggles, matching getKnownWordMaturityEnabled. --- src/renderer/modals/session-help.test.ts | 30 ++++++++++++++++++++++++ src/renderer/modals/session-help.ts | 20 ++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/renderer/modals/session-help.test.ts b/src/renderer/modals/session-help.test.ts index 59721ea2..d8d5df10 100644 --- a/src/renderer/modals/session-help.test.ts +++ b/src/renderer/modals/session-help.test.ts @@ -9,7 +9,9 @@ import { createSessionHelpModal, describeSessionHelpCommand, formatSessionHelpKeybinding, + isKnownWordMaturityLegendEnabled, } from './session-help.js'; +import type { RuntimeOptionId, RuntimeOptionState } from '../../types/runtime-options.js'; test('session help describes sub-seek commands as subtitle-line navigation', () => { assert.equal(describeSessionHelpCommand(['sub-seek', 1]), 'Jump to next subtitle'); @@ -104,6 +106,34 @@ test('session help builds rows from canonical session bindings and fixed overlay assert.ok(rows.some((row) => row.shortcut === 'Y then D' && row.action === 'Toggle DevTools')); }); +function booleanRuntimeOption(id: RuntimeOptionId, value: boolean): RuntimeOptionState { + return { + id, + label: id, + scope: 'subtitle', + valueType: 'boolean', + value, + allowedValues: [true, false], + requiresRestart: false, + }; +} + +test('maturity legend requires both known-word highlighting and maturity coloring', () => { + const highlightOn = booleanRuntimeOption('subtitle.annotation.knownWords.highlightEnabled', true); + const highlightOff = booleanRuntimeOption( + 'subtitle.annotation.knownWords.highlightEnabled', + false, + ); + const maturityOn = booleanRuntimeOption('subtitle.annotation.knownWords.maturityEnabled', true); + const maturityOff = booleanRuntimeOption('subtitle.annotation.knownWords.maturityEnabled', false); + + assert.equal(isKnownWordMaturityLegendEnabled([highlightOn, maturityOn]), true); + assert.equal(isKnownWordMaturityLegendEnabled([highlightOff, maturityOn]), false); + assert.equal(isKnownWordMaturityLegendEnabled([highlightOn, maturityOff]), false); + assert.equal(isKnownWordMaturityLegendEnabled([maturityOn]), false); + assert.equal(isKnownWordMaturityLegendEnabled([]), false); +}); + function colorLegendRows(input: Parameters[0]) { const sections = buildSessionHelpSections(input); return sections.find((section) => section.title === 'Color legend')?.rows ?? []; diff --git a/src/renderer/modals/session-help.ts b/src/renderer/modals/session-help.ts index 5acabe85..1a8321a7 100644 --- a/src/renderer/modals/session-help.ts +++ b/src/renderer/modals/session-help.ts @@ -1,4 +1,5 @@ import type { ModalStateReader, RendererContext } from '../context'; +import type { RuntimeOptionId, RuntimeOptionState } from '../../types/runtime-options'; import { buildSessionHelpSections, type SessionHelpSection, @@ -19,6 +20,19 @@ type SessionHelpBindingInfo = { fallbackUnavailable: boolean; }; +/** + * Tiers only render when known-word highlighting is also on, matching + * getKnownWordMaturityEnabled in anki-integration/known-word-maturity. + */ +export function isKnownWordMaturityLegendEnabled(runtimeOptions: RuntimeOptionState[]): boolean { + const isOn = (id: RuntimeOptionId): boolean => + runtimeOptions.some((option) => option.id === id && option.value === true); + return ( + isOn('subtitle.annotation.knownWords.highlightEnabled') && + isOn('subtitle.annotation.knownWords.maturityEnabled') + ); +} + /** * Maturity coloring is a live runtime toggle, so the color legend reads it from * runtime options instead of the resolved subtitle style. A missing or failing @@ -26,11 +40,7 @@ type SessionHelpBindingInfo = { */ async function readKnownWordMaturityEnabled(): Promise { try { - const runtimeOptions = await window.electronAPI.getRuntimeOptions(); - return runtimeOptions.some( - (option) => - option.id === 'subtitle.annotation.knownWords.maturityEnabled' && option.value === true, - ); + return isKnownWordMaturityLegendEnabled(await window.electronAPI.getRuntimeOptions()); } catch { return false; }