mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-04 00:41:33 -07:00
chore(release): prepare v0.12.0
This commit is contained in:
@@ -364,7 +364,10 @@ test('isYomitanPopupVisible requires visible iframe geometry', () => {
|
||||
const root = {
|
||||
querySelectorAll: (value: string) => {
|
||||
selectors.push(value);
|
||||
if (value === YOMITAN_POPUP_VISIBLE_HOST_SELECTOR || value === YOMITAN_POPUP_HOST_SELECTOR) {
|
||||
if (
|
||||
value === YOMITAN_POPUP_VISIBLE_HOST_SELECTOR ||
|
||||
value === YOMITAN_POPUP_HOST_SELECTOR
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return [hiddenFrame, visibleFrame];
|
||||
|
||||
@@ -1063,7 +1063,9 @@ test('session binding: Ctrl+Alt+S dispatches subsync action locally', async () =
|
||||
|
||||
testGlobals.dispatchKeydown({ key: 's', code: 'KeyS', ctrlKey: true, altKey: true });
|
||||
|
||||
assert.deepEqual(testGlobals.sessionActions, [{ actionId: 'triggerSubsync', payload: undefined }]);
|
||||
assert.deepEqual(testGlobals.sessionActions, [
|
||||
{ actionId: 'triggerSubsync', payload: undefined },
|
||||
]);
|
||||
} finally {
|
||||
testGlobals.restore();
|
||||
}
|
||||
|
||||
@@ -39,12 +39,10 @@ export function createKeyboardHandlers(
|
||||
let pendingLookupRefreshAfterSubtitleSeek = false;
|
||||
let resetSelectionToStartOnNextSubtitleSync = false;
|
||||
let lookupScanFallbackTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let pendingNumericSelection:
|
||||
| {
|
||||
actionId: 'copySubtitleMultiple' | 'mineSentenceMultiple';
|
||||
timeout: ReturnType<typeof setTimeout> | null;
|
||||
}
|
||||
| null = null;
|
||||
let pendingNumericSelection: {
|
||||
actionId: 'copySubtitleMultiple' | 'mineSentenceMultiple';
|
||||
timeout: ReturnType<typeof setTimeout> | null;
|
||||
} | null = null;
|
||||
|
||||
const CHORD_MAP = new Map<
|
||||
string,
|
||||
|
||||
@@ -73,11 +73,13 @@ export function createMouseHandlers(
|
||||
syncOverlayMouseIgnoreState(ctx);
|
||||
}
|
||||
|
||||
function reconcilePopupInteraction(args: {
|
||||
assumeVisible?: boolean;
|
||||
reclaimFocus?: boolean;
|
||||
allowPause?: boolean;
|
||||
} = {}): boolean {
|
||||
function reconcilePopupInteraction(
|
||||
args: {
|
||||
assumeVisible?: boolean;
|
||||
reclaimFocus?: boolean;
|
||||
allowPause?: boolean;
|
||||
} = {},
|
||||
): boolean {
|
||||
const popupVisible = syncPopupVisibilityState(args.assumeVisible === true);
|
||||
if (!popupVisible) {
|
||||
syncOverlayMouseIgnoreState(ctx);
|
||||
|
||||
@@ -168,48 +168,54 @@ function withRuntimeOptionsModal(
|
||||
test('openRuntimeOptionsModal shows loading shell before runtime options resolve', async () => {
|
||||
const deferred = createDeferred<RuntimeOptionState[]>();
|
||||
|
||||
await withRuntimeOptionsModal(() => deferred.promise, async (input) => {
|
||||
input.modal.openRuntimeOptionsModal();
|
||||
await withRuntimeOptionsModal(
|
||||
() => deferred.promise,
|
||||
async (input) => {
|
||||
input.modal.openRuntimeOptionsModal();
|
||||
|
||||
assert.equal(input.state.runtimeOptionsModalOpen, true);
|
||||
assert.equal(input.overlayClassList.contains('interactive'), true);
|
||||
assert.equal(input.modalClassList.contains('hidden'), false);
|
||||
assert.equal(input.statusNode.textContent, 'Loading runtime options...');
|
||||
assert.deepEqual(input.syncCalls, ['sync']);
|
||||
assert.equal(input.state.runtimeOptionsModalOpen, true);
|
||||
assert.equal(input.overlayClassList.contains('interactive'), true);
|
||||
assert.equal(input.modalClassList.contains('hidden'), false);
|
||||
assert.equal(input.statusNode.textContent, 'Loading runtime options...');
|
||||
assert.deepEqual(input.syncCalls, ['sync']);
|
||||
|
||||
deferred.resolve([
|
||||
{
|
||||
id: 'anki.autoUpdateNewCards',
|
||||
label: 'Auto-update new cards',
|
||||
scope: 'ankiConnect',
|
||||
valueType: 'boolean',
|
||||
value: true,
|
||||
allowedValues: [true, false],
|
||||
requiresRestart: false,
|
||||
},
|
||||
]);
|
||||
await flushAsyncWork();
|
||||
deferred.resolve([
|
||||
{
|
||||
id: 'anki.autoUpdateNewCards',
|
||||
label: 'Auto-update new cards',
|
||||
scope: 'ankiConnect',
|
||||
valueType: 'boolean',
|
||||
value: true,
|
||||
allowedValues: [true, false],
|
||||
requiresRestart: false,
|
||||
},
|
||||
]);
|
||||
await flushAsyncWork();
|
||||
|
||||
assert.equal(
|
||||
input.statusNode.textContent,
|
||||
'Use arrow keys. Click value to cycle. Enter or double-click to apply.',
|
||||
);
|
||||
assert.equal(input.statusNode.classList.contains('error'), false);
|
||||
});
|
||||
assert.equal(
|
||||
input.statusNode.textContent,
|
||||
'Use arrow keys. Click value to cycle. Enter or double-click to apply.',
|
||||
);
|
||||
assert.equal(input.statusNode.classList.contains('error'), false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('openRuntimeOptionsModal keeps modal visible when loading fails', async () => {
|
||||
const deferred = createDeferred<RuntimeOptionState[]>();
|
||||
|
||||
await withRuntimeOptionsModal(() => deferred.promise, async (input) => {
|
||||
input.modal.openRuntimeOptionsModal();
|
||||
deferred.reject(new Error('boom'));
|
||||
await flushAsyncWork();
|
||||
await withRuntimeOptionsModal(
|
||||
() => deferred.promise,
|
||||
async (input) => {
|
||||
input.modal.openRuntimeOptionsModal();
|
||||
deferred.reject(new Error('boom'));
|
||||
await flushAsyncWork();
|
||||
|
||||
assert.equal(input.state.runtimeOptionsModalOpen, true);
|
||||
assert.equal(input.overlayClassList.contains('interactive'), true);
|
||||
assert.equal(input.modalClassList.contains('hidden'), false);
|
||||
assert.equal(input.statusNode.textContent, 'Failed to load runtime options');
|
||||
assert.equal(input.statusNode.classList.contains('error'), true);
|
||||
});
|
||||
assert.equal(input.state.runtimeOptionsModalOpen, true);
|
||||
assert.equal(input.overlayClassList.contains('interactive'), true);
|
||||
assert.equal(input.modalClassList.contains('hidden'), false);
|
||||
assert.equal(input.statusNode.textContent, 'Failed to load runtime options');
|
||||
assert.equal(input.statusNode.classList.contains('error'), true);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -130,7 +130,8 @@ test('visible yomitan popup host keeps overlay interactive even when cached popu
|
||||
},
|
||||
document: {
|
||||
querySelectorAll: (selector: string) =>
|
||||
selector === '[data-subminer-yomitan-popup-host="true"][data-subminer-yomitan-popup-visible="true"]'
|
||||
selector ===
|
||||
'[data-subminer-yomitan-popup-host="true"][data-subminer-yomitan-popup-visible="true"]'
|
||||
? [{ getAttribute: () => 'true' }]
|
||||
: [],
|
||||
},
|
||||
|
||||
@@ -73,7 +73,10 @@ function queryPopupElements<T extends Element>(
|
||||
}
|
||||
|
||||
export function isYomitanPopupVisible(root: ParentNode | null | undefined = document): boolean {
|
||||
const visiblePopupHosts = queryPopupElements<HTMLElement>(root, YOMITAN_POPUP_VISIBLE_HOST_SELECTOR);
|
||||
const visiblePopupHosts = queryPopupElements<HTMLElement>(
|
||||
root,
|
||||
YOMITAN_POPUP_VISIBLE_HOST_SELECTOR,
|
||||
);
|
||||
if (visiblePopupHosts.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user