mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-22 12:11:27 -07:00
feat(subtitle-sidebar): add sidebar runtime and modal plumbing
This commit is contained in:
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
|
||||
|
||||
import { createIpcDepsRuntime, registerIpcHandlers, type IpcServiceDeps } from './ipc';
|
||||
import { IPC_CHANNELS } from '../../shared/ipc/contracts';
|
||||
import type { SubtitleSidebarSnapshot } from '../../types';
|
||||
|
||||
interface FakeIpcRegistrar {
|
||||
on: Map<string, (event: unknown, ...args: unknown[]) => void>;
|
||||
@@ -77,6 +78,30 @@ function createControllerConfigFixture() {
|
||||
};
|
||||
}
|
||||
|
||||
function createSubtitleSidebarSnapshotFixture(): SubtitleSidebarSnapshot {
|
||||
return {
|
||||
cues: [],
|
||||
currentSubtitle: { text: '', startTime: null, endTime: null },
|
||||
config: {
|
||||
enabled: false,
|
||||
layout: 'overlay',
|
||||
toggleKey: 'Backslash',
|
||||
pauseVideoOnHover: false,
|
||||
autoScroll: true,
|
||||
maxWidth: 420,
|
||||
opacity: 0.92,
|
||||
backgroundColor: 'rgba(54, 58, 79, 0.88)',
|
||||
textColor: '#cad3f5',
|
||||
fontFamily: '"M PLUS 1", "Noto Sans CJK JP", sans-serif',
|
||||
fontSize: 16,
|
||||
timestampColor: '#a5adcb',
|
||||
activeLineColor: '#f5bde6',
|
||||
activeLineBackgroundColor: 'rgba(138, 173, 244, 0.22)',
|
||||
hoverLineBackgroundColor: 'rgba(54, 58, 79, 0.84)',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServiceDeps {
|
||||
return {
|
||||
onOverlayModalClosed: () => {},
|
||||
@@ -88,6 +113,7 @@ function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServ
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
@@ -173,6 +199,7 @@ test('createIpcDepsRuntime wires AniList handlers', async () => {
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => true,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
@@ -269,6 +296,7 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
cycles.push({ id, direction });
|
||||
return { ok: true };
|
||||
},
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
reportOverlayContentBounds: () => {},
|
||||
getAnilistStatus: () => ({}),
|
||||
clearAnilistToken: () => {},
|
||||
@@ -320,6 +348,24 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers exposes subtitle sidebar snapshot request', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const snapshot = createSubtitleSidebarSnapshotFixture();
|
||||
snapshot.cues = [{ startTime: 1, endTime: 2, text: 'line-1' }];
|
||||
snapshot.config.enabled = true;
|
||||
|
||||
registerIpcHandlers(
|
||||
createRegisterIpcDeps({
|
||||
getSubtitleSidebarSnapshot: async () => snapshot,
|
||||
}),
|
||||
registrar,
|
||||
);
|
||||
|
||||
const handler = handlers.handle.get(IPC_CHANNELS.request.getSubtitleSidebarSnapshot);
|
||||
assert.ok(handler);
|
||||
assert.deepEqual(await handler!({}), snapshot);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers forwards yomitan lookup tracking commands to immersion tracker', () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const calls: string[] = [];
|
||||
@@ -530,6 +576,7 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
@@ -596,6 +643,7 @@ test('registerIpcHandlers awaits saveControllerPreference through request-respon
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
@@ -667,6 +715,7 @@ test('registerIpcHandlers rejects malformed controller preference payloads', asy
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
ResolvedControllerConfig,
|
||||
RuntimeOptionId,
|
||||
RuntimeOptionValue,
|
||||
SubtitleSidebarSnapshot,
|
||||
SubtitlePosition,
|
||||
SubsyncManualRunRequest,
|
||||
SubsyncResult,
|
||||
@@ -37,6 +38,7 @@ export interface IpcServiceDeps {
|
||||
tokenizeCurrentSubtitle: () => Promise<unknown>;
|
||||
getCurrentSubtitleRaw: () => string;
|
||||
getCurrentSubtitleAss: () => string;
|
||||
getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>;
|
||||
getPlaybackPaused: () => boolean | null;
|
||||
getSubtitlePosition: () => unknown;
|
||||
getSubtitleStyle: () => unknown;
|
||||
@@ -143,6 +145,7 @@ export interface IpcDepsRuntimeOptions {
|
||||
tokenizeCurrentSubtitle: () => Promise<unknown>;
|
||||
getCurrentSubtitleRaw: () => string;
|
||||
getCurrentSubtitleAss: () => string;
|
||||
getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>;
|
||||
getPlaybackPaused: () => boolean | null;
|
||||
getSubtitlePosition: () => unknown;
|
||||
getSubtitleStyle: () => unknown;
|
||||
@@ -190,6 +193,7 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
tokenizeCurrentSubtitle: options.tokenizeCurrentSubtitle,
|
||||
getCurrentSubtitleRaw: options.getCurrentSubtitleRaw,
|
||||
getCurrentSubtitleAss: options.getCurrentSubtitleAss,
|
||||
getSubtitleSidebarSnapshot: options.getSubtitleSidebarSnapshot,
|
||||
getPlaybackPaused: options.getPlaybackPaused,
|
||||
getSubtitlePosition: options.getSubtitlePosition,
|
||||
getSubtitleStyle: options.getSubtitleStyle,
|
||||
@@ -321,6 +325,13 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
return deps.getCurrentSubtitleAss();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getSubtitleSidebarSnapshot, async () => {
|
||||
if (!deps.getSubtitleSidebarSnapshot) {
|
||||
throw new Error('Subtitle sidebar snapshot is unavailable.');
|
||||
}
|
||||
return await deps.getSubtitleSidebarSnapshot();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getPlaybackPaused, () => {
|
||||
return deps.getPlaybackPaused();
|
||||
});
|
||||
|
||||
@@ -183,7 +183,13 @@ export function parseSubtitleCues(content: string, filename: string): SubtitleCu
|
||||
cues = parseAssCues(content);
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
cues = [];
|
||||
}
|
||||
|
||||
if (cues.length === 0) {
|
||||
const assCues = parseAssCues(content);
|
||||
const srtCues = parseSrtCues(content);
|
||||
cues = assCues.length >= srtCues.length ? assCues : srtCues;
|
||||
}
|
||||
|
||||
cues.sort((a, b) => a.startTime - b.startTime);
|
||||
|
||||
Reference in New Issue
Block a user