mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-30 06:12:06 -07:00
fix(renderer): restore subtitle sidebar mpv passthrough
This commit is contained in:
@@ -1241,6 +1241,7 @@ test('subtitle sidebar closes and resumes a hover pause', async () => {
|
|||||||
const previousDocument = globals.document;
|
const previousDocument = globals.document;
|
||||||
const mpvCommands: Array<Array<string | number>> = [];
|
const mpvCommands: Array<Array<string | number>> = [];
|
||||||
const modalListeners = new Map<string, Array<() => void>>();
|
const modalListeners = new Map<string, Array<() => void>>();
|
||||||
|
const contentListeners = new Map<string, Array<() => void>>();
|
||||||
|
|
||||||
const snapshot: SubtitleSidebarSnapshot = {
|
const snapshot: SubtitleSidebarSnapshot = {
|
||||||
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
||||||
@@ -1317,6 +1318,11 @@ test('subtitle sidebar closes and resumes a hover pause', async () => {
|
|||||||
subtitleSidebarContent: {
|
subtitleSidebarContent: {
|
||||||
classList: createClassList(),
|
classList: createClassList(),
|
||||||
getBoundingClientRect: () => ({ width: 420 }),
|
getBoundingClientRect: () => ({ width: 420 }),
|
||||||
|
addEventListener: (type: string, listener: () => void) => {
|
||||||
|
const bucket = contentListeners.get(type) ?? [];
|
||||||
|
bucket.push(listener);
|
||||||
|
contentListeners.set(type, bucket);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
subtitleSidebarClose: { addEventListener: () => {} },
|
subtitleSidebarClose: { addEventListener: () => {} },
|
||||||
subtitleSidebarStatus: { textContent: '' },
|
subtitleSidebarStatus: { textContent: '' },
|
||||||
@@ -1333,7 +1339,7 @@ test('subtitle sidebar closes and resumes a hover pause', async () => {
|
|||||||
await modal.openSubtitleSidebarModal();
|
await modal.openSubtitleSidebarModal();
|
||||||
await modal.refreshSubtitleSidebarSnapshot();
|
await modal.refreshSubtitleSidebarSnapshot();
|
||||||
mpvCommands.length = 0;
|
mpvCommands.length = 0;
|
||||||
await modalListeners.get('mouseenter')?.[0]?.();
|
await contentListeners.get('mouseenter')?.[0]?.();
|
||||||
|
|
||||||
assert.deepEqual(mpvCommands.at(-1), ['set_property', 'pause', 'yes']);
|
assert.deepEqual(mpvCommands.at(-1), ['set_property', 'pause', 'yes']);
|
||||||
|
|
||||||
@@ -1353,6 +1359,7 @@ test('subtitle sidebar hover pause ignores playback-state IPC failures', async (
|
|||||||
const previousDocument = globals.document;
|
const previousDocument = globals.document;
|
||||||
const mpvCommands: Array<Array<string | number>> = [];
|
const mpvCommands: Array<Array<string | number>> = [];
|
||||||
const modalListeners = new Map<string, Array<() => Promise<void> | void>>();
|
const modalListeners = new Map<string, Array<() => Promise<void> | void>>();
|
||||||
|
const contentListeners = new Map<string, Array<() => Promise<void> | void>>();
|
||||||
|
|
||||||
const snapshot: SubtitleSidebarSnapshot = {
|
const snapshot: SubtitleSidebarSnapshot = {
|
||||||
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
||||||
@@ -1431,6 +1438,11 @@ test('subtitle sidebar hover pause ignores playback-state IPC failures', async (
|
|||||||
subtitleSidebarContent: {
|
subtitleSidebarContent: {
|
||||||
classList: createClassList(),
|
classList: createClassList(),
|
||||||
getBoundingClientRect: () => ({ width: 420 }),
|
getBoundingClientRect: () => ({ width: 420 }),
|
||||||
|
addEventListener: (type: string, listener: () => Promise<void> | void) => {
|
||||||
|
const bucket = contentListeners.get(type) ?? [];
|
||||||
|
bucket.push(listener);
|
||||||
|
contentListeners.set(type, bucket);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
subtitleSidebarClose: { addEventListener: () => {} },
|
subtitleSidebarClose: { addEventListener: () => {} },
|
||||||
subtitleSidebarStatus: { textContent: '' },
|
subtitleSidebarStatus: { textContent: '' },
|
||||||
@@ -1446,7 +1458,7 @@ test('subtitle sidebar hover pause ignores playback-state IPC failures', async (
|
|||||||
|
|
||||||
await modal.openSubtitleSidebarModal();
|
await modal.openSubtitleSidebarModal();
|
||||||
await assert.doesNotReject(async () => {
|
await assert.doesNotReject(async () => {
|
||||||
await modalListeners.get('mouseenter')?.[0]?.();
|
await contentListeners.get('mouseenter')?.[0]?.();
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(state.subtitleSidebarPausedByHover, false);
|
assert.equal(state.subtitleSidebarPausedByHover, false);
|
||||||
@@ -1744,6 +1756,7 @@ test('subtitle sidebar embedded layout restores macOS and Windows passthrough ou
|
|||||||
const mpvCommands: Array<Array<string | number>> = [];
|
const mpvCommands: Array<Array<string | number>> = [];
|
||||||
const ignoreMouseCalls: Array<[boolean, { forward?: boolean } | undefined]> = [];
|
const ignoreMouseCalls: Array<[boolean, { forward?: boolean } | undefined]> = [];
|
||||||
const modalListeners = new Map<string, Array<() => void>>();
|
const modalListeners = new Map<string, Array<() => void>>();
|
||||||
|
const contentListeners = new Map<string, Array<() => void>>();
|
||||||
|
|
||||||
const snapshot: SubtitleSidebarSnapshot = {
|
const snapshot: SubtitleSidebarSnapshot = {
|
||||||
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
||||||
@@ -1823,6 +1836,11 @@ test('subtitle sidebar embedded layout restores macOS and Windows passthrough ou
|
|||||||
subtitleSidebarContent: {
|
subtitleSidebarContent: {
|
||||||
classList: createClassList(),
|
classList: createClassList(),
|
||||||
getBoundingClientRect: () => ({ width: 360 }),
|
getBoundingClientRect: () => ({ width: 360 }),
|
||||||
|
addEventListener: (type: string, listener: () => void) => {
|
||||||
|
const bucket = contentListeners.get(type) ?? [];
|
||||||
|
bucket.push(listener);
|
||||||
|
contentListeners.set(type, bucket);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
subtitleSidebarClose: { addEventListener: () => {} },
|
subtitleSidebarClose: { addEventListener: () => {} },
|
||||||
subtitleSidebarStatus: { textContent: '' },
|
subtitleSidebarStatus: { textContent: '' },
|
||||||
@@ -1842,15 +1860,15 @@ test('subtitle sidebar embedded layout restores macOS and Windows passthrough ou
|
|||||||
await modal.openSubtitleSidebarModal();
|
await modal.openSubtitleSidebarModal();
|
||||||
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
||||||
|
|
||||||
modalListeners.get('mouseenter')?.[0]?.();
|
contentListeners.get('mouseenter')?.[0]?.();
|
||||||
assert.deepEqual(ignoreMouseCalls.at(-1), [false, undefined]);
|
assert.deepEqual(ignoreMouseCalls.at(-1), [false, undefined]);
|
||||||
|
|
||||||
modalListeners.get('mouseleave')?.[0]?.();
|
contentListeners.get('mouseleave')?.[0]?.();
|
||||||
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
||||||
|
|
||||||
state.isOverSubtitle = true;
|
state.isOverSubtitle = true;
|
||||||
modalListeners.get('mouseenter')?.[0]?.();
|
contentListeners.get('mouseenter')?.[0]?.();
|
||||||
modalListeners.get('mouseleave')?.[0]?.();
|
contentListeners.get('mouseleave')?.[0]?.();
|
||||||
assert.deepEqual(ignoreMouseCalls.at(-1), [false, undefined]);
|
assert.deepEqual(ignoreMouseCalls.at(-1), [false, undefined]);
|
||||||
|
|
||||||
void mpvCommands;
|
void mpvCommands;
|
||||||
@@ -1860,6 +1878,251 @@ test('subtitle sidebar embedded layout restores macOS and Windows passthrough ou
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('subtitle sidebar overlay layout restores macOS and Windows passthrough outside sidebar hover', async () => {
|
||||||
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
||||||
|
const previousWindow = globals.window;
|
||||||
|
const previousDocument = globals.document;
|
||||||
|
const mpvCommands: Array<Array<string | number>> = [];
|
||||||
|
const ignoreMouseCalls: Array<[boolean, { forward?: boolean } | undefined]> = [];
|
||||||
|
const modalListeners = new Map<string, Array<() => void>>();
|
||||||
|
const contentListeners = new Map<string, Array<() => void>>();
|
||||||
|
|
||||||
|
const snapshot: SubtitleSidebarSnapshot = {
|
||||||
|
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
||||||
|
currentSubtitle: {
|
||||||
|
text: 'first',
|
||||||
|
startTime: 1,
|
||||||
|
endTime: 2,
|
||||||
|
},
|
||||||
|
currentTimeSec: 1.1,
|
||||||
|
config: {
|
||||||
|
enabled: true,
|
||||||
|
autoOpen: false,
|
||||||
|
layout: 'overlay',
|
||||||
|
toggleKey: 'Backslash',
|
||||||
|
pauseVideoOnHover: false,
|
||||||
|
autoScroll: true,
|
||||||
|
maxWidth: 360,
|
||||||
|
opacity: 0.92,
|
||||||
|
backgroundColor: 'rgba(54, 58, 79, 0.88)',
|
||||||
|
textColor: '#cad3f5',
|
||||||
|
fontFamily: '"Iosevka Aile", sans-serif',
|
||||||
|
fontSize: 17,
|
||||||
|
timestampColor: '#a5adcb',
|
||||||
|
activeLineColor: '#f5bde6',
|
||||||
|
activeLineBackgroundColor: 'rgba(138, 173, 244, 0.22)',
|
||||||
|
hoverLineBackgroundColor: 'rgba(54, 58, 79, 0.84)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(globalThis, 'window', {
|
||||||
|
configurable: true,
|
||||||
|
value: {
|
||||||
|
innerWidth: 1200,
|
||||||
|
electronAPI: {
|
||||||
|
getSubtitleSidebarSnapshot: async () => snapshot,
|
||||||
|
sendMpvCommand: (command: Array<string | number>) => {
|
||||||
|
mpvCommands.push(command);
|
||||||
|
},
|
||||||
|
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
|
||||||
|
ignoreMouseCalls.push([ignore, options]);
|
||||||
|
},
|
||||||
|
} as unknown as ElectronAPI,
|
||||||
|
addEventListener: () => {},
|
||||||
|
removeEventListener: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Object.defineProperty(globalThis, 'document', {
|
||||||
|
configurable: true,
|
||||||
|
value: {
|
||||||
|
createElement: () => createCueRow(),
|
||||||
|
body: {
|
||||||
|
classList: createClassList(),
|
||||||
|
},
|
||||||
|
documentElement: {
|
||||||
|
style: {
|
||||||
|
setProperty: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const state = createRendererState();
|
||||||
|
const ctx = {
|
||||||
|
dom: {
|
||||||
|
overlay: { classList: createClassList() },
|
||||||
|
subtitleSidebarModal: {
|
||||||
|
classList: createClassList(['hidden']),
|
||||||
|
setAttribute: () => {},
|
||||||
|
style: { setProperty: () => {} },
|
||||||
|
addEventListener: (type: string, listener: () => void) => {
|
||||||
|
const bucket = modalListeners.get(type) ?? [];
|
||||||
|
bucket.push(listener);
|
||||||
|
modalListeners.set(type, bucket);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
subtitleSidebarContent: {
|
||||||
|
classList: createClassList(),
|
||||||
|
getBoundingClientRect: () => ({ width: 360 }),
|
||||||
|
addEventListener: (type: string, listener: () => void) => {
|
||||||
|
const bucket = contentListeners.get(type) ?? [];
|
||||||
|
bucket.push(listener);
|
||||||
|
contentListeners.set(type, bucket);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
subtitleSidebarClose: { addEventListener: () => {} },
|
||||||
|
subtitleSidebarStatus: { textContent: '' },
|
||||||
|
subtitleSidebarList: createListStub(),
|
||||||
|
},
|
||||||
|
platform: {
|
||||||
|
shouldToggleMouseIgnore: true,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
|
||||||
|
const modal = createSubtitleSidebarModal(ctx as never, {
|
||||||
|
modalStateReader: { isAnyModalOpen: () => false },
|
||||||
|
});
|
||||||
|
modal.wireDomEvents();
|
||||||
|
|
||||||
|
assert.equal(modalListeners.get('mouseenter')?.length ?? 0, 0);
|
||||||
|
assert.equal(modalListeners.get('mouseleave')?.length ?? 0, 0);
|
||||||
|
assert.equal(contentListeners.get('mouseenter')?.length ?? 0, 1);
|
||||||
|
assert.equal(contentListeners.get('mouseleave')?.length ?? 0, 1);
|
||||||
|
|
||||||
|
await modal.openSubtitleSidebarModal();
|
||||||
|
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
||||||
|
|
||||||
|
contentListeners.get('mouseenter')?.[0]?.();
|
||||||
|
assert.deepEqual(ignoreMouseCalls.at(-1), [false, undefined]);
|
||||||
|
|
||||||
|
contentListeners.get('mouseleave')?.[0]?.();
|
||||||
|
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
||||||
|
|
||||||
|
void mpvCommands;
|
||||||
|
} finally {
|
||||||
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
||||||
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('subtitle sidebar overlay layout only stays interactive while focus remains inside the sidebar panel', async () => {
|
||||||
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
||||||
|
const previousWindow = globals.window;
|
||||||
|
const previousDocument = globals.document;
|
||||||
|
const ignoreMouseCalls: Array<[boolean, { forward?: boolean } | undefined]> = [];
|
||||||
|
const contentListeners = new Map<string, Array<(event?: FocusEvent) => void>>();
|
||||||
|
|
||||||
|
const snapshot: SubtitleSidebarSnapshot = {
|
||||||
|
cues: [{ startTime: 1, endTime: 2, text: 'first' }],
|
||||||
|
currentSubtitle: {
|
||||||
|
text: 'first',
|
||||||
|
startTime: 1,
|
||||||
|
endTime: 2,
|
||||||
|
},
|
||||||
|
currentTimeSec: 1.1,
|
||||||
|
config: {
|
||||||
|
enabled: true,
|
||||||
|
autoOpen: false,
|
||||||
|
layout: 'overlay',
|
||||||
|
toggleKey: 'Backslash',
|
||||||
|
pauseVideoOnHover: false,
|
||||||
|
autoScroll: true,
|
||||||
|
maxWidth: 360,
|
||||||
|
opacity: 0.92,
|
||||||
|
backgroundColor: 'rgba(54, 58, 79, 0.88)',
|
||||||
|
textColor: '#cad3f5',
|
||||||
|
fontFamily: '"Iosevka Aile", sans-serif',
|
||||||
|
fontSize: 17,
|
||||||
|
timestampColor: '#a5adcb',
|
||||||
|
activeLineColor: '#f5bde6',
|
||||||
|
activeLineBackgroundColor: 'rgba(138, 173, 244, 0.22)',
|
||||||
|
hoverLineBackgroundColor: 'rgba(54, 58, 79, 0.84)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(globalThis, 'window', {
|
||||||
|
configurable: true,
|
||||||
|
value: {
|
||||||
|
innerWidth: 1200,
|
||||||
|
electronAPI: {
|
||||||
|
getSubtitleSidebarSnapshot: async () => snapshot,
|
||||||
|
sendMpvCommand: () => {},
|
||||||
|
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
|
||||||
|
ignoreMouseCalls.push([ignore, options]);
|
||||||
|
},
|
||||||
|
} as unknown as ElectronAPI,
|
||||||
|
addEventListener: () => {},
|
||||||
|
removeEventListener: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Object.defineProperty(globalThis, 'document', {
|
||||||
|
configurable: true,
|
||||||
|
value: {
|
||||||
|
createElement: () => createCueRow(),
|
||||||
|
body: {
|
||||||
|
classList: createClassList(),
|
||||||
|
},
|
||||||
|
documentElement: {
|
||||||
|
style: {
|
||||||
|
setProperty: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const state = createRendererState();
|
||||||
|
const sidebarContent = {
|
||||||
|
classList: createClassList(),
|
||||||
|
getBoundingClientRect: () => ({ width: 360 }),
|
||||||
|
addEventListener: (type: string, listener: (event?: FocusEvent) => void) => {
|
||||||
|
const bucket = contentListeners.get(type) ?? [];
|
||||||
|
bucket.push(listener);
|
||||||
|
contentListeners.set(type, bucket);
|
||||||
|
},
|
||||||
|
contains: () => false,
|
||||||
|
};
|
||||||
|
const ctx = {
|
||||||
|
dom: {
|
||||||
|
overlay: { classList: createClassList() },
|
||||||
|
subtitleSidebarModal: {
|
||||||
|
classList: createClassList(['hidden']),
|
||||||
|
setAttribute: () => {},
|
||||||
|
style: { setProperty: () => {} },
|
||||||
|
addEventListener: () => {},
|
||||||
|
},
|
||||||
|
subtitleSidebarContent: sidebarContent,
|
||||||
|
subtitleSidebarClose: { addEventListener: () => {} },
|
||||||
|
subtitleSidebarStatus: { textContent: '' },
|
||||||
|
subtitleSidebarList: createListStub(),
|
||||||
|
},
|
||||||
|
platform: {
|
||||||
|
shouldToggleMouseIgnore: true,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
|
||||||
|
const modal = createSubtitleSidebarModal(ctx as never, {
|
||||||
|
modalStateReader: { isAnyModalOpen: () => false },
|
||||||
|
});
|
||||||
|
modal.wireDomEvents();
|
||||||
|
|
||||||
|
await modal.openSubtitleSidebarModal();
|
||||||
|
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
||||||
|
|
||||||
|
contentListeners.get('focusin')?.[0]?.();
|
||||||
|
assert.deepEqual(ignoreMouseCalls.at(-1), [false, undefined]);
|
||||||
|
|
||||||
|
contentListeners.get('focusout')?.[0]?.({ relatedTarget: null } as FocusEvent);
|
||||||
|
assert.deepEqual(ignoreMouseCalls.at(-1), [true, { forward: true }]);
|
||||||
|
} finally {
|
||||||
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
||||||
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('closing embedded subtitle sidebar recomputes passthrough from remaining subtitle hover state', async () => {
|
test('closing embedded subtitle sidebar recomputes passthrough from remaining subtitle hover state', async () => {
|
||||||
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
||||||
const previousWindow = globals.window;
|
const previousWindow = globals.window;
|
||||||
|
|||||||
@@ -143,11 +143,23 @@ export function createSubtitleSidebarModal(
|
|||||||
let lastAppliedVideoMarginRatio: number | null = null;
|
let lastAppliedVideoMarginRatio: number | null = null;
|
||||||
let subtitleSidebarHoverRequestId = 0;
|
let subtitleSidebarHoverRequestId = 0;
|
||||||
let disposeDomEvents: (() => void) | null = null;
|
let disposeDomEvents: (() => void) | null = null;
|
||||||
|
let subtitleSidebarHovered = false;
|
||||||
|
let subtitleSidebarFocusedWithin = false;
|
||||||
|
|
||||||
function restoreEmbeddedSidebarPassthrough(): void {
|
function restoreEmbeddedSidebarPassthrough(): void {
|
||||||
syncOverlayMouseIgnoreState(ctx);
|
syncOverlayMouseIgnoreState(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncSidebarInteractionState(): void {
|
||||||
|
ctx.state.isOverSubtitleSidebar = subtitleSidebarHovered || subtitleSidebarFocusedWithin;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSidebarInteractionState(): void {
|
||||||
|
subtitleSidebarHovered = false;
|
||||||
|
subtitleSidebarFocusedWithin = false;
|
||||||
|
syncSidebarInteractionState();
|
||||||
|
}
|
||||||
|
|
||||||
function setStatus(message: string): void {
|
function setStatus(message: string): void {
|
||||||
ctx.dom.subtitleSidebarStatus.textContent = message;
|
ctx.dom.subtitleSidebarStatus.textContent = message;
|
||||||
}
|
}
|
||||||
@@ -379,6 +391,7 @@ export function createSubtitleSidebarModal(
|
|||||||
applyConfig(snapshot);
|
applyConfig(snapshot);
|
||||||
if (!snapshot.config.enabled) {
|
if (!snapshot.config.enabled) {
|
||||||
resumeSubtitleSidebarHoverPause();
|
resumeSubtitleSidebarHoverPause();
|
||||||
|
clearSidebarInteractionState();
|
||||||
ctx.state.subtitleSidebarCues = [];
|
ctx.state.subtitleSidebarCues = [];
|
||||||
ctx.state.subtitleSidebarModalOpen = false;
|
ctx.state.subtitleSidebarModalOpen = false;
|
||||||
ctx.dom.subtitleSidebarModal.classList.add('hidden');
|
ctx.dom.subtitleSidebarModal.classList.add('hidden');
|
||||||
@@ -450,7 +463,7 @@ export function createSubtitleSidebarModal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.state.subtitleSidebarModalOpen = true;
|
ctx.state.subtitleSidebarModalOpen = true;
|
||||||
ctx.state.isOverSubtitleSidebar = false;
|
clearSidebarInteractionState();
|
||||||
ctx.dom.subtitleSidebarModal.classList.remove('hidden');
|
ctx.dom.subtitleSidebarModal.classList.remove('hidden');
|
||||||
ctx.dom.subtitleSidebarModal.setAttribute('aria-hidden', 'false');
|
ctx.dom.subtitleSidebarModal.setAttribute('aria-hidden', 'false');
|
||||||
renderCueList();
|
renderCueList();
|
||||||
@@ -478,7 +491,7 @@ export function createSubtitleSidebarModal(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resumeSubtitleSidebarHoverPause();
|
resumeSubtitleSidebarHoverPause();
|
||||||
ctx.state.isOverSubtitleSidebar = false;
|
clearSidebarInteractionState();
|
||||||
ctx.state.subtitleSidebarModalOpen = false;
|
ctx.state.subtitleSidebarModalOpen = false;
|
||||||
ctx.dom.subtitleSidebarModal.classList.add('hidden');
|
ctx.dom.subtitleSidebarModal.classList.add('hidden');
|
||||||
ctx.dom.subtitleSidebarModal.setAttribute('aria-hidden', 'true');
|
ctx.dom.subtitleSidebarModal.setAttribute('aria-hidden', 'true');
|
||||||
@@ -536,8 +549,9 @@ export function createSubtitleSidebarModal(
|
|||||||
ctx.dom.subtitleSidebarList.addEventListener('wheel', () => {
|
ctx.dom.subtitleSidebarList.addEventListener('wheel', () => {
|
||||||
ctx.state.subtitleSidebarManualScrollUntilMs = Date.now() + MANUAL_SCROLL_HOLD_MS;
|
ctx.state.subtitleSidebarManualScrollUntilMs = Date.now() + MANUAL_SCROLL_HOLD_MS;
|
||||||
});
|
});
|
||||||
ctx.dom.subtitleSidebarModal.addEventListener('mouseenter', async () => {
|
ctx.dom.subtitleSidebarContent.addEventListener('mouseenter', async () => {
|
||||||
ctx.state.isOverSubtitleSidebar = true;
|
subtitleSidebarHovered = true;
|
||||||
|
syncSidebarInteractionState();
|
||||||
restoreEmbeddedSidebarPassthrough();
|
restoreEmbeddedSidebarPassthrough();
|
||||||
if (!ctx.state.subtitleSidebarPauseVideoOnHover || ctx.state.subtitleSidebarPausedByHover) {
|
if (!ctx.state.subtitleSidebarPauseVideoOnHover || ctx.state.subtitleSidebarPausedByHover) {
|
||||||
return;
|
return;
|
||||||
@@ -557,8 +571,36 @@ export function createSubtitleSidebarModal(
|
|||||||
ctx.state.subtitleSidebarPausedByHover = true;
|
ctx.state.subtitleSidebarPausedByHover = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ctx.dom.subtitleSidebarModal.addEventListener('mouseleave', () => {
|
ctx.dom.subtitleSidebarContent.addEventListener('mouseleave', () => {
|
||||||
ctx.state.isOverSubtitleSidebar = false;
|
subtitleSidebarHovered = false;
|
||||||
|
syncSidebarInteractionState();
|
||||||
|
if (ctx.state.isOverSubtitleSidebar) {
|
||||||
|
restoreEmbeddedSidebarPassthrough();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resumeSubtitleSidebarHoverPause();
|
||||||
|
});
|
||||||
|
ctx.dom.subtitleSidebarContent.addEventListener('focusin', () => {
|
||||||
|
subtitleSidebarFocusedWithin = true;
|
||||||
|
syncSidebarInteractionState();
|
||||||
|
restoreEmbeddedSidebarPassthrough();
|
||||||
|
});
|
||||||
|
ctx.dom.subtitleSidebarContent.addEventListener('focusout', (event: FocusEvent) => {
|
||||||
|
const relatedTarget = event.relatedTarget;
|
||||||
|
if (
|
||||||
|
typeof Node !== 'undefined' &&
|
||||||
|
relatedTarget instanceof Node &&
|
||||||
|
ctx.dom.subtitleSidebarContent.contains(relatedTarget)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subtitleSidebarFocusedWithin = false;
|
||||||
|
syncSidebarInteractionState();
|
||||||
|
if (ctx.state.isOverSubtitleSidebar) {
|
||||||
|
restoreEmbeddedSidebarPassthrough();
|
||||||
|
return;
|
||||||
|
}
|
||||||
resumeSubtitleSidebarHoverPause();
|
resumeSubtitleSidebarHoverPause();
|
||||||
});
|
});
|
||||||
const resizeHandler = () => {
|
const resizeHandler = () => {
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ import type { RendererContext } from './context';
|
|||||||
import type { RendererState } from './state';
|
import type { RendererState } from './state';
|
||||||
|
|
||||||
function isBlockingOverlayModalOpen(state: RendererState): boolean {
|
function isBlockingOverlayModalOpen(state: RendererState): boolean {
|
||||||
const embeddedSidebarOpen =
|
|
||||||
state.subtitleSidebarModalOpen && state.subtitleSidebarConfig?.layout === 'embedded';
|
|
||||||
|
|
||||||
return Boolean(
|
return Boolean(
|
||||||
state.controllerSelectModalOpen ||
|
state.controllerSelectModalOpen ||
|
||||||
state.controllerDebugModalOpen ||
|
state.controllerDebugModalOpen ||
|
||||||
@@ -13,8 +10,7 @@ function isBlockingOverlayModalOpen(state: RendererState): boolean {
|
|||||||
state.kikuModalOpen ||
|
state.kikuModalOpen ||
|
||||||
state.runtimeOptionsModalOpen ||
|
state.runtimeOptionsModalOpen ||
|
||||||
state.subsyncModalOpen ||
|
state.subsyncModalOpen ||
|
||||||
state.sessionHelpModalOpen ||
|
state.sessionHelpModalOpen,
|
||||||
(state.subtitleSidebarModalOpen && !embeddedSidebarOpen),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user