mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-06 19:57:26 -08:00
Gate subtitle name highlighting on character dictionary setting
- Disable `getNameMatchEnabled` when `anilist.characterDictionary.enabled` is false - Wire character-dictionary enablement into main subtitle tokenization deps - Add runtime deps test coverage and record task/plan docs
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: TASK-98
|
||||
title: Gate subtitle character-name highlighting on character dictionary enablement
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-07 00:54'
|
||||
updated_date: '2026-03-07 00:56'
|
||||
labels:
|
||||
- subtitle
|
||||
- character-dictionary
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-subtitle.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Ensure subtitle tokenization and other annotations continue to work, but character-name lookup/highlighting is disabled whenever the AniList character dictionary feature is disabled. This avoids unnecessary name-match processing when the backing dictionary is unavailable.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When anilist.characterDictionary.enabled is false, subtitle tokenization does not request character-name match metadata or highlight character names.
|
||||
- [x] #2 When anilist.characterDictionary.enabled is true and subtitleStyle.nameMatchEnabled is true, existing character-name matching behavior remains enabled.
|
||||
- [x] #3 Subtitle tokenization, JLPT, frequency, and other non-name annotation behavior remain unchanged when character dictionaries are disabled.
|
||||
- [x] #4 Automated tests cover the runtime gating behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing test in `src/main/runtime/subtitle-tokenization-main-deps.test.ts` proving name-match enablement resolves to false when `anilist.characterDictionary.enabled` is false even if `subtitleStyle.nameMatchEnabled` is true.
|
||||
2. Update `src/main/runtime/subtitle-tokenization-main-deps.ts` and `src/main.ts` so subtitle tokenization only enables name matching when both the subtitle setting and the character dictionary setting are enabled.
|
||||
3. Run focused Bun tests for the updated runtime deps and subtitle processing seams.
|
||||
4. If verification stays green, check off acceptance criteria and record the result.
|
||||
|
||||
Implementation plan saved in `docs/plans/2026-03-06-character-name-gating.md`.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Created plan doc `docs/plans/2026-03-06-character-name-gating.md` after user approved the narrow runtime-gating approach. Proceeding with TDD from the subtitle tokenization main-deps seam.
|
||||
|
||||
Implemented the gate at the subtitle tokenization runtime-deps boundary so `getNameMatchEnabled` is false unless both `subtitleStyle.nameMatchEnabled` and `anilist.characterDictionary.enabled` are true.
|
||||
|
||||
Verification: `bun test src/main/runtime/subtitle-tokenization-main-deps.test.ts`, `bun test src/core/services/subtitle-processing-controller.test.ts`, `bun run typecheck`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Character-name lookup/highlighting is now suppressed when the AniList character dictionary is disabled, while subtitle tokenization and other annotation paths remain active. Added focused runtime-deps coverage and wired the main runtime to pass the character-dictionary enabled flag into subtitle tokenization.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
50
docs/plans/2026-03-06-character-name-gating.md
Normal file
50
docs/plans/2026-03-06-character-name-gating.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Character Name Gating Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Disable subtitle character-name lookup/highlighting when the AniList character dictionary feature is disabled, while keeping tokenization and all other annotations working.
|
||||
|
||||
**Architecture:** Gate `getNameMatchEnabled` at the runtime-deps boundary used by subtitle tokenization. Keep the tokenizer pipeline intact and only suppress character-name metadata requests when `anilist.characterDictionary.enabled` is false, regardless of `subtitleStyle.nameMatchEnabled`.
|
||||
|
||||
**Tech Stack:** TypeScript, Bun test runner, Electron main/runtime wiring.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add runtime gating coverage
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/runtime/subtitle-tokenization-main-deps.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a test proving `getNameMatchEnabled()` resolves to `false` when `getCharacterDictionaryEnabled()` is `false` even if `getNameMatchEnabled()` is `true`.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `bun test src/main/runtime/subtitle-tokenization-main-deps.test.ts`
|
||||
Expected: FAIL because the deps builder does not yet combine the two flags.
|
||||
|
||||
### Task 2: Implement minimal runtime gate
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/runtime/subtitle-tokenization-main-deps.ts`
|
||||
- Modify: `src/main.ts`
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add `getCharacterDictionaryEnabled` to the main handler deps and make the built `getNameMatchEnabled` return true only when both the subtitle setting and the character dictionary setting are enabled.
|
||||
|
||||
**Step 4: Run tests to verify green**
|
||||
|
||||
Run: `bun test src/main/runtime/subtitle-tokenization-main-deps.test.ts`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 3: Verify no regressions in related tokenization seams
|
||||
|
||||
**Files:**
|
||||
- Modify: none unless failures reveal drift
|
||||
|
||||
**Step 5: Run focused verification**
|
||||
|
||||
Run: `bun test src/core/services/subtitle-processing-controller.test.ts src/main/runtime/subtitle-tokenization-main-deps.test.ts`
|
||||
Expected: PASS.
|
||||
@@ -2436,6 +2436,7 @@ const {
|
||||
'subtitle.annotation.jlpt',
|
||||
getResolvedConfig().subtitleStyle.enableJlpt,
|
||||
),
|
||||
getCharacterDictionaryEnabled: () => getResolvedConfig().anilist.characterDictionary.enabled,
|
||||
getNameMatchEnabled: () => getResolvedConfig().subtitleStyle.nameMatchEnabled,
|
||||
getFrequencyDictionaryEnabled: () =>
|
||||
getRuntimeBooleanOption(
|
||||
|
||||
@@ -54,6 +54,34 @@ test('tokenizer deps builder records known-word lookups and maps readers', () =>
|
||||
assert.deepEqual(calls, ['lookup:true', 'lookup:false', 'set-window', 'set-ready', 'set-init']);
|
||||
});
|
||||
|
||||
test('tokenizer deps builder disables name matching when character dictionary is disabled', () => {
|
||||
const deps = createBuildTokenizerDepsMainHandler({
|
||||
getYomitanExt: () => null,
|
||||
getYomitanParserWindow: () => null,
|
||||
setYomitanParserWindow: () => undefined,
|
||||
getYomitanParserReadyPromise: () => null,
|
||||
setYomitanParserReadyPromise: () => undefined,
|
||||
getYomitanParserInitPromise: () => null,
|
||||
setYomitanParserInitPromise: () => undefined,
|
||||
isKnownWord: () => false,
|
||||
recordLookup: () => undefined,
|
||||
getKnownWordMatchMode: () => 'surface',
|
||||
getNPlusOneEnabled: () => true,
|
||||
getMinSentenceWordsForNPlusOne: () => 3,
|
||||
getJlptLevel: () => 'N2',
|
||||
getJlptEnabled: () => true,
|
||||
getCharacterDictionaryEnabled: () => false,
|
||||
getNameMatchEnabled: () => true,
|
||||
getFrequencyDictionaryEnabled: () => true,
|
||||
getFrequencyDictionaryMatchMode: () => 'surface',
|
||||
getFrequencyRank: () => 5,
|
||||
getYomitanGroupDebugEnabled: () => false,
|
||||
getMecabTokenizer: () => null,
|
||||
})();
|
||||
|
||||
assert.equal(deps.getNameMatchEnabled?.(), false);
|
||||
});
|
||||
|
||||
test('mecab tokenizer check creates tokenizer once and runs availability check', async () => {
|
||||
const calls: string[] = [];
|
||||
type Tokenizer = { id: string };
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { TokenizerDepsRuntimeOptions } from '../../core/services/tokenizer'
|
||||
|
||||
type TokenizerMainDeps = TokenizerDepsRuntimeOptions & {
|
||||
getJlptEnabled: NonNullable<TokenizerDepsRuntimeOptions['getJlptEnabled']>;
|
||||
getCharacterDictionaryEnabled?: () => boolean;
|
||||
getNameMatchEnabled?: NonNullable<TokenizerDepsRuntimeOptions['getNameMatchEnabled']>;
|
||||
getFrequencyDictionaryEnabled: NonNullable<
|
||||
TokenizerDepsRuntimeOptions['getFrequencyDictionaryEnabled']
|
||||
@@ -46,7 +47,8 @@ export function createBuildTokenizerDepsMainHandler(deps: TokenizerMainDeps) {
|
||||
getJlptEnabled: () => deps.getJlptEnabled(),
|
||||
...(deps.getNameMatchEnabled
|
||||
? {
|
||||
getNameMatchEnabled: () => deps.getNameMatchEnabled!(),
|
||||
getNameMatchEnabled: () =>
|
||||
deps.getCharacterDictionaryEnabled?.() !== false && deps.getNameMatchEnabled!(),
|
||||
}
|
||||
: {}),
|
||||
getFrequencyDictionaryEnabled: () => deps.getFrequencyDictionaryEnabled(),
|
||||
|
||||
Reference in New Issue
Block a user