fix: preserve keyboard subtitle navigation state

This commit is contained in:
2026-03-05 18:39:40 -08:00
parent 23623ad1e1
commit 0cac446725
9 changed files with 434 additions and 83 deletions

View File

@@ -51,7 +51,7 @@ test('prefers scanning parser when scanning candidate has more than one token',
test('keeps scanning parser candidate when scanning candidate is single token', () => {
const parseResults = [
makeParseItem('scanning-parser', [
[{ text: '俺は公園にいきたい', reading: 'おれはこうえんにいきたい' }],
[{ text: '俺は公園にいきたい', reading: 'おれはこうえんにいきたい', headword: '行きたい' }],
]),
makeParseItem('mecab', [
[{ text: '俺', reading: 'おれ', headword: '俺' }],
@@ -96,3 +96,34 @@ test('returns null when only mecab-source candidates are present', () => {
const tokens = selectYomitanParseTokens(parseResults, () => false, 'headword');
assert.equal(tokens, null);
});
test('returns null when scanning parser candidates have no dictionary headwords', () => {
const parseResults = [
makeParseItem('scanning-parser', [
[{ text: 'これは', reading: 'これは' }],
[{ text: 'テスト', reading: 'てすと' }],
]),
];
const tokens = selectYomitanParseTokens(parseResults, () => false, 'headword');
assert.equal(tokens, null);
});
test('drops scanning parser tokens which have no dictionary headword', () => {
const parseResults = [
makeParseItem('scanning-parser', [
[{ text: '(ダクネスの荒い息)', reading: 'だくねすのあらいいき' }],
[{ text: 'アクア', reading: 'あくあ', headword: 'アクア' }],
[{ text: 'トラウマ', reading: 'とらうま', headword: 'トラウマ' }],
]),
];
const tokens = selectYomitanParseTokens(parseResults, () => false, 'headword');
assert.deepEqual(
tokens?.map((token) => ({ surface: token.surface, headword: token.headword })),
[
{ surface: 'アクア', headword: 'アクア' },
{ surface: 'トラウマ', headword: 'トラウマ' },
],
);
});

View File

@@ -130,6 +130,7 @@ export function mapYomitanParseResultItemToMergedTokens(
const tokens: MergedToken[] = [];
let charOffset = 0;
let validLineCount = 0;
let hasDictionaryMatch = false;
for (const line of content) {
if (!isYomitanParseLine(line)) {
@@ -163,7 +164,13 @@ export function mapYomitanParseResultItemToMergedTokens(
const start = charOffset;
const end = start + combinedSurface.length;
charOffset = end;
const headword = combinedHeadword || combinedSurface;
if (!combinedHeadword) {
// No dictionary-backed headword for this merged unit; skip it entirely so
// downstream keyboard/frequency/JLPT flows only operate on lookup-backed tokens.
continue;
}
hasDictionaryMatch = true;
const headword = combinedHeadword;
tokens.push({
surface: combinedSurface,
@@ -182,7 +189,7 @@ export function mapYomitanParseResultItemToMergedTokens(
});
}
if (validLineCount === 0 || tokens.length === 0) {
if (validLineCount === 0 || tokens.length === 0 || !hasDictionaryMatch) {
return null;
}