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',
|
'subtitle.annotation.jlpt',
|
||||||
getResolvedConfig().subtitleStyle.enableJlpt,
|
getResolvedConfig().subtitleStyle.enableJlpt,
|
||||||
),
|
),
|
||||||
|
getCharacterDictionaryEnabled: () => getResolvedConfig().anilist.characterDictionary.enabled,
|
||||||
getNameMatchEnabled: () => getResolvedConfig().subtitleStyle.nameMatchEnabled,
|
getNameMatchEnabled: () => getResolvedConfig().subtitleStyle.nameMatchEnabled,
|
||||||
getFrequencyDictionaryEnabled: () =>
|
getFrequencyDictionaryEnabled: () =>
|
||||||
getRuntimeBooleanOption(
|
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']);
|
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 () => {
|
test('mecab tokenizer check creates tokenizer once and runs availability check', async () => {
|
||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
type Tokenizer = { id: string };
|
type Tokenizer = { id: string };
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { TokenizerDepsRuntimeOptions } from '../../core/services/tokenizer'
|
|||||||
|
|
||||||
type TokenizerMainDeps = TokenizerDepsRuntimeOptions & {
|
type TokenizerMainDeps = TokenizerDepsRuntimeOptions & {
|
||||||
getJlptEnabled: NonNullable<TokenizerDepsRuntimeOptions['getJlptEnabled']>;
|
getJlptEnabled: NonNullable<TokenizerDepsRuntimeOptions['getJlptEnabled']>;
|
||||||
|
getCharacterDictionaryEnabled?: () => boolean;
|
||||||
getNameMatchEnabled?: NonNullable<TokenizerDepsRuntimeOptions['getNameMatchEnabled']>;
|
getNameMatchEnabled?: NonNullable<TokenizerDepsRuntimeOptions['getNameMatchEnabled']>;
|
||||||
getFrequencyDictionaryEnabled: NonNullable<
|
getFrequencyDictionaryEnabled: NonNullable<
|
||||||
TokenizerDepsRuntimeOptions['getFrequencyDictionaryEnabled']
|
TokenizerDepsRuntimeOptions['getFrequencyDictionaryEnabled']
|
||||||
@@ -46,7 +47,8 @@ export function createBuildTokenizerDepsMainHandler(deps: TokenizerMainDeps) {
|
|||||||
getJlptEnabled: () => deps.getJlptEnabled(),
|
getJlptEnabled: () => deps.getJlptEnabled(),
|
||||||
...(deps.getNameMatchEnabled
|
...(deps.getNameMatchEnabled
|
||||||
? {
|
? {
|
||||||
getNameMatchEnabled: () => deps.getNameMatchEnabled!(),
|
getNameMatchEnabled: () =>
|
||||||
|
deps.getCharacterDictionaryEnabled?.() !== false && deps.getNameMatchEnabled!(),
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
getFrequencyDictionaryEnabled: () => deps.getFrequencyDictionaryEnabled(),
|
getFrequencyDictionaryEnabled: () => deps.getFrequencyDictionaryEnabled(),
|
||||||
|
|||||||
Reference in New Issue
Block a user