mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
Address second CodeRabbit review round
This commit is contained in:
@@ -262,20 +262,24 @@ function M.create(ctx)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function register_bindings()
|
local function register_bindings()
|
||||||
clear_bindings()
|
|
||||||
|
|
||||||
local artifact, load_error = load_artifact()
|
local artifact, load_error = load_artifact()
|
||||||
if not artifact then
|
if not artifact then
|
||||||
subminer_log("warn", "session-bindings", load_error)
|
subminer_log("warn", "session-bindings", load_error)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_numeric_selection(false)
|
||||||
|
|
||||||
|
local previous_binding_names = state.session_binding_names
|
||||||
|
local next_binding_names = {}
|
||||||
|
state.session_binding_names = next_binding_names
|
||||||
|
|
||||||
local timeout_ms = tonumber(artifact.numericSelectionTimeoutMs) or 3000
|
local timeout_ms = tonumber(artifact.numericSelectionTimeoutMs) or 3000
|
||||||
for index, binding in ipairs(artifact.bindings) do
|
for index, binding in ipairs(artifact.bindings) do
|
||||||
local key_name = key_spec_to_mpv_binding(binding.key)
|
local key_name = key_spec_to_mpv_binding(binding.key)
|
||||||
if key_name then
|
if key_name then
|
||||||
local name = "subminer-session-binding-" .. tostring(index)
|
local name = "subminer-session-binding-" .. tostring(index)
|
||||||
state.session_binding_names[#state.session_binding_names + 1] = name
|
next_binding_names[#next_binding_names + 1] = name
|
||||||
mp.add_forced_key_binding(key_name, name, function()
|
mp.add_forced_key_binding(key_name, name, function()
|
||||||
handle_binding(binding, timeout_ms)
|
handle_binding(binding, timeout_ms)
|
||||||
end)
|
end)
|
||||||
@@ -288,10 +292,12 @@ function M.create(ctx)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
remove_binding_names(previous_binding_names)
|
||||||
|
|
||||||
subminer_log(
|
subminer_log(
|
||||||
"info",
|
"info",
|
||||||
"session-bindings",
|
"session-bindings",
|
||||||
"Registered " .. tostring(#state.session_binding_names) .. " shared session bindings"
|
"Registered " .. tostring(#next_binding_names) .. " shared session bindings"
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import {
|
|||||||
OverlayShortcutRuntimeDeps,
|
OverlayShortcutRuntimeDeps,
|
||||||
runOverlayShortcutLocalFallback,
|
runOverlayShortcutLocalFallback,
|
||||||
} from './overlay-shortcut-handler';
|
} from './overlay-shortcut-handler';
|
||||||
import { shouldActivateOverlayShortcuts } from './overlay-shortcut';
|
import {
|
||||||
|
registerOverlayShortcutsRuntime,
|
||||||
|
shouldActivateOverlayShortcuts,
|
||||||
|
unregisterOverlayShortcutsRuntime,
|
||||||
|
} from './overlay-shortcut';
|
||||||
|
|
||||||
function makeShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): ConfiguredShortcuts {
|
function makeShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): ConfiguredShortcuts {
|
||||||
return {
|
return {
|
||||||
@@ -313,3 +317,85 @@ test('shouldActivateOverlayShortcuts preserves non-macOS behavior', () => {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('registerOverlayShortcutsRuntime reports active shortcuts when configured', () => {
|
||||||
|
const result = registerOverlayShortcutsRuntime({
|
||||||
|
getConfiguredShortcuts: () =>
|
||||||
|
({
|
||||||
|
toggleVisibleOverlayGlobal: null,
|
||||||
|
copySubtitle: null,
|
||||||
|
copySubtitleMultiple: null,
|
||||||
|
updateLastCardFromClipboard: null,
|
||||||
|
triggerFieldGrouping: null,
|
||||||
|
triggerSubsync: null,
|
||||||
|
mineSentence: null,
|
||||||
|
mineSentenceMultiple: null,
|
||||||
|
multiCopyTimeoutMs: 2500,
|
||||||
|
toggleSecondarySub: null,
|
||||||
|
markAudioCard: null,
|
||||||
|
openRuntimeOptions: null,
|
||||||
|
openJimaku: 'Ctrl+J',
|
||||||
|
}) as never,
|
||||||
|
getOverlayHandlers: () => ({
|
||||||
|
copySubtitle: () => {},
|
||||||
|
copySubtitleMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: () => {},
|
||||||
|
triggerFieldGrouping: () => {},
|
||||||
|
triggerSubsync: () => {},
|
||||||
|
mineSentence: () => {},
|
||||||
|
mineSentenceMultiple: () => {},
|
||||||
|
toggleSecondarySub: () => {},
|
||||||
|
markAudioCard: () => {},
|
||||||
|
openRuntimeOptions: () => {},
|
||||||
|
openJimaku: () => {},
|
||||||
|
}),
|
||||||
|
cancelPendingMultiCopy: () => {},
|
||||||
|
cancelPendingMineSentenceMultiple: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unregisterOverlayShortcutsRuntime clears pending shortcut work when active', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
const result = unregisterOverlayShortcutsRuntime(true, {
|
||||||
|
getConfiguredShortcuts: () =>
|
||||||
|
({
|
||||||
|
toggleVisibleOverlayGlobal: null,
|
||||||
|
copySubtitle: null,
|
||||||
|
copySubtitleMultiple: null,
|
||||||
|
updateLastCardFromClipboard: null,
|
||||||
|
triggerFieldGrouping: null,
|
||||||
|
triggerSubsync: null,
|
||||||
|
mineSentence: null,
|
||||||
|
mineSentenceMultiple: null,
|
||||||
|
multiCopyTimeoutMs: 2500,
|
||||||
|
toggleSecondarySub: null,
|
||||||
|
markAudioCard: null,
|
||||||
|
openRuntimeOptions: null,
|
||||||
|
openJimaku: null,
|
||||||
|
}) as never,
|
||||||
|
getOverlayHandlers: () => ({
|
||||||
|
copySubtitle: () => {},
|
||||||
|
copySubtitleMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: () => {},
|
||||||
|
triggerFieldGrouping: () => {},
|
||||||
|
triggerSubsync: () => {},
|
||||||
|
mineSentence: () => {},
|
||||||
|
mineSentenceMultiple: () => {},
|
||||||
|
toggleSecondarySub: () => {},
|
||||||
|
markAudioCard: () => {},
|
||||||
|
openRuntimeOptions: () => {},
|
||||||
|
openJimaku: () => {},
|
||||||
|
}),
|
||||||
|
cancelPendingMultiCopy: () => {
|
||||||
|
calls.push('cancel-multi-copy');
|
||||||
|
},
|
||||||
|
cancelPendingMineSentenceMultiple: () => {
|
||||||
|
calls.push('cancel-mine-sentence-multiple');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result, false);
|
||||||
|
assert.deepEqual(calls, ['cancel-multi-copy', 'cancel-mine-sentence-multiple']);
|
||||||
|
});
|
||||||
|
|||||||
94
src/core/services/overlay-shortcut.test.ts
Normal file
94
src/core/services/overlay-shortcut.test.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import type { ConfiguredShortcuts } from '../utils/shortcut-config';
|
||||||
|
import {
|
||||||
|
registerOverlayShortcuts,
|
||||||
|
syncOverlayShortcutsRuntime,
|
||||||
|
unregisterOverlayShortcutsRuntime,
|
||||||
|
} from './overlay-shortcut';
|
||||||
|
|
||||||
|
function createShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): ConfiguredShortcuts {
|
||||||
|
return {
|
||||||
|
toggleVisibleOverlayGlobal: null,
|
||||||
|
copySubtitle: null,
|
||||||
|
copySubtitleMultiple: null,
|
||||||
|
updateLastCardFromClipboard: null,
|
||||||
|
triggerFieldGrouping: null,
|
||||||
|
triggerSubsync: null,
|
||||||
|
mineSentence: null,
|
||||||
|
mineSentenceMultiple: null,
|
||||||
|
multiCopyTimeoutMs: 2500,
|
||||||
|
toggleSecondarySub: null,
|
||||||
|
markAudioCard: null,
|
||||||
|
openRuntimeOptions: null,
|
||||||
|
openJimaku: null,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('registerOverlayShortcuts reports active overlay shortcuts when configured', () => {
|
||||||
|
assert.equal(
|
||||||
|
registerOverlayShortcuts(createShortcuts({ openJimaku: 'Ctrl+J' }), {
|
||||||
|
copySubtitle: () => {},
|
||||||
|
copySubtitleMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: () => {},
|
||||||
|
triggerFieldGrouping: () => {},
|
||||||
|
triggerSubsync: () => {},
|
||||||
|
mineSentence: () => {},
|
||||||
|
mineSentenceMultiple: () => {},
|
||||||
|
toggleSecondarySub: () => {},
|
||||||
|
markAudioCard: () => {},
|
||||||
|
openRuntimeOptions: () => {},
|
||||||
|
openJimaku: () => {},
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('registerOverlayShortcuts stays inactive when overlay shortcuts are absent', () => {
|
||||||
|
assert.equal(
|
||||||
|
registerOverlayShortcuts(createShortcuts(), {
|
||||||
|
copySubtitle: () => {},
|
||||||
|
copySubtitleMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: () => {},
|
||||||
|
triggerFieldGrouping: () => {},
|
||||||
|
triggerSubsync: () => {},
|
||||||
|
mineSentence: () => {},
|
||||||
|
mineSentenceMultiple: () => {},
|
||||||
|
toggleSecondarySub: () => {},
|
||||||
|
markAudioCard: () => {},
|
||||||
|
openRuntimeOptions: () => {},
|
||||||
|
openJimaku: () => {},
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncOverlayShortcutsRuntime deactivates cleanly when shortcuts were active', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
const result = syncOverlayShortcutsRuntime(false, true, {
|
||||||
|
getConfiguredShortcuts: () => createShortcuts(),
|
||||||
|
getOverlayHandlers: () => ({
|
||||||
|
copySubtitle: () => {},
|
||||||
|
copySubtitleMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: () => {},
|
||||||
|
triggerFieldGrouping: () => {},
|
||||||
|
triggerSubsync: () => {},
|
||||||
|
mineSentence: () => {},
|
||||||
|
mineSentenceMultiple: () => {},
|
||||||
|
toggleSecondarySub: () => {},
|
||||||
|
markAudioCard: () => {},
|
||||||
|
openRuntimeOptions: () => {},
|
||||||
|
openJimaku: () => {},
|
||||||
|
}),
|
||||||
|
cancelPendingMultiCopy: () => {
|
||||||
|
calls.push('cancel-multi-copy');
|
||||||
|
},
|
||||||
|
cancelPendingMineSentenceMultiple: () => {
|
||||||
|
calls.push('cancel-mine-sentence-multiple');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result, false);
|
||||||
|
assert.deepEqual(calls, ['cancel-multi-copy', 'cancel-mine-sentence-multiple']);
|
||||||
|
});
|
||||||
@@ -21,6 +21,27 @@ export interface OverlayShortcutLifecycleDeps {
|
|||||||
cancelPendingMineSentenceMultiple: () => void;
|
cancelPendingMineSentenceMultiple: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const OVERLAY_SHORTCUT_KEYS: Array<keyof Omit<ConfiguredShortcuts, 'multiCopyTimeoutMs'>> = [
|
||||||
|
'copySubtitle',
|
||||||
|
'copySubtitleMultiple',
|
||||||
|
'updateLastCardFromClipboard',
|
||||||
|
'triggerFieldGrouping',
|
||||||
|
'triggerSubsync',
|
||||||
|
'mineSentence',
|
||||||
|
'mineSentenceMultiple',
|
||||||
|
'toggleSecondarySub',
|
||||||
|
'markAudioCard',
|
||||||
|
'openRuntimeOptions',
|
||||||
|
'openJimaku',
|
||||||
|
];
|
||||||
|
|
||||||
|
function hasConfiguredOverlayShortcuts(shortcuts: ConfiguredShortcuts): boolean {
|
||||||
|
return OVERLAY_SHORTCUT_KEYS.some((key) => {
|
||||||
|
const shortcut = shortcuts[key];
|
||||||
|
return typeof shortcut === 'string' && shortcut.trim().length > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function shouldActivateOverlayShortcuts(args: {
|
export function shouldActivateOverlayShortcuts(args: {
|
||||||
overlayRuntimeInitialized: boolean;
|
overlayRuntimeInitialized: boolean;
|
||||||
isMacOSPlatform: boolean;
|
isMacOSPlatform: boolean;
|
||||||
@@ -36,10 +57,10 @@ export function shouldActivateOverlayShortcuts(args: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function registerOverlayShortcuts(
|
export function registerOverlayShortcuts(
|
||||||
_shortcuts: ConfiguredShortcuts,
|
shortcuts: ConfiguredShortcuts,
|
||||||
_handlers: OverlayShortcutHandlers,
|
_handlers: OverlayShortcutHandlers,
|
||||||
): boolean {
|
): boolean {
|
||||||
return false;
|
return hasConfiguredOverlayShortcuts(shortcuts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unregisterOverlayShortcuts(_shortcuts: ConfiguredShortcuts): void {}
|
export function unregisterOverlayShortcuts(_shortcuts: ConfiguredShortcuts): void {}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ const overlayWindowContentReady = new WeakSet<BrowserWindow>();
|
|||||||
const OVERLAY_WINDOW_CONTENT_READY_FLAG = '__subminerOverlayContentReady';
|
const OVERLAY_WINDOW_CONTENT_READY_FLAG = '__subminerOverlayContentReady';
|
||||||
|
|
||||||
export function isOverlayWindowContentReady(window: BrowserWindow): boolean {
|
export function isOverlayWindowContentReady(window: BrowserWindow): boolean {
|
||||||
|
if (window.isDestroyed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
overlayWindowContentReady.has(window) ||
|
overlayWindowContentReady.has(window) ||
|
||||||
(window as BrowserWindow & { [OVERLAY_WINDOW_CONTENT_READY_FLAG]?: boolean })[
|
(window as BrowserWindow & { [OVERLAY_WINDOW_CONTENT_READY_FLAG]?: boolean })[
|
||||||
|
|||||||
@@ -101,6 +101,55 @@ test('compileSessionBindings resolves CommandOrControl per platform', () => {
|
|||||||
assert.deepEqual(mac.bindings[0]?.key.modifiers, ['shift', 'meta']);
|
assert.deepEqual(mac.bindings[0]?.key.modifiers, ['shift', 'meta']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('compileSessionBindings resolves CommandOrControl in DOM key strings per platform', () => {
|
||||||
|
const input = {
|
||||||
|
shortcuts: createShortcuts(),
|
||||||
|
keybindings: [createKeybinding('CommandOrControl+Shift+J', ['cycle', 'fullscreen'])],
|
||||||
|
statsToggleKey: 'CommandOrControl+Backquote',
|
||||||
|
};
|
||||||
|
|
||||||
|
const windows = compileSessionBindings({ ...input, platform: 'win32' });
|
||||||
|
const mac = compileSessionBindings({ ...input, platform: 'darwin' });
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
windows.bindings
|
||||||
|
.map((binding) => ({
|
||||||
|
sourcePath: binding.sourcePath,
|
||||||
|
modifiers: binding.key.modifiers,
|
||||||
|
}))
|
||||||
|
.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath)),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sourcePath: 'keybindings[0].key',
|
||||||
|
modifiers: ['ctrl', 'shift'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourcePath: 'stats.toggleKey',
|
||||||
|
modifiers: ['ctrl'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
mac.bindings
|
||||||
|
.map((binding) => ({
|
||||||
|
sourcePath: binding.sourcePath,
|
||||||
|
modifiers: binding.key.modifiers,
|
||||||
|
}))
|
||||||
|
.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath)),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sourcePath: 'keybindings[0].key',
|
||||||
|
modifiers: ['shift', 'meta'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourcePath: 'stats.toggleKey',
|
||||||
|
modifiers: ['meta'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('compileSessionBindings drops conflicting bindings that canonicalize to the same key', () => {
|
test('compileSessionBindings drops conflicting bindings that canonicalize to the same key', () => {
|
||||||
const result = compileSessionBindings({
|
const result = compileSessionBindings({
|
||||||
shortcuts: createShortcuts({
|
shortcuts: createShortcuts({
|
||||||
@@ -173,3 +222,26 @@ test('compileSessionBindings warns on deprecated toggleVisibleOverlayGlobal conf
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('compileSessionBindings includes stats toggle in the shared session binding artifact', () => {
|
||||||
|
const result = compileSessionBindings({
|
||||||
|
shortcuts: createShortcuts(),
|
||||||
|
keybindings: [],
|
||||||
|
statsToggleKey: 'Backquote',
|
||||||
|
platform: 'win32',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.warnings.length, 0);
|
||||||
|
assert.deepEqual(result.bindings, [
|
||||||
|
{
|
||||||
|
sourcePath: 'stats.toggleKey',
|
||||||
|
originalKey: 'Backquote',
|
||||||
|
key: {
|
||||||
|
code: 'Backquote',
|
||||||
|
modifiers: [],
|
||||||
|
},
|
||||||
|
actionType: 'session-action',
|
||||||
|
actionId: 'toggleStatsOverlay',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|||||||
@@ -162,7 +162,10 @@ function parseAccelerator(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDomKeyString(key: string): { key: SessionKeySpec | null; message?: string } {
|
function parseDomKeyString(
|
||||||
|
key: string,
|
||||||
|
platform: PlatformKeyModel,
|
||||||
|
): { key: SessionKeySpec | null; message?: string } {
|
||||||
const parts = key
|
const parts = key
|
||||||
.split('+')
|
.split('+')
|
||||||
.map((part) => part.trim())
|
.map((part) => part.trim())
|
||||||
@@ -194,7 +197,9 @@ function parseDomKeyString(key: string): { key: SessionKeySpec | null; message?:
|
|||||||
lower === 'cmd' ||
|
lower === 'cmd' ||
|
||||||
lower === 'commandorcontrol'
|
lower === 'commandorcontrol'
|
||||||
) {
|
) {
|
||||||
modifiers.push(lower === 'commandorcontrol' ? 'ctrl' : 'meta');
|
modifiers.push(
|
||||||
|
lower === 'commandorcontrol' ? (platform === 'darwin' ? 'meta' : 'ctrl') : 'meta',
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -335,7 +340,7 @@ export function compileSessionBindings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (statsToggleKey) {
|
if (statsToggleKey) {
|
||||||
const parsed = parseDomKeyString(statsToggleKey);
|
const parsed = parseDomKeyString(statsToggleKey, input.platform);
|
||||||
if (!parsed.key) {
|
if (!parsed.key) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
kind: 'unsupported',
|
kind: 'unsupported',
|
||||||
@@ -363,7 +368,7 @@ export function compileSessionBindings(
|
|||||||
|
|
||||||
input.keybindings.forEach((binding, index) => {
|
input.keybindings.forEach((binding, index) => {
|
||||||
if (!binding.command) return;
|
if (!binding.command) return;
|
||||||
const parsed = parseDomKeyString(binding.key);
|
const parsed = parseDomKeyString(binding.key, input.platform);
|
||||||
if (!parsed.key) {
|
if (!parsed.key) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
kind: 'unsupported',
|
kind: 'unsupported',
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { YOMITAN_LOOKUP_EVENT, registerYomitanLookupListener } from './yomitan-popup.js';
|
import {
|
||||||
|
YOMITAN_LOOKUP_EVENT,
|
||||||
|
YOMITAN_POPUP_VISIBLE_HOST_SELECTOR,
|
||||||
|
isYomitanPopupVisible,
|
||||||
|
registerYomitanLookupListener,
|
||||||
|
} from './yomitan-popup.js';
|
||||||
|
|
||||||
test('registerYomitanLookupListener forwards the SubMiner Yomitan lookup event', () => {
|
test('registerYomitanLookupListener forwards the SubMiner Yomitan lookup event', () => {
|
||||||
const target = new EventTarget();
|
const target = new EventTarget();
|
||||||
@@ -16,3 +21,12 @@ test('registerYomitanLookupListener forwards the SubMiner Yomitan lookup event',
|
|||||||
|
|
||||||
assert.deepEqual(calls, ['lookup']);
|
assert.deepEqual(calls, ['lookup']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('isYomitanPopupVisible falls back to querySelector when querySelectorAll is unavailable', () => {
|
||||||
|
const root = {
|
||||||
|
querySelector: (selector: string) =>
|
||||||
|
selector === YOMITAN_POPUP_VISIBLE_HOST_SELECTOR ? ({} as Element) : null,
|
||||||
|
} as ParentNode;
|
||||||
|
|
||||||
|
assert.equal(isYomitanPopupVisible(root), true);
|
||||||
|
});
|
||||||
|
|||||||
@@ -62,10 +62,14 @@ function queryPopupElements<T extends Element>(
|
|||||||
root: ParentNode | null | undefined,
|
root: ParentNode | null | undefined,
|
||||||
selector: string,
|
selector: string,
|
||||||
): T[] {
|
): T[] {
|
||||||
if (typeof root?.querySelectorAll !== 'function') {
|
if (typeof root?.querySelectorAll === 'function') {
|
||||||
return [];
|
return Array.from(root.querySelectorAll<T>(selector));
|
||||||
}
|
}
|
||||||
return Array.from(root.querySelectorAll<T>(selector));
|
if (typeof root?.querySelector === 'function') {
|
||||||
|
const first = root.querySelector(selector) as T | null;
|
||||||
|
return first ? [first] : [];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isYomitanPopupVisible(root: ParentNode | null | undefined = document): boolean {
|
export function isYomitanPopupVisible(root: ParentNode | null | undefined = document): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user