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
+83
View File
@@ -970,6 +970,89 @@ test('window blur reclaims overlay focus while a yomitan popup remains visible o
}
});
test('yomitan popup visibility marks primary subtitle hover hold while enabled', () => {
const ctx = createMouseTestContext();
(ctx.state as { primaryVisibleOnYomitanPopup?: boolean }).primaryVisibleOnYomitanPopup = true;
const previousWindow = (globalThis as { window?: unknown }).window;
const previousDocument = (globalThis as { document?: unknown }).document;
const previousMutationObserver = (globalThis as { MutationObserver?: unknown }).MutationObserver;
const previousNode = (globalThis as { Node?: unknown }).Node;
const windowListeners = new Map<string, Array<() => void>>();
const bodyClassList = createClassList();
Object.defineProperty(globalThis, 'window', {
configurable: true,
value: {
addEventListener: (type: string, listener: () => void) => {
const bucket = windowListeners.get(type) ?? [];
bucket.push(listener);
windowListeners.set(type, bucket);
},
electronAPI: {
setIgnoreMouseEvents: () => {},
},
focus: () => {},
},
});
Object.defineProperty(globalThis, 'document', {
configurable: true,
value: {
body: { classList: bodyClassList },
querySelectorAll: () => [],
querySelector: () => null,
visibilityState: 'visible',
},
});
Object.defineProperty(globalThis, 'MutationObserver', {
configurable: true,
value: class {
observe() {}
},
});
Object.defineProperty(globalThis, 'Node', {
configurable: true,
value: {
ELEMENT_NODE: 1,
},
});
try {
const handlers = createMouseHandlers(ctx as never, {
modalStateReader: {
isAnySettingsModalOpen: () => false,
isAnyModalOpen: () => false,
},
applyYPercent: () => {},
getCurrentYPercent: () => 10,
persistSubtitlePositionPatch: () => {},
getSubtitleHoverAutoPauseEnabled: () => false,
getYomitanPopupAutoPauseEnabled: () => false,
getPlaybackPaused: async () => false,
sendMpvCommand: () => {},
});
handlers.setupYomitanObserver();
for (const listener of windowListeners.get(YOMITAN_POPUP_SHOWN_EVENT) ?? []) {
listener();
}
assert.equal(bodyClassList.contains('primary-sub-visible-on-yomitan-popup'), true);
for (const listener of windowListeners.get(YOMITAN_POPUP_HIDDEN_EVENT) ?? []) {
listener();
}
assert.equal(bodyClassList.contains('primary-sub-visible-on-yomitan-popup'), false);
} finally {
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
Object.defineProperty(globalThis, 'MutationObserver', {
configurable: true,
value: previousMutationObserver,
});
Object.defineProperty(globalThis, 'Node', { configurable: true, value: previousNode });
}
});
test('restorePointerInteractionState re-enables subtitle hover when pointer is already over subtitles', () => {
const ctx = createMouseTestContext();
const originalWindow = globalThis.window;
+13
View File
@@ -5,6 +5,7 @@ import {
YOMITAN_POPUP_MOUSE_ENTER_EVENT,
YOMITAN_POPUP_MOUSE_LEAVE_EVENT,
YOMITAN_POPUP_SHOWN_EVENT,
PRIMARY_SUB_VISIBLE_ON_YOMITAN_POPUP_CLASS,
isYomitanPopupVisible,
isYomitanPopupIframe,
} from '../yomitan-popup.js';
@@ -44,10 +45,21 @@ export function createMouseHandlers(
return typeof document !== 'undefined' && isYomitanPopupVisible(document);
}
function syncPrimaryVisibleOnYomitanPopupClass(popupVisible: boolean): void {
if (typeof document === 'undefined') {
return;
}
document.body?.classList?.toggle(
PRIMARY_SUB_VISIBLE_ON_YOMITAN_POPUP_CLASS,
popupVisible && ctx.state.primaryVisibleOnYomitanPopup,
);
}
function syncPopupVisibilityState(assumeVisible = false): boolean {
const popupVisible = assumeVisible || getPopupVisibilityFromDom();
yomitanPopupVisible = popupVisible;
ctx.state.yomitanPopupVisible = popupVisible;
syncPrimaryVisibleOnYomitanPopupClass(popupVisible);
return popupVisible;
}
@@ -293,6 +305,7 @@ export function createMouseHandlers(
yomitanPopupVisible = false;
ctx.state.yomitanPopupVisible = false;
syncPrimaryVisibleOnYomitanPopupClass(false);
popupPauseRequestId += 1;
maybeResumeYomitanPopupPause();
maybeResumeHoverPause();