fix: Kiku field grouping, frequency particles, sidebar media, Yomitan popup visibility (#91)

This commit is contained in:
2026-05-27 01:40:48 -07:00
committed by GitHub
parent efe50ed1e4
commit 1dcfed86ab
52 changed files with 1695 additions and 368 deletions
+2
View File
@@ -96,6 +96,7 @@ export interface MainIpcRuntimeServiceDepsParams {
getAnilistQueueStatus: IpcDepsRuntimeOptions['getAnilistQueueStatus'];
retryAnilistQueueNow: IpcDepsRuntimeOptions['retryAnilistQueueNow'];
runAnilistPostWatchUpdateOnManualMark?: IpcDepsRuntimeOptions['runAnilistPostWatchUpdateOnManualMark'];
recordSubtitleMiningContext?: IpcDepsRuntimeOptions['recordSubtitleMiningContext'];
getCharacterDictionarySelection?: IpcDepsRuntimeOptions['getCharacterDictionarySelection'];
setCharacterDictionarySelection?: IpcDepsRuntimeOptions['setCharacterDictionarySelection'];
getCharacterDictionaryManagerSnapshot?: IpcDepsRuntimeOptions['getCharacterDictionaryManagerSnapshot'];
@@ -273,6 +274,7 @@ export function createMainIpcRuntimeServiceDeps(
getAnilistQueueStatus: params.getAnilistQueueStatus,
retryAnilistQueueNow: params.retryAnilistQueueNow,
runAnilistPostWatchUpdateOnManualMark: params.runAnilistPostWatchUpdateOnManualMark,
recordSubtitleMiningContext: params.recordSubtitleMiningContext,
getCharacterDictionarySelection: params.getCharacterDictionarySelection,
setCharacterDictionarySelection: params.setCharacterDictionarySelection,
getCharacterDictionaryManagerSnapshot: params.getCharacterDictionaryManagerSnapshot,
+22
View File
@@ -804,6 +804,28 @@ test('waitForModalOpen resolves true after modal acknowledgement', async () => {
assert.equal(await pending, true);
});
test('waitForModalOpen resolves true when modal acknowledgement arrives before waiter registration', async () => {
const modalWindow = createMockWindow();
const runtime = createOverlayModalRuntimeService({
getMainWindow: () => null,
getModalWindow: () => modalWindow as never,
createModalWindow: () => modalWindow as never,
getModalGeometry: () => ({ x: 0, y: 0, width: 400, height: 300 }),
setModalWindowBounds: () => {},
});
runtime.sendToActiveOverlayWindow(
'kiku:field-grouping-request',
{},
{
restoreOnModalClose: 'kiku',
},
);
runtime.notifyOverlayModalOpened('kiku');
assert.equal(await runtime.waitForModalOpen('kiku', 5), true);
});
test('waitForModalOpen resolves false on timeout', async () => {
const runtime = createOverlayModalRuntimeService({
getMainWindow: () => null,
+7
View File
@@ -64,6 +64,7 @@ export function createOverlayModalRuntimeService(
): OverlayModalRuntime {
const restoreVisibleOverlayOnModalClose = new Set<OverlayHostedModal>();
const modalOpenWaiters = new Map<OverlayHostedModal, Array<(opened: boolean) => void>>();
const openedModals = new Set<OverlayHostedModal>();
let modalActive = false;
let mainWindowMousePassthroughForcedByModal = false;
let mainWindowHiddenByModal = false;
@@ -375,6 +376,7 @@ export function createOverlayModalRuntimeService(
};
const handleOverlayModalClosed = (modal: OverlayHostedModal): void => {
openedModals.delete(modal);
if (!restoreVisibleOverlayOnModalClose.has(modal)) return;
restoreVisibleOverlayOnModalClose.delete(modal);
const modalWindow = deps.getModalWindow();
@@ -392,6 +394,7 @@ export function createOverlayModalRuntimeService(
const notifyOverlayModalOpened = (modal: OverlayHostedModal): void => {
if (!restoreVisibleOverlayOnModalClose.has(modal)) return;
openedModals.add(modal);
const waiters = modalOpenWaiters.get(modal) ?? [];
modalOpenWaiters.delete(modal);
for (const resolve of waiters) {
@@ -420,6 +423,10 @@ export function createOverlayModalRuntimeService(
const waitForModalOpen = async (modal: OverlayHostedModal, timeoutMs: number): Promise<boolean> =>
await new Promise<boolean>((resolve) => {
if (openedModals.has(modal)) {
resolve(true);
return;
}
const waiters = modalOpenWaiters.get(modal) ?? [];
const finish = (opened: boolean): void => {
clearTimeout(timeout);
@@ -7,7 +7,7 @@ type FieldGroupingOverlayMainDeps<TModal extends string> = Omit<
sendToActiveOverlayWindow: (
channel: string,
payload?: unknown,
runtimeOptions?: { restoreOnModalClose?: TModal },
runtimeOptions?: { restoreOnModalClose?: TModal; preferModalWindow?: boolean },
) => boolean;
};
@@ -31,7 +31,7 @@ export function createBuildFieldGroupingOverlayMainDepsHandler<TModal extends st
sendToVisibleOverlay: (
channel: string,
payload?: unknown,
runtimeOptions?: { restoreOnModalClose?: TModal },
runtimeOptions?: { restoreOnModalClose?: TModal; preferModalWindow?: boolean },
) => deps.sendToActiveOverlayWindow(channel, payload, runtimeOptions),
});
}
+27
View File
@@ -11,6 +11,7 @@ type LogCandidate = {
mtimeMs: number;
mtimeDateKey: string;
fileDateKey: string | null;
fileWeekKey: string | null;
};
export type ExportLogsResult = {
@@ -38,10 +39,21 @@ function localDateKey(date: Date): string {
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
}
function localWeekKey(date: Date): string {
const startOfYear = new Date(date.getFullYear(), 0, 1);
const dayOfYear =
Math.floor((date.getTime() - startOfYear.getTime()) / (24 * 60 * 60 * 1000)) + 1;
return `${date.getFullYear()}-W${pad(Math.max(1, Math.ceil(dayOfYear / 7)))}`;
}
function filenameDateKey(fileName: string): string | null {
return fileName.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? null;
}
function filenameWeekKey(fileName: string): string | null {
return fileName.match(/\d{4}-W\d{2}/)?.[0] ?? null;
}
function fileKind(fileName: string): string {
const match = fileName.match(/^([A-Za-z0-9_-]+)-/);
return match?.[1] ?? fileName;
@@ -84,6 +96,7 @@ function buildCandidate(logsDir: string, entry: string): LogCandidate | null {
mtimeMs: stats.mtimeMs,
mtimeDateKey: localDateKey(stats.mtime),
fileDateKey: filenameDateKey(entry),
fileWeekKey: filenameWeekKey(entry),
};
}
@@ -117,6 +130,14 @@ function candidateFreshnessMs(candidate: LogCandidate): number {
if (candidate.fileDateKey) {
return Date.parse(`${candidate.fileDateKey}T23:59:59.999Z`);
}
if (candidate.fileWeekKey) {
const match = candidate.fileWeekKey.match(/^(\d{4})-W(\d{2})$/);
if (match) {
const year = Number(match[1]);
const week = Number(match[2]);
return Date.UTC(year, 0, week * 7, 23, 59, 59, 999);
}
}
return candidate.mtimeMs;
}
@@ -130,6 +151,12 @@ function selectLogCandidates(
return { mode: 'current-day', selected: currentDated };
}
const currentWeek = localWeekKey(now);
const currentWeekly = candidates.filter((candidate) => candidate.fileWeekKey === currentWeek);
if (currentWeekly.length > 0) {
return { mode: 'current-day', selected: currentWeekly };
}
const currentUndated = candidates.filter(
(candidate) => candidate.fileDateKey === null && candidate.mtimeDateKey === today,
);