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
|
id: TASK-238.8
|
||||||
title: Refactor src/main.ts composition root into domain runtimes
|
title: Refactor src/main.ts composition root into domain runtimes
|
||||||
status: To Do
|
status: In Progress
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-31 06:28'
|
created_date: '2026-03-31 06:28'
|
||||||
|
updated_date: '2026-04-01 07:07'
|
||||||
labels:
|
labels:
|
||||||
- tech-debt
|
- tech-debt
|
||||||
- runtime
|
- 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.
|
- [ ] #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.
|
- [ ] #6 No user-facing behavior, config fields, or IPC channel names change.
|
||||||
<!-- AC:END -->
|
<!-- 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 { createWindowsMpvLaunchDeps, launchWindowsMpv } from './main/runtime/windows-mpv-launch';
|
||||||
import { shouldEnsureTrayOnStartupForInitialArgs } from './main/runtime/startup-tray-policy';
|
import { shouldEnsureTrayOnStartupForInitialArgs } from './main/runtime/startup-tray-policy';
|
||||||
import { createImmersionTrackerStartupHandler } from './main/runtime/immersion-startup';
|
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 {
|
import {
|
||||||
createRunStatsCliCommandHandler,
|
createRunStatsCliCommandHandler,
|
||||||
writeStatsCliCommandResponse,
|
writeStatsCliCommandResponse,
|
||||||
@@ -931,6 +933,7 @@ const { mpvRuntime, mining } = createMainPlaybackRuntime({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const playlistBrowserIpcRuntime = createPlaylistBrowserIpcRuntime(() => appState.mpvClient);
|
||||||
|
|
||||||
function createMpvClientRuntimeService(): MpvIpcClient {
|
function createMpvClientRuntimeService(): MpvIpcClient {
|
||||||
return mpvRuntime.createMpvClientRuntimeService();
|
return mpvRuntime.createMpvClientRuntimeService();
|
||||||
@@ -1101,6 +1104,15 @@ const { registerIpcRuntimeHandlers } = createIpcRuntimeBootstrap({
|
|||||||
actions: {
|
actions: {
|
||||||
requestAppQuit,
|
requestAppQuit,
|
||||||
openYomitanSettings: () => yomitan.openYomitanSettings(),
|
openYomitanSettings: () => yomitan.openYomitanSettings(),
|
||||||
|
openPlaylistBrowser: () => {
|
||||||
|
void openPlaylistBrowserRuntime({
|
||||||
|
ensureOverlayStartupPrereqs: () => startupRuntime.appReady.ensureOverlayStartupPrereqs(),
|
||||||
|
ensureOverlayWindowsReadyForVisibilityActions: () =>
|
||||||
|
overlayUi?.ensureOverlayWindowsReadyForVisibilityActions(),
|
||||||
|
sendToActiveOverlayWindow: (channel, payload, runtimeOptions) =>
|
||||||
|
overlayUi?.sendToActiveOverlayWindow(channel, payload, runtimeOptions) ?? false,
|
||||||
|
});
|
||||||
|
},
|
||||||
showDesktopNotification,
|
showDesktopNotification,
|
||||||
setAnkiIntegration: (integration: AnkiIntegration | null) => {
|
setAnkiIntegration: (integration: AnkiIntegration | null) => {
|
||||||
appState.ankiIntegration = integration;
|
appState.ankiIntegration = integration;
|
||||||
@@ -1112,6 +1124,9 @@ const { registerIpcRuntimeHandlers } = createIpcRuntimeBootstrap({
|
|||||||
anilist,
|
anilist,
|
||||||
mining,
|
mining,
|
||||||
dictionarySupport,
|
dictionarySupport,
|
||||||
|
playlistBrowser: {
|
||||||
|
playlistBrowserMainDeps: playlistBrowserIpcRuntime.playlistBrowserMainDeps,
|
||||||
|
},
|
||||||
configDerived: configDerivedRuntime,
|
configDerived: configDerivedRuntime,
|
||||||
subsync: subsyncRuntime,
|
subsync: subsyncRuntime,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import type { OverlayModalRuntime } from './overlay-runtime';
|
|||||||
import type { OverlayUiRuntime } from './overlay-ui-runtime';
|
import type { OverlayUiRuntime } from './overlay-ui-runtime';
|
||||||
import type { AppState } from './state';
|
import type { AppState } from './state';
|
||||||
import type { SubtitleRuntime } from './subtitle-runtime';
|
import type { SubtitleRuntime } from './subtitle-runtime';
|
||||||
|
import type { PlaylistBrowserIpcRuntime } from './runtime/playlist-browser-ipc';
|
||||||
import type { YoutubeRuntime } from './youtube-runtime';
|
import type { YoutubeRuntime } from './youtube-runtime';
|
||||||
import { resolveSubtitleStyleForRenderer } from './runtime/domains/overlay';
|
import { resolveSubtitleStyleForRenderer } from './runtime/domains/overlay';
|
||||||
import type { ShortcutsRuntime } from './shortcuts-runtime';
|
import type { ShortcutsRuntime } from './shortcuts-runtime';
|
||||||
@@ -82,6 +83,7 @@ export interface IpcRuntimeBootstrapInput {
|
|||||||
actions: {
|
actions: {
|
||||||
requestAppQuit: () => void;
|
requestAppQuit: () => void;
|
||||||
openYomitanSettings: () => boolean;
|
openYomitanSettings: () => boolean;
|
||||||
|
openPlaylistBrowser: () => void | Promise<void>;
|
||||||
showDesktopNotification: (title: string, options: { body?: string }) => void;
|
showDesktopNotification: (title: string, options: { body?: string }) => void;
|
||||||
setAnkiIntegration: (integration: AnkiIntegration | null) => void;
|
setAnkiIntegration: (integration: AnkiIntegration | null) => void;
|
||||||
};
|
};
|
||||||
@@ -103,6 +105,7 @@ export interface IpcRuntimeBootstrapInput {
|
|||||||
| 'setFieldGroupingResolver'
|
| 'setFieldGroupingResolver'
|
||||||
| 'resolveMediaPathForJimaku'
|
| 'resolveMediaPathForJimaku'
|
||||||
>;
|
>;
|
||||||
|
playlistBrowser: Pick<PlaylistBrowserIpcRuntime, 'playlistBrowserMainDeps'>;
|
||||||
configDerived: ConfigDerivedRuntimeLike;
|
configDerived: ConfigDerivedRuntimeLike;
|
||||||
subsync: SubsyncRuntimeLike;
|
subsync: SubsyncRuntimeLike;
|
||||||
};
|
};
|
||||||
@@ -115,6 +118,7 @@ export function createIpcRuntimeBootstrap(input: IpcRuntimeBootstrapInput): IpcR
|
|||||||
triggerSubsyncFromConfig: () => input.runtimes.subsync.triggerFromConfig(),
|
triggerSubsyncFromConfig: () => input.runtimes.subsync.triggerFromConfig(),
|
||||||
openRuntimeOptionsPalette: () => input.overlay.getOverlayUi()?.openRuntimeOptionsPalette(),
|
openRuntimeOptionsPalette: () => input.overlay.getOverlayUi()?.openRuntimeOptionsPalette(),
|
||||||
openYoutubeTrackPicker: () => input.runtimes.youtube.openYoutubeTrackPickerFromPlayback(),
|
openYoutubeTrackPicker: () => input.runtimes.youtube.openYoutubeTrackPickerFromPlayback(),
|
||||||
|
openPlaylistBrowser: () => input.actions.openPlaylistBrowser(),
|
||||||
cycleRuntimeOption: (id, direction) => {
|
cycleRuntimeOption: (id, direction) => {
|
||||||
if (!input.appState.runtimeOptionsManager) {
|
if (!input.appState.runtimeOptionsManager) {
|
||||||
return { ok: false, error: 'Runtime options manager unavailable' };
|
return { ok: false, error: 'Runtime options manager unavailable' };
|
||||||
@@ -204,6 +208,7 @@ export function createIpcRuntimeBootstrap(input: IpcRuntimeBootstrapInput): IpcR
|
|||||||
},
|
},
|
||||||
getImmersionTracker: () => input.appState.immersionTracker,
|
getImmersionTracker: () => input.appState.immersionTracker,
|
||||||
},
|
},
|
||||||
|
playlistBrowser: input.runtimes.playlistBrowser.playlistBrowserMainDeps,
|
||||||
anilist: {
|
anilist: {
|
||||||
getStatus: () => input.runtimes.anilist.getStatusSnapshot(),
|
getStatus: () => input.runtimes.anilist.getStatusSnapshot(),
|
||||||
clearToken: () => input.runtimes.anilist.clearTokenState(),
|
clearToken: () => input.runtimes.anilist.clearTokenState(),
|
||||||
|
|||||||
@@ -17,6 +17,21 @@ function createBaseRuntimeInput(capturedRegistration: { value: unknown | null })
|
|||||||
quitApp: () => {},
|
quitApp: () => {},
|
||||||
toggleVisibleOverlay: () => {},
|
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: {
|
subtitle: {
|
||||||
tokenizeCurrentSubtitle: async () => null,
|
tokenizeCurrentSubtitle: async () => null,
|
||||||
getCurrentSubtitleRaw: () => '',
|
getCurrentSubtitleRaw: () => '',
|
||||||
@@ -86,6 +101,7 @@ function createBaseRuntimeInput(capturedRegistration: { value: unknown | null })
|
|||||||
triggerSubsyncFromConfig: () => {},
|
triggerSubsyncFromConfig: () => {},
|
||||||
openRuntimeOptionsPalette: () => {},
|
openRuntimeOptionsPalette: () => {},
|
||||||
openYoutubeTrackPicker: () => {},
|
openYoutubeTrackPicker: () => {},
|
||||||
|
openPlaylistBrowser: () => {},
|
||||||
cycleRuntimeOption: () => ({ ok: true }),
|
cycleRuntimeOption: () => ({ ok: true }),
|
||||||
showMpvOsd: () => {},
|
showMpvOsd: () => {},
|
||||||
replayCurrentSubtitle: () => {},
|
replayCurrentSubtitle: () => {},
|
||||||
@@ -134,11 +150,13 @@ test('ipc runtime registers composed IPC handlers from explicit registration inp
|
|||||||
const registration = capturedRegistration.value as {
|
const registration = capturedRegistration.value as {
|
||||||
runtimeOptions: { showMpvOsd: unknown };
|
runtimeOptions: { showMpvOsd: unknown };
|
||||||
mainDeps: {
|
mainDeps: {
|
||||||
|
getPlaylistBrowserSnapshot: unknown;
|
||||||
handleMpvCommand: unknown;
|
handleMpvCommand: unknown;
|
||||||
runSubsyncManual: (payload: unknown) => Promise<unknown>;
|
runSubsyncManual: (payload: unknown) => Promise<unknown>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
assert.equal(registration.runtimeOptions.showMpvOsd !== undefined, true);
|
assert.equal(registration.runtimeOptions.showMpvOsd !== undefined, true);
|
||||||
|
assert.equal(registration.mainDeps.getPlaylistBrowserSnapshot instanceof Function, true);
|
||||||
assert.equal(registration.mainDeps.handleMpvCommand instanceof Function, true);
|
assert.equal(registration.mainDeps.handleMpvCommand instanceof Function, true);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
await registration.mainDeps.runSubsyncManual({ payload: null } as never),
|
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 {
|
const registration = capturedRegistration.value as {
|
||||||
runtimeOptions: { showMpvOsd: unknown };
|
runtimeOptions: { showMpvOsd: unknown };
|
||||||
mainDeps: {
|
mainDeps: {
|
||||||
|
getPlaylistBrowserSnapshot: unknown;
|
||||||
handleMpvCommand: unknown;
|
handleMpvCommand: unknown;
|
||||||
runSubsyncManual: (payload: unknown) => Promise<unknown>;
|
runSubsyncManual: (payload: unknown) => Promise<unknown>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
assert.equal(registration.runtimeOptions.showMpvOsd !== undefined, true);
|
assert.equal(registration.runtimeOptions.showMpvOsd !== undefined, true);
|
||||||
|
assert.equal(registration.mainDeps.getPlaylistBrowserSnapshot instanceof Function, true);
|
||||||
assert.equal(registration.mainDeps.handleMpvCommand instanceof Function, true);
|
assert.equal(registration.mainDeps.handleMpvCommand instanceof Function, true);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
await registration.mainDeps.runSubsyncManual({ payload: null } as never),
|
await registration.mainDeps.runSubsyncManual({ payload: null } as never),
|
||||||
|
|||||||
@@ -30,6 +30,14 @@ export interface IpcRuntimeMainInput {
|
|||||||
| 'quitApp'
|
| 'quitApp'
|
||||||
| 'toggleVisibleOverlay'
|
| 'toggleVisibleOverlay'
|
||||||
>;
|
>;
|
||||||
|
playlistBrowser: Pick<
|
||||||
|
RegisterIpcRuntimeServicesParams['mainDeps'],
|
||||||
|
| 'getPlaylistBrowserSnapshot'
|
||||||
|
| 'appendPlaylistBrowserFile'
|
||||||
|
| 'playPlaylistBrowserIndex'
|
||||||
|
| 'removePlaylistBrowserIndex'
|
||||||
|
| 'movePlaylistBrowserIndex'
|
||||||
|
>;
|
||||||
subtitle: Pick<
|
subtitle: Pick<
|
||||||
RegisterIpcRuntimeServicesParams['mainDeps'],
|
RegisterIpcRuntimeServicesParams['mainDeps'],
|
||||||
| 'tokenizeCurrentSubtitle'
|
| 'tokenizeCurrentSubtitle'
|
||||||
@@ -110,6 +118,7 @@ export function createIpcRuntime(input: IpcRuntimeInput): IpcRuntime {
|
|||||||
runtimeOptions: input.registration.runtimeOptions,
|
runtimeOptions: input.registration.runtimeOptions,
|
||||||
mainDeps: {
|
mainDeps: {
|
||||||
...input.registration.main.window,
|
...input.registration.main.window,
|
||||||
|
...input.registration.main.playlistBrowser,
|
||||||
...input.registration.main.subtitle,
|
...input.registration.main.subtitle,
|
||||||
...input.registration.main.controller,
|
...input.registration.main.controller,
|
||||||
...input.registration.main.runtime,
|
...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