mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
run prettier
This commit is contained in:
@@ -132,16 +132,15 @@ export function createAnilistTokenStore(
|
||||
}
|
||||
return decrypted;
|
||||
}
|
||||
if (
|
||||
typeof parsed.plaintextToken === 'string' &&
|
||||
parsed.plaintextToken.trim().length > 0
|
||||
) {
|
||||
if (typeof parsed.plaintextToken === 'string' && parsed.plaintextToken.trim().length > 0) {
|
||||
if (storage.isEncryptionAvailable()) {
|
||||
if (!isSafeStorageUsable()) {
|
||||
return null;
|
||||
}
|
||||
const plaintext = parsed.plaintextToken.trim();
|
||||
notifyUser('AniList token plaintext fallback payload found. Migrating to encrypted storage.');
|
||||
notifyUser(
|
||||
'AniList token plaintext fallback payload found. Migrating to encrypted storage.',
|
||||
);
|
||||
this.saveToken(plaintext);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
@@ -283,8 +283,7 @@ export function handleCliCommand(
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldStart =
|
||||
args.start || args.toggle || args.toggleVisibleOverlay;
|
||||
const shouldStart = args.start || args.toggle || args.toggleVisibleOverlay;
|
||||
const needsOverlayRuntime = commandNeedsOverlayRuntime(args);
|
||||
const shouldInitializeOverlayRuntime = needsOverlayRuntime || args.start;
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ test('createFrequencyDictionaryLookup aggregates duplicate-term logs into a sing
|
||||
|
||||
assert.equal(lookup('猫'), 100);
|
||||
assert.equal(
|
||||
logs.filter((entry) => entry.includes('Frequency dictionary ignored 2 duplicate term entries')).length,
|
||||
logs.filter((entry) => entry.includes('Frequency dictionary ignored 2 duplicate term entries'))
|
||||
.length,
|
||||
1,
|
||||
);
|
||||
assert.equal(
|
||||
|
||||
@@ -32,7 +32,7 @@ function parsePositiveFrequencyString(value: string): number | null {
|
||||
const chunks = numericPrefix.split(',');
|
||||
const normalizedNumber =
|
||||
chunks.length <= 1
|
||||
? chunks[0] ?? ''
|
||||
? (chunks[0] ?? '')
|
||||
: chunks.slice(1).every((chunk) => /^\d{3}$/.test(chunk))
|
||||
? chunks.join('')
|
||||
: (chunks[0] ?? '');
|
||||
|
||||
@@ -62,10 +62,7 @@ export {
|
||||
updateOverlayWindowBounds,
|
||||
} from './overlay-window';
|
||||
export { initializeOverlayRuntime } from './overlay-runtime-init';
|
||||
export {
|
||||
setVisibleOverlayVisible,
|
||||
updateVisibleOverlayVisibility,
|
||||
} from './overlay-visibility';
|
||||
export { setVisibleOverlayVisible, updateVisibleOverlayVisibility } from './overlay-visibility';
|
||||
export {
|
||||
MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY,
|
||||
MpvIpcClient,
|
||||
|
||||
@@ -150,7 +150,12 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
});
|
||||
const validResult = await setHandler!({}, 'anki.autoUpdateNewCards', true);
|
||||
assert.deepEqual(validResult, { ok: true });
|
||||
assert.deepEqual(calls, [{ id: 'anki.autoUpdateNewCards', value: true }]);
|
||||
const validSubtitleAnnotationResult = await setHandler!({}, 'subtitle.annotation.jlpt', false);
|
||||
assert.deepEqual(validSubtitleAnnotationResult, { ok: true });
|
||||
assert.deepEqual(calls, [
|
||||
{ id: 'anki.autoUpdateNewCards', value: true },
|
||||
{ id: 'subtitle.annotation.jlpt', value: false },
|
||||
]);
|
||||
|
||||
const cycleHandler = handlers.handle.get(IPC_CHANNELS.request.cycleRuntimeOption);
|
||||
assert.ok(cycleHandler);
|
||||
@@ -215,9 +220,7 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
|
||||
handlers.on.get(IPC_CHANNELS.command.saveSubtitlePosition)!({}, { yPercent: 'bad' });
|
||||
handlers.on.get(IPC_CHANNELS.command.saveSubtitlePosition)!({}, { yPercent: 42 });
|
||||
assert.deepEqual(saves, [
|
||||
{ yPercent: 42 },
|
||||
]);
|
||||
assert.deepEqual(saves, [{ yPercent: 42 }]);
|
||||
|
||||
handlers.on.get(IPC_CHANNELS.command.overlayModalClosed)!({}, 'not-a-modal');
|
||||
handlers.on.get(IPC_CHANNELS.command.overlayModalClosed)!({}, 'subsync');
|
||||
|
||||
@@ -62,7 +62,8 @@ export function createJellyfinTokenStore(
|
||||
}
|
||||
const decrypted = safeStorage.decryptString(encrypted).trim();
|
||||
const session = JSON.parse(decrypted) as Partial<JellyfinStoredSession>;
|
||||
const accessToken = typeof session.accessToken === 'string' ? session.accessToken.trim() : '';
|
||||
const accessToken =
|
||||
typeof session.accessToken === 'string' ? session.accessToken.trim() : '';
|
||||
const userId = typeof session.userId === 'string' ? session.userId.trim() : '';
|
||||
if (!accessToken || !userId) return null;
|
||||
return { accessToken, userId };
|
||||
@@ -88,7 +89,9 @@ export function createJellyfinTokenStore(
|
||||
(typeof parsed.encryptedToken === 'string' && parsed.encryptedToken.length > 0) ||
|
||||
(typeof parsed.plaintextToken === 'string' && parsed.plaintextToken.trim().length > 0)
|
||||
) {
|
||||
logger.warn('Ignoring legacy Jellyfin token-only store payload because userId is missing.');
|
||||
logger.warn(
|
||||
'Ignoring legacy Jellyfin token-only store payload because userId is missing.',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to read Jellyfin session store.', error);
|
||||
|
||||
@@ -34,10 +34,7 @@ export function sendToVisibleOverlayRuntime<T extends string>(options: {
|
||||
|
||||
const isLoading = typeof webContents.isLoading === 'function' ? webContents.isLoading() : false;
|
||||
const currentURL = typeof webContents.getURL === 'function' ? webContents.getURL() : '';
|
||||
const isReady =
|
||||
!isLoading &&
|
||||
currentURL !== '' &&
|
||||
currentURL !== 'about:blank';
|
||||
const isReady = !isLoading && currentURL !== '' && currentURL !== 'about:blank';
|
||||
|
||||
if (!isReady) {
|
||||
if (typeof webContents.once !== 'function') {
|
||||
|
||||
@@ -85,7 +85,9 @@ export function parseClipboardVideoPath(text: string): string | null {
|
||||
return isSupportedVideoPath(unquoted) ? unquoted : null;
|
||||
}
|
||||
|
||||
export function collectDroppedVideoPaths(dataTransfer: DropDataTransferLike | null | undefined): string[] {
|
||||
export function collectDroppedVideoPaths(
|
||||
dataTransfer: DropDataTransferLike | null | undefined,
|
||||
): string[] {
|
||||
if (!dataTransfer) return [];
|
||||
|
||||
const out: string[] = [];
|
||||
|
||||
@@ -117,13 +117,9 @@ test('runtime-option broadcast still uses expected channel', () => {
|
||||
},
|
||||
);
|
||||
let state = false;
|
||||
const changed = setOverlayDebugVisualizationEnabledRuntime(
|
||||
state,
|
||||
true,
|
||||
(enabled) => {
|
||||
state = enabled;
|
||||
},
|
||||
);
|
||||
const changed = setOverlayDebugVisualizationEnabledRuntime(state, true, (enabled) => {
|
||||
state = enabled;
|
||||
});
|
||||
assert.equal(changed, true);
|
||||
assert.equal(state, true);
|
||||
assert.deepEqual(broadcasts, [['runtime-options:changed', []]]);
|
||||
|
||||
@@ -41,13 +41,12 @@ test('initializeOverlayRuntime skips Anki integration when ankiConnect.enabled i
|
||||
setIntegrationCalls += 1;
|
||||
},
|
||||
showDesktopNotification: () => {},
|
||||
createFieldGroupingCallback: () =>
|
||||
async () => ({
|
||||
keepNoteId: 1,
|
||||
deleteNoteId: 2,
|
||||
deleteDuplicate: false,
|
||||
cancelled: false,
|
||||
}),
|
||||
createFieldGroupingCallback: () => async () => ({
|
||||
keepNoteId: 1,
|
||||
deleteNoteId: 2,
|
||||
deleteDuplicate: false,
|
||||
cancelled: false,
|
||||
}),
|
||||
getKnownWordCacheStatePath: () => '/tmp/known-words-cache.json',
|
||||
});
|
||||
|
||||
@@ -96,13 +95,12 @@ test('initializeOverlayRuntime starts Anki integration when ankiConnect.enabled
|
||||
setIntegrationCalls += 1;
|
||||
},
|
||||
showDesktopNotification: () => {},
|
||||
createFieldGroupingCallback: () =>
|
||||
async () => ({
|
||||
keepNoteId: 3,
|
||||
deleteNoteId: 4,
|
||||
deleteDuplicate: false,
|
||||
cancelled: false,
|
||||
}),
|
||||
createFieldGroupingCallback: () => async () => ({
|
||||
keepNoteId: 3,
|
||||
deleteNoteId: 4,
|
||||
deleteDuplicate: false,
|
||||
cancelled: false,
|
||||
}),
|
||||
getKnownWordCacheStatePath: () => '/tmp/known-words-cache.json',
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ type CreateAnkiIntegrationArgs = {
|
||||
};
|
||||
|
||||
function createDefaultAnkiIntegration(args: CreateAnkiIntegrationArgs): AnkiIntegrationLike {
|
||||
const { AnkiIntegration } = require('../../anki-integration') as typeof import('../../anki-integration');
|
||||
const { AnkiIntegration } =
|
||||
require('../../anki-integration') as typeof import('../../anki-integration');
|
||||
return new AnkiIntegration(
|
||||
args.config,
|
||||
args.subtitleTimingTracker as never,
|
||||
@@ -76,7 +77,10 @@ export function initializeOverlayRuntime(options: {
|
||||
options.registerGlobalShortcuts();
|
||||
|
||||
const createWindowTrackerHandler = options.createWindowTracker ?? createWindowTracker;
|
||||
const windowTracker = createWindowTrackerHandler(options.backendOverride, options.getMpvSocketPath());
|
||||
const windowTracker = createWindowTrackerHandler(
|
||||
options.backendOverride,
|
||||
options.getMpvSocketPath(),
|
||||
);
|
||||
options.setWindowTracker(windowTracker);
|
||||
if (windowTracker) {
|
||||
windowTracker.onGeometryChange = (geometry: WindowGeometry) => {
|
||||
|
||||
@@ -138,3 +138,35 @@ test('subtitle processing refresh can use explicit text override', async () => {
|
||||
|
||||
assert.deepEqual(emitted, [{ text: 'initial', tokens: [] }]);
|
||||
});
|
||||
|
||||
test('subtitle processing cache invalidation only affects future subtitle events', async () => {
|
||||
const emitted: SubtitleData[] = [];
|
||||
const callsByText = new Map<string, number>();
|
||||
const controller = createSubtitleProcessingController({
|
||||
tokenizeSubtitle: async (text) => {
|
||||
callsByText.set(text, (callsByText.get(text) ?? 0) + 1);
|
||||
return { text, tokens: [] };
|
||||
},
|
||||
emitSubtitle: (payload) => emitted.push(payload),
|
||||
});
|
||||
|
||||
controller.onSubtitleChange('same');
|
||||
await flushMicrotasks();
|
||||
controller.onSubtitleChange('other');
|
||||
await flushMicrotasks();
|
||||
controller.onSubtitleChange('same');
|
||||
await flushMicrotasks();
|
||||
|
||||
assert.equal(callsByText.get('same'), 1);
|
||||
assert.equal(emitted.length, 3);
|
||||
|
||||
controller.invalidateTokenizationCache();
|
||||
assert.equal(emitted.length, 3);
|
||||
|
||||
controller.onSubtitleChange('different');
|
||||
await flushMicrotasks();
|
||||
controller.onSubtitleChange('same');
|
||||
await flushMicrotasks();
|
||||
|
||||
assert.equal(callsByText.get('same'), 2);
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface SubtitleProcessingControllerDeps {
|
||||
export interface SubtitleProcessingController {
|
||||
onSubtitleChange: (text: string) => void;
|
||||
refreshCurrentSubtitle: (textOverride?: string) => void;
|
||||
invalidateTokenizationCache: () => void;
|
||||
}
|
||||
|
||||
export function createSubtitleProcessingController(
|
||||
@@ -126,5 +127,8 @@ export function createSubtitleProcessingController(
|
||||
refreshRequested = true;
|
||||
processLatest();
|
||||
},
|
||||
invalidateTokenizationCache: () => {
|
||||
tokenizationCache.clear();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,7 +52,12 @@ test('annotateTokens known-word match mode uses headword vs surface', () => {
|
||||
|
||||
test('annotateTokens excludes frequency for particle/bound_auxiliary and pos1 exclusions', () => {
|
||||
const tokens = [
|
||||
makeToken({ surface: 'は', headword: 'は', partOfSpeech: PartOfSpeech.particle, frequencyRank: 3 }),
|
||||
makeToken({
|
||||
surface: 'は',
|
||||
headword: 'は',
|
||||
partOfSpeech: PartOfSpeech.particle,
|
||||
frequencyRank: 3,
|
||||
}),
|
||||
makeToken({
|
||||
surface: 'です',
|
||||
headword: 'です',
|
||||
|
||||
@@ -7,12 +7,7 @@ import {
|
||||
DEFAULT_ANNOTATION_POS2_EXCLUSION_CONFIG,
|
||||
resolveAnnotationPos2ExclusionSet,
|
||||
} from '../../../token-pos2-exclusions';
|
||||
import {
|
||||
JlptLevel,
|
||||
MergedToken,
|
||||
NPlusOneMatchMode,
|
||||
PartOfSpeech,
|
||||
} from '../../../types';
|
||||
import { JlptLevel, MergedToken, NPlusOneMatchMode, PartOfSpeech } from '../../../types';
|
||||
import { shouldIgnoreJlptByTerm, shouldIgnoreJlptForMecabPos1 } from '../jlpt-token-filter';
|
||||
|
||||
const KATAKANA_TO_HIRAGANA_OFFSET = 0x60;
|
||||
@@ -67,10 +62,7 @@ function normalizePos1Tag(pos1: string | undefined): string {
|
||||
return typeof pos1 === 'string' ? pos1.trim() : '';
|
||||
}
|
||||
|
||||
function isExcludedByTagSet(
|
||||
normalizedTag: string,
|
||||
exclusions: ReadonlySet<string>,
|
||||
): boolean {
|
||||
function isExcludedByTagSet(normalizedTag: string, exclusions: ReadonlySet<string>): boolean {
|
||||
if (!normalizedTag) {
|
||||
return false;
|
||||
}
|
||||
@@ -350,10 +342,7 @@ function isLikelyFrequencyNoiseToken(token: MergedToken): boolean {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
shouldIgnoreJlptByTerm(trimmedCandidate) ||
|
||||
shouldIgnoreJlptByTerm(normalizedCandidate)
|
||||
) {
|
||||
if (shouldIgnoreJlptByTerm(trimmedCandidate) || shouldIgnoreJlptByTerm(normalizedCandidate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -442,13 +431,12 @@ export function annotateTokens(
|
||||
}));
|
||||
|
||||
const frequencyEnabled = options.frequencyEnabled !== false;
|
||||
const frequencyMarkedTokens =
|
||||
frequencyEnabled
|
||||
? applyFrequencyMarking(knownMarkedTokens, pos1Exclusions, pos2Exclusions)
|
||||
: knownMarkedTokens.map((token) => ({
|
||||
...token,
|
||||
frequencyRank: undefined,
|
||||
}));
|
||||
const frequencyMarkedTokens = frequencyEnabled
|
||||
? applyFrequencyMarking(knownMarkedTokens, pos1Exclusions, pos2Exclusions)
|
||||
: knownMarkedTokens.map((token) => ({
|
||||
...token,
|
||||
frequencyRank: undefined,
|
||||
}));
|
||||
|
||||
const jlptEnabled = options.jlptEnabled !== false;
|
||||
const jlptMarkedTokens = jlptEnabled
|
||||
|
||||
@@ -116,7 +116,9 @@ class ParserEnrichmentWorkerRuntime {
|
||||
}
|
||||
|
||||
private handleWorkerFailure(error: Error): void {
|
||||
logger.debug(`Parser enrichment worker unavailable, falling back to main thread: ${error.message}`);
|
||||
logger.debug(
|
||||
`Parser enrichment worker unavailable, falling back to main thread: ${error.message}`,
|
||||
);
|
||||
for (const pending of this.pending.values()) {
|
||||
pending.reject(error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user