mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-01 06:12:07 -07:00
fix: wire playlist-browser IPC deps through bootstrap surfaces
- Thread `openPlaylistBrowser` action into `IpcRuntimeBootstrapInput` - Pass `playlistBrowserMainDeps` through bootstrap into `createIpcRuntime` - Add playlist-browser mock deps to ipc-runtime tests - Bump subminer-yomitan submodule
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
---
|
||||
id: TASK-238.8
|
||||
title: Refactor src/main.ts composition root into domain runtimes
|
||||
status: To Do
|
||||
status: In Progress
|
||||
assignee: []
|
||||
created_date: '2026-03-31 06:28'
|
||||
updated_date: '2026-04-01 07:07'
|
||||
labels:
|
||||
- tech-debt
|
||||
- runtime
|
||||
@@ -34,3 +35,9 @@ Refactor `src/main.ts` so it becomes a thin composition root and the domain-spec
|
||||
- [ ] #5 Cross-domain coordination stays in `main.ts`; wrapper modules stay acyclic and communicate via injected callbacks.
|
||||
- [ ] #6 No user-facing behavior, config fields, or IPC channel names change.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
CI follow-up: typecheck failed after the runtime split because playlist-browser IPC deps were not threaded through the new bootstrap/composer surfaces. Wiring the missing open action and registration deps now.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
15
src/main.ts
15
src/main.ts
@@ -131,6 +131,8 @@ import { shouldAutoOpenFirstRunSetup } from './main/first-run-runtime';
|
||||
import { createWindowsMpvLaunchDeps, launchWindowsMpv } from './main/runtime/windows-mpv-launch';
|
||||
import { shouldEnsureTrayOnStartupForInitialArgs } from './main/runtime/startup-tray-policy';
|
||||
import { createImmersionTrackerStartupHandler } from './main/runtime/immersion-startup';
|
||||
import { createPlaylistBrowserIpcRuntime } from './main/runtime/playlist-browser-ipc';
|
||||
import { openPlaylistBrowser as openPlaylistBrowserRuntime } from './main/runtime/playlist-browser-open';
|
||||
import {
|
||||
createRunStatsCliCommandHandler,
|
||||
writeStatsCliCommandResponse,
|
||||
@@ -931,6 +933,7 @@ const { mpvRuntime, mining } = createMainPlaybackRuntime({
|
||||
},
|
||||
},
|
||||
});
|
||||
const playlistBrowserIpcRuntime = createPlaylistBrowserIpcRuntime(() => appState.mpvClient);
|
||||
|
||||
function createMpvClientRuntimeService(): MpvIpcClient {
|
||||
return mpvRuntime.createMpvClientRuntimeService();
|
||||
@@ -1101,6 +1104,15 @@ const { registerIpcRuntimeHandlers } = createIpcRuntimeBootstrap({
|
||||
actions: {
|
||||
requestAppQuit,
|
||||
openYomitanSettings: () => yomitan.openYomitanSettings(),
|
||||
openPlaylistBrowser: () => {
|
||||
void openPlaylistBrowserRuntime({
|
||||
ensureOverlayStartupPrereqs: () => startupRuntime.appReady.ensureOverlayStartupPrereqs(),
|
||||
ensureOverlayWindowsReadyForVisibilityActions: () =>
|
||||
overlayUi?.ensureOverlayWindowsReadyForVisibilityActions(),
|
||||
sendToActiveOverlayWindow: (channel, payload, runtimeOptions) =>
|
||||
overlayUi?.sendToActiveOverlayWindow(channel, payload, runtimeOptions) ?? false,
|
||||
});
|
||||
},
|
||||
showDesktopNotification,
|
||||
setAnkiIntegration: (integration: AnkiIntegration | null) => {
|
||||
appState.ankiIntegration = integration;
|
||||
@@ -1112,6 +1124,9 @@ const { registerIpcRuntimeHandlers } = createIpcRuntimeBootstrap({
|
||||
anilist,
|
||||
mining,
|
||||
dictionarySupport,
|
||||
playlistBrowser: {
|
||||
playlistBrowserMainDeps: playlistBrowserIpcRuntime.playlistBrowserMainDeps,
|
||||
},
|
||||
configDerived: configDerivedRuntime,
|
||||
subsync: subsyncRuntime,
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@ import type { OverlayModalRuntime } from './overlay-runtime';
|
||||
import type { OverlayUiRuntime } from './overlay-ui-runtime';
|
||||
import type { AppState } from './state';
|
||||
import type { SubtitleRuntime } from './subtitle-runtime';
|
||||
import type { PlaylistBrowserIpcRuntime } from './runtime/playlist-browser-ipc';
|
||||
import type { YoutubeRuntime } from './youtube-runtime';
|
||||
import { resolveSubtitleStyleForRenderer } from './runtime/domains/overlay';
|
||||
import type { ShortcutsRuntime } from './shortcuts-runtime';
|
||||
@@ -82,6 +83,7 @@ export interface IpcRuntimeBootstrapInput {
|
||||
actions: {
|
||||
requestAppQuit: () => void;
|
||||
openYomitanSettings: () => boolean;
|
||||
openPlaylistBrowser: () => void | Promise<void>;
|
||||
showDesktopNotification: (title: string, options: { body?: string }) => void;
|
||||
setAnkiIntegration: (integration: AnkiIntegration | null) => void;
|
||||
};
|
||||
@@ -103,6 +105,7 @@ export interface IpcRuntimeBootstrapInput {
|
||||
| 'setFieldGroupingResolver'
|
||||
| 'resolveMediaPathForJimaku'
|
||||
>;
|
||||
playlistBrowser: Pick<PlaylistBrowserIpcRuntime, 'playlistBrowserMainDeps'>;
|
||||
configDerived: ConfigDerivedRuntimeLike;
|
||||
subsync: SubsyncRuntimeLike;
|
||||
};
|
||||
@@ -115,6 +118,7 @@ export function createIpcRuntimeBootstrap(input: IpcRuntimeBootstrapInput): IpcR
|
||||
triggerSubsyncFromConfig: () => input.runtimes.subsync.triggerFromConfig(),
|
||||
openRuntimeOptionsPalette: () => input.overlay.getOverlayUi()?.openRuntimeOptionsPalette(),
|
||||
openYoutubeTrackPicker: () => input.runtimes.youtube.openYoutubeTrackPickerFromPlayback(),
|
||||
openPlaylistBrowser: () => input.actions.openPlaylistBrowser(),
|
||||
cycleRuntimeOption: (id, direction) => {
|
||||
if (!input.appState.runtimeOptionsManager) {
|
||||
return { ok: false, error: 'Runtime options manager unavailable' };
|
||||
@@ -204,6 +208,7 @@ export function createIpcRuntimeBootstrap(input: IpcRuntimeBootstrapInput): IpcR
|
||||
},
|
||||
getImmersionTracker: () => input.appState.immersionTracker,
|
||||
},
|
||||
playlistBrowser: input.runtimes.playlistBrowser.playlistBrowserMainDeps,
|
||||
anilist: {
|
||||
getStatus: () => input.runtimes.anilist.getStatusSnapshot(),
|
||||
clearToken: () => input.runtimes.anilist.clearTokenState(),
|
||||
|
||||
@@ -17,6 +17,21 @@ function createBaseRuntimeInput(capturedRegistration: { value: unknown | null })
|
||||
quitApp: () => {},
|
||||
toggleVisibleOverlay: () => {},
|
||||
},
|
||||
playlistBrowser: {
|
||||
getPlaylistBrowserSnapshot: async () => ({
|
||||
directoryPath: null,
|
||||
directoryAvailable: false,
|
||||
directoryStatus: '',
|
||||
directoryItems: [],
|
||||
playlistItems: [],
|
||||
playingIndex: null,
|
||||
currentFilePath: null,
|
||||
}),
|
||||
appendPlaylistBrowserFile: async () => ({ ok: true, message: 'ok' }),
|
||||
playPlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
removePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
movePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
},
|
||||
subtitle: {
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
@@ -86,6 +101,7 @@ function createBaseRuntimeInput(capturedRegistration: { value: unknown | null })
|
||||
triggerSubsyncFromConfig: () => {},
|
||||
openRuntimeOptionsPalette: () => {},
|
||||
openYoutubeTrackPicker: () => {},
|
||||
openPlaylistBrowser: () => {},
|
||||
cycleRuntimeOption: () => ({ ok: true }),
|
||||
showMpvOsd: () => {},
|
||||
replayCurrentSubtitle: () => {},
|
||||
@@ -134,11 +150,13 @@ test('ipc runtime registers composed IPC handlers from explicit registration inp
|
||||
const registration = capturedRegistration.value as {
|
||||
runtimeOptions: { showMpvOsd: unknown };
|
||||
mainDeps: {
|
||||
getPlaylistBrowserSnapshot: unknown;
|
||||
handleMpvCommand: unknown;
|
||||
runSubsyncManual: (payload: unknown) => Promise<unknown>;
|
||||
};
|
||||
};
|
||||
assert.equal(registration.runtimeOptions.showMpvOsd !== undefined, true);
|
||||
assert.equal(registration.mainDeps.getPlaylistBrowserSnapshot instanceof Function, true);
|
||||
assert.equal(registration.mainDeps.handleMpvCommand instanceof Function, true);
|
||||
assert.deepEqual(
|
||||
await registration.mainDeps.runSubsyncManual({ payload: null } as never),
|
||||
@@ -163,11 +181,13 @@ test('ipc runtime builds grouped registration input from main state', async () =
|
||||
const registration = capturedRegistration.value as {
|
||||
runtimeOptions: { showMpvOsd: unknown };
|
||||
mainDeps: {
|
||||
getPlaylistBrowserSnapshot: unknown;
|
||||
handleMpvCommand: unknown;
|
||||
runSubsyncManual: (payload: unknown) => Promise<unknown>;
|
||||
};
|
||||
};
|
||||
assert.equal(registration.runtimeOptions.showMpvOsd !== undefined, true);
|
||||
assert.equal(registration.mainDeps.getPlaylistBrowserSnapshot instanceof Function, true);
|
||||
assert.equal(registration.mainDeps.handleMpvCommand instanceof Function, true);
|
||||
assert.deepEqual(
|
||||
await registration.mainDeps.runSubsyncManual({ payload: null } as never),
|
||||
|
||||
@@ -30,6 +30,14 @@ export interface IpcRuntimeMainInput {
|
||||
| 'quitApp'
|
||||
| 'toggleVisibleOverlay'
|
||||
>;
|
||||
playlistBrowser: Pick<
|
||||
RegisterIpcRuntimeServicesParams['mainDeps'],
|
||||
| 'getPlaylistBrowserSnapshot'
|
||||
| 'appendPlaylistBrowserFile'
|
||||
| 'playPlaylistBrowserIndex'
|
||||
| 'removePlaylistBrowserIndex'
|
||||
| 'movePlaylistBrowserIndex'
|
||||
>;
|
||||
subtitle: Pick<
|
||||
RegisterIpcRuntimeServicesParams['mainDeps'],
|
||||
| 'tokenizeCurrentSubtitle'
|
||||
@@ -110,6 +118,7 @@ export function createIpcRuntime(input: IpcRuntimeInput): IpcRuntime {
|
||||
runtimeOptions: input.registration.runtimeOptions,
|
||||
mainDeps: {
|
||||
...input.registration.main.window,
|
||||
...input.registration.main.playlistBrowser,
|
||||
...input.registration.main.subtitle,
|
||||
...input.registration.main.controller,
|
||||
...input.registration.main.runtime,
|
||||
|
||||
2
vendor/subminer-yomitan
vendored
2
vendor/subminer-yomitan
vendored
Submodule vendor/subminer-yomitan updated: 3c9ee577ac...69620abcbc
Reference in New Issue
Block a user