mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-27 16:49:51 -07:00
fix(ci): address PR review and quality gate
This commit is contained in:
@@ -433,6 +433,12 @@
|
|||||||
"nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
|
"nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
|
||||||
"nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight.
|
"nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight.
|
||||||
"knownWordColor": "#a6da95", // Color used for known-word subtitle highlights.
|
"knownWordColor": "#a6da95", // Color used for known-word subtitle highlights.
|
||||||
|
"knownWordMaturityColors": {
|
||||||
|
"new": "#ee99a0", // Color for known words whose Anki cards are new (never reviewed), when maturity highlighting is enabled.
|
||||||
|
"learning": "#b7bdf8", // Color for known words whose Anki cards are in (re)learning, when maturity highlighting is enabled.
|
||||||
|
"young": "#91d7e3", // Color for known words whose Anki cards are in review below the mature threshold, when maturity highlighting is enabled.
|
||||||
|
"mature": "#a6da95" // Color for known words whose Anki cards are at or above the mature interval threshold, when maturity highlighting is enabled.
|
||||||
|
}, // Known word maturity colors setting.
|
||||||
"jlptColors": {
|
"jlptColors": {
|
||||||
"N1": "#ed8796", // N1 setting.
|
"N1": "#ed8796", // N1 setting.
|
||||||
"N2": "#f5a97f", // N2 setting.
|
"N2": "#f5a97f", // N2 setting.
|
||||||
@@ -569,6 +575,8 @@
|
|||||||
}, // Media setting.
|
}, // Media setting.
|
||||||
"knownWords": {
|
"knownWords": {
|
||||||
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
||||||
|
"maturityEnabled": false, // Color known-word highlights by Anki card maturity (new, learning, young, mature) instead of a single color. Requires known-word highlighting. Values: true | false
|
||||||
|
"matureThresholdDays": 21, // Card interval in days at which a known word counts as mature (Anki convention: 21).
|
||||||
"refreshMinutes": 1440, // Minutes between known-word cache refreshes.
|
"refreshMinutes": 1440, // Minutes between known-word cache refreshes.
|
||||||
"addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false
|
"addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false
|
||||||
"matchMode": "headword", // Known-word matching strategy for subtitle annotations. Cache matches always receive known-word highlighting even when POS filters suppress other annotation types. Values: headword | surface
|
"matchMode": "headword", // Known-word matching strategy for subtitle annotations. Cache matches always receive known-word highlighting even when POS filters suppress other annotation types. Values: headword | surface
|
||||||
|
|||||||
@@ -433,6 +433,12 @@
|
|||||||
"nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
|
"nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
|
||||||
"nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight.
|
"nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight.
|
||||||
"knownWordColor": "#a6da95", // Color used for known-word subtitle highlights.
|
"knownWordColor": "#a6da95", // Color used for known-word subtitle highlights.
|
||||||
|
"knownWordMaturityColors": {
|
||||||
|
"new": "#ee99a0", // Color for known words whose Anki cards are new (never reviewed), when maturity highlighting is enabled.
|
||||||
|
"learning": "#b7bdf8", // Color for known words whose Anki cards are in (re)learning, when maturity highlighting is enabled.
|
||||||
|
"young": "#91d7e3", // Color for known words whose Anki cards are in review below the mature threshold, when maturity highlighting is enabled.
|
||||||
|
"mature": "#a6da95" // Color for known words whose Anki cards are at or above the mature interval threshold, when maturity highlighting is enabled.
|
||||||
|
}, // Known word maturity colors setting.
|
||||||
"jlptColors": {
|
"jlptColors": {
|
||||||
"N1": "#ed8796", // N1 setting.
|
"N1": "#ed8796", // N1 setting.
|
||||||
"N2": "#f5a97f", // N2 setting.
|
"N2": "#f5a97f", // N2 setting.
|
||||||
@@ -569,6 +575,8 @@
|
|||||||
}, // Media setting.
|
}, // Media setting.
|
||||||
"knownWords": {
|
"knownWords": {
|
||||||
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
||||||
|
"maturityEnabled": false, // Color known-word highlights by Anki card maturity (new, learning, young, mature) instead of a single color. Requires known-word highlighting. Values: true | false
|
||||||
|
"matureThresholdDays": 21, // Card interval in days at which a known word counts as mature (Anki convention: 21).
|
||||||
"refreshMinutes": 1440, // Minutes between known-word cache refreshes.
|
"refreshMinutes": 1440, // Minutes between known-word cache refreshes.
|
||||||
"addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false
|
"addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false
|
||||||
"matchMode": "headword", // Known-word matching strategy for subtitle annotations. Cache matches always receive known-word highlighting even when POS filters suppress other annotation types. Values: headword | surface
|
"matchMode": "headword", // Known-word matching strategy for subtitle annotations. Cache matches always receive known-word highlighting even when POS filters suppress other annotation types. Values: headword | surface
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import os from 'node:os';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import type { AnkiConnectConfig } from '../types/anki';
|
import type { AnkiConnectConfig } from '../types/anki';
|
||||||
|
import { setLogLevel } from '../logger';
|
||||||
import { KnownWordCacheManager, getKnownWordCacheLifecycleConfig } from './known-word-cache';
|
import { KnownWordCacheManager, getKnownWordCacheLifecycleConfig } from './known-word-cache';
|
||||||
|
|
||||||
interface HarnessNoteInfo {
|
interface HarnessNoteInfo {
|
||||||
@@ -255,8 +256,14 @@ test('getKnownWordTier returns null and skips tier queries when maturity is disa
|
|||||||
|
|
||||||
test('refresh preserves known-word cache when maturity lookup fails', async () => {
|
test('refresh preserves known-word cache when maturity lookup fails', async () => {
|
||||||
const { manager, statePath, clientState, cleanup } = createMaturityHarness(maturityConfig());
|
const { manager, statePath, clientState, cleanup } = createMaturityHarness(maturityConfig());
|
||||||
|
const originalInfo = console.info;
|
||||||
|
const infoLogs: string[] = [];
|
||||||
|
setLogLevel('info');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.info = (...args: unknown[]) => {
|
||||||
|
infoLogs.push(args.map((value) => String(value)).join(' '));
|
||||||
|
};
|
||||||
clientState.findNotesByQuery.set('deck:"Mining"', [1]);
|
clientState.findNotesByQuery.set('deck:"Mining"', [1]);
|
||||||
clientState.failedQueries.add('deck:"Mining" prop:ivl>=21');
|
clientState.failedQueries.add('deck:"Mining" prop:ivl>=21');
|
||||||
clientState.notesInfoResult = [{ noteId: 1, fields: { Word: { value: '猫' } } }];
|
clientState.notesInfoResult = [{ noteId: 1, fields: { Word: { value: '猫' } } }];
|
||||||
@@ -271,7 +278,10 @@ test('refresh preserves known-word cache when maturity lookup fails', async () =
|
|||||||
};
|
};
|
||||||
assert.equal(persisted.version, 4);
|
assert.equal(persisted.version, 4);
|
||||||
assert.deepEqual(persisted.tiers, {});
|
assert.deepEqual(persisted.tiers, {});
|
||||||
|
assert.match(infoLogs.join('\n'), /maturityTiers=fetch-failed/);
|
||||||
} finally {
|
} finally {
|
||||||
|
console.info = originalInfo;
|
||||||
|
setLogLevel(undefined);
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -389,8 +389,10 @@ export class KnownWordCacheManager {
|
|||||||
this.isRefreshingKnownWords = true;
|
this.isRefreshingKnownWords = true;
|
||||||
try {
|
try {
|
||||||
const noteFieldsById = await this.fetchKnownWordNoteFieldsById();
|
const noteFieldsById = await this.fetchKnownWordNoteFieldsById();
|
||||||
|
const maturityTrackingEnabled = this.isMaturityTrackingEnabled();
|
||||||
|
let maturityFetchFailed = false;
|
||||||
let tierSets = null;
|
let tierSets = null;
|
||||||
if (this.isMaturityTrackingEnabled()) {
|
if (maturityTrackingEnabled) {
|
||||||
try {
|
try {
|
||||||
tierSets = await fetchKnownWordMaturityTierSets(
|
tierSets = await fetchKnownWordMaturityTierSets(
|
||||||
(query, options) => this.deps.client.findNotes(query, options),
|
(query, options) => this.deps.client.findNotes(query, options),
|
||||||
@@ -398,6 +400,7 @@ export class KnownWordCacheManager {
|
|||||||
getMatureIntervalThresholdDays(this.deps.getConfig()),
|
getMatureIntervalThresholdDays(this.deps.getConfig()),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
maturityFetchFailed = true;
|
||||||
log.warn('Failed to fetch known-word maturity tiers:', (error as Error).message);
|
log.warn('Failed to fetch known-word maturity tiers:', (error as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,7 +444,11 @@ export class KnownWordCacheManager {
|
|||||||
'Known-word cache refreshed',
|
'Known-word cache refreshed',
|
||||||
`noteCount=${currentNoteIds.length}`,
|
`noteCount=${currentNoteIds.length}`,
|
||||||
`wordCount=${this.wordReadingNoteIds.size}`,
|
`wordCount=${this.wordReadingNoteIds.size}`,
|
||||||
tierSets ? `maturityTiers=${this.noteTierById.size}` : 'maturityTiers=off',
|
tierSets
|
||||||
|
? `maturityTiers=${this.noteTierById.size}`
|
||||||
|
: maturityFetchFailed
|
||||||
|
? 'maturityTiers=fetch-failed'
|
||||||
|
: 'maturityTiers=off',
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.warn('Failed to refresh known-word cache:', (error as Error).message);
|
log.warn('Failed to refresh known-word cache:', (error as Error).message);
|
||||||
|
|||||||
@@ -35,20 +35,11 @@ test('maturity is enabled only when both highlight and maturity flags are on', (
|
|||||||
|
|
||||||
test('mature threshold falls back to default for invalid values', () => {
|
test('mature threshold falls back to default for invalid values', () => {
|
||||||
assert.equal(DEFAULT_MATURE_INTERVAL_THRESHOLD_DAYS, 21);
|
assert.equal(DEFAULT_MATURE_INTERVAL_THRESHOLD_DAYS, 21);
|
||||||
assert.equal(
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 30 })), 30);
|
||||||
getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 30 })),
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 14.9 })), 14);
|
||||||
30,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 14.9 })),
|
|
||||||
14,
|
|
||||||
);
|
|
||||||
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 0 })), 21);
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 0 })), 21);
|
||||||
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: -5 })), 21);
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: -5 })), 21);
|
||||||
assert.equal(
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: Number.NaN })), 21);
|
||||||
getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: Number.NaN })),
|
|
||||||
21,
|
|
||||||
);
|
|
||||||
assert.equal(getMatureIntervalThresholdDays(makeConfig({})), 21);
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({})), 21);
|
||||||
assert.equal(getMatureIntervalThresholdDays(makeConfig(undefined)), 21);
|
assert.equal(getMatureIntervalThresholdDays(makeConfig(undefined)), 21);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,9 +80,7 @@ test('annotateTokens resolves maturity through the kana reading fallback', () =>
|
|||||||
reading: string | undefined;
|
reading: string | undefined;
|
||||||
allowReadingOnlyMatch: boolean | undefined;
|
allowReadingOnlyMatch: boolean | undefined;
|
||||||
}> = [];
|
}> = [];
|
||||||
const tokens = [
|
const tokens = [makeToken({ surface: '大体', headword: '大体', reading: 'だいたい', endPos: 2 })];
|
||||||
makeToken({ surface: '大体', headword: '大体', reading: 'だいたい', endPos: 2 }),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = annotateTokens(
|
const result = annotateTokens(
|
||||||
tokens,
|
tokens,
|
||||||
|
|||||||
@@ -646,7 +646,9 @@ function computeTokenKnownMaturity(
|
|||||||
if (!fallbackReading || fallbackReading === matchText.trim()) {
|
if (!fallbackReading || fallbackReading === matchText.trim()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return getKnownWordTier(fallbackReading, undefined, { allowReadingOnlyMatch: false }) ?? undefined;
|
return (
|
||||||
|
getKnownWordTier(fallbackReading, undefined, { allowReadingOnlyMatch: false }) ?? undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterTokenFrequencyRank(
|
function filterTokenFrequencyRank(
|
||||||
|
|||||||
@@ -250,8 +250,5 @@ test('computeWordClass gives n+1 and name matches precedence over maturity', ()
|
|||||||
surface: 'アクア',
|
surface: 'アクア',
|
||||||
}) as MergedToken & { isNameMatch?: boolean };
|
}) as MergedToken & { isNameMatch?: boolean };
|
||||||
nameMatch.isNameMatch = true;
|
nameMatch.isNameMatch = true;
|
||||||
assert.equal(
|
assert.equal(computeWordClass(nameMatch, { nameMatchEnabled: true }), 'word word-name-match');
|
||||||
computeWordClass(nameMatch, { nameMatchEnabled: true }),
|
|
||||||
'word word-name-match',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user