# SubMiner Refactoring Plan ## Goals - Eliminate unnecessary abstraction layers and thin wrapper services - Consolidate related services into cohesive domain modules - Fix known bugs and code quality issues - Add test coverage for critical untested services - Keep main.ts lean without pushing complexity into pointless indirection ## Guiding Principles - **Verify after every phase**: `pnpm run build && pnpm run test:config && pnpm run test:core` must pass - **One concern per commit**: each commit should be a single logical change (can be multiple files as long as it makes sense logically) - **Inline first, restructure second**: delete the wrapper, verify nothing breaks, then clean up - **Don't create new abstractions**: the goal is fewer files, not different files --- ## Phase 1: Delete Thin Wrappers (9 files + 7 test files) These services wrap a single function call or trivial operation. Inline their logic into callers (mostly main.ts or the service that calls them) and delete both the service file and its test file. ### 1.1 Inline `config-warning-runtime-service.ts` (14 lines) Two pure string-formatting functions. Inline the format string directly where `logConfigWarningRuntimeService` is called (should be in main.ts startup or `app-logging-runtime-service.ts`). Delete both files. - Delete: `config-warning-runtime-service.ts`, `config-warning-runtime-service.test.ts` ### 1.2 Inline `overlay-modal-restore-service.ts` (18 lines) Wraps `Set.add()` and a conditional `Set.delete()`. Inline the Set operations at call sites in main.ts. - Delete: `overlay-modal-restore-service.ts`, `overlay-modal-restore-service.test.ts` ### 1.3 Inline `runtime-options-manager-runtime-service.ts` (17 lines) Wraps `new RuntimeOptionsManager(...)`. Call the constructor directly. - Delete: `runtime-options-manager-runtime-service.ts`, `runtime-options-manager-runtime-service.test.ts` ### 1.4 Inline `app-logging-runtime-service.ts` (28 lines) Creates an object with two methods. After inlining config-warning (1.1), this becomes a trivial object literal. Inline where the logging runtime is created. - Delete: `app-logging-runtime-service.ts`, `app-logging-runtime-service.test.ts` ### 1.5 Inline `overlay-send-service.ts` (26 lines) Wraps `window.webContents.send()` with a null/destroyed guard. This is a one-liner pattern. Inline at call sites. - Delete: `overlay-send-service.ts` (no test file) ### 1.6 Inline `startup-resource-runtime-service.ts` (26 lines) Two functions that call a constructor and a check method. Inline into the app-ready startup flow. - Delete: `startup-resource-runtime-service.ts`, `startup-resource-runtime-service.test.ts` ### 1.7 Inline `config-generation-runtime-service.ts` (26 lines) Simple conditional (if args say generate config, call generator, quit). Inline into the startup bootstrap flow. - Delete: `config-generation-runtime-service.ts`, `config-generation-runtime-service.test.ts` ### 1.8 Inline `app-shutdown-runtime-service.ts` (27 lines) Calls 10 cleanup functions in sequence. Inline into the willQuit handler in main.ts. - Delete: `app-shutdown-runtime-service.ts`, `app-shutdown-runtime-service.test.ts` ### 1.9 Inline `shortcut-ui-deps-runtime-service.ts` (24 lines) Single wrapper that unwraps 4 getters and calls `runOverlayShortcutLocalFallback`. Inline at call site. - Delete: `shortcut-ui-deps-runtime-service.ts`, `shortcut-ui-deps-runtime-service.test.ts` ### Phase 1 verification ```bash pnpm run build && pnpm run test:core ``` **Expected result**: 16 files deleted, ~260 lines of service code removed, ~350 lines of test code for trivial wrappers removed. `index.ts` barrel export shrinks by ~10 entries. --- ## Phase 2: Consolidate DI Adapter Services (4 files) These are 50-130 line files that map one interface shape to another with minimal logic. They exist because the service they adapt has a different interface than what main.ts provides. The fix is to align the interfaces so the adapter isn't needed, or absorb the adapter into the service it feeds. ### 2.1 Merge `cli-command-deps-runtime-service.ts` into `cli-command-service.ts` The deps adapter (132 lines) maps main.ts state into `CliCommandServiceDeps`. Instead: make `handleCliCommandService` accept the same shape main.ts naturally provides, or accept a smaller interface with the actual values rather than getter/setter pairs. Move any null-guarding logic into the command handlers themselves. - Delete: `cli-command-deps-runtime-service.ts`, `cli-command-deps-runtime-service.test.ts` - Modify: `cli-command-service.ts` to accept deps directly ### 2.2 Merge `ipc-deps-runtime-service.ts` into `ipc-service.ts` Same pattern as 2.1. The deps adapter (100 lines) maps main.ts state for IPC handlers. Merge the defensive null checks and window guards into the IPC handlers that need them. - Delete: `ipc-deps-runtime-service.ts`, `ipc-deps-runtime-service.test.ts` - Modify: `ipc-service.ts` ### 2.3 Merge `tokenizer-deps-runtime-service.ts` into `tokenizer-service.ts` The adapter (45 lines) has one non-trivial function (`tokenizeWithMecab` with null checks and token merging). Move that logic into `tokenizer-service.ts`. - Delete: `tokenizer-deps-runtime-service.ts`, `tokenizer-deps-runtime-service.test.ts` - Modify: `tokenizer-service.ts` ### 2.4 Merge `app-lifecycle-deps-runtime-service.ts` into `app-lifecycle-service.ts` The adapter (57 lines) wraps Electron app events. Merge event binding into the lifecycle service itself since it already knows about Electron's app lifecycle. - Delete: `app-lifecycle-deps-runtime-service.ts`, `app-lifecycle-deps-runtime-service.test.ts` - Modify: `app-lifecycle-service.ts` ### Phase 2 verification ```bash pnpm run build && pnpm run test:core ``` **Expected result**: 8 more files deleted. DI adapters absorbed into the services they feed. `index.ts` shrinks further. --- ## Phase 3: Consolidate Related Services Merge services that are split across multiple files but represent a single concern. ### 3.1 Merge overlay visibility files `overlay-visibility-runtime-service.ts` (46 lines) is a thin orchestration layer over `overlay-visibility-service.ts` (183 lines). Merge into one file: `overlay-visibility-service.ts`. - Delete: `overlay-visibility-runtime-service.ts` - Modify: `overlay-visibility-service.ts` — absorb the 3 exported functions ### 3.2 Merge overlay broadcast files `overlay-broadcast-runtime-service.ts` (45 lines) contains utility functions for window filtering and broadcasting. These are closely related to `overlay-manager-service.ts` (49 lines) which manages the window references. Merge broadcast functions into the overlay manager since it already owns the window state. - Delete: `overlay-broadcast-runtime-service.ts`, `overlay-broadcast-runtime-service.test.ts` - Modify: `overlay-manager-service.ts` — add broadcast methods ### 3.3 Merge overlay shortcut files There are 4 shortcut-related files: - `overlay-shortcut-service.ts` (169 lines) — registration - `overlay-shortcut-runtime-service.ts` (105 lines) — runtime handlers - `overlay-shortcut-lifecycle-service.ts` (52 lines) — sync/refresh/unregister - `overlay-shortcut-fallback-runner.ts` (114 lines) — local fallback execution Consolidate into 2 files: - `overlay-shortcut-service.ts` — registration + lifecycle (absorb lifecycle-service) - `overlay-shortcut-handler.ts` — runtime handlers + fallback runner - Delete: `overlay-shortcut-lifecycle-service.ts`, `overlay-shortcut-fallback-runner.ts` ### 3.4 Merge numeric shortcut files `numeric-shortcut-runtime-service.ts` (37 lines) and `numeric-shortcut-session-service.ts` (99 lines) are two halves of one feature. Merge into `numeric-shortcut-service.ts`. - Delete: `numeric-shortcut-runtime-service.ts`, `numeric-shortcut-runtime-service.test.ts` - Modify: `numeric-shortcut-session-service.ts` → rename to `numeric-shortcut-service.ts` ### 3.5 Merge startup/bootstrap files `startup-bootstrap-runtime-service.ts` (53 lines) and `app-ready-runtime-service.ts` (77 lines) are both startup orchestration. Merge into a single `startup-service.ts`. - Delete: `startup-bootstrap-runtime-service.ts`, `startup-bootstrap-runtime-service.test.ts` - Modify: `app-ready-runtime-service.ts` → rename to `startup-service.ts`, absorb bootstrap ### Phase 3 verification ```bash pnpm run build && pnpm run test:core ``` **Expected result**: ~10 more files deleted. Related services consolidated into single cohesive modules. --- ## Phase 4: Fix Bugs and Code Quality ### 4.1 Remove debug `console.log` statements `overlay-visibility-service.ts` has 7+ raw `console.log`/`console.warn` calls used for debugging. Remove them or replace with the app logger if the messages have ongoing diagnostic value. ### 4.2 Fix `as never` type cast `tokenizer-deps-runtime-service.ts` (or its successor after Phase 2) uses `return mergeTokens(rawTokens as never)`. Investigate the actual type mismatch and fix it properly. ### 4.3 Guard `fieldGroupingResolver` against race conditions In main.ts, the `fieldGroupingResolver` is a single global variable. If two field grouping requests arrive concurrently, the second overwrites the first's resolver. Add a request ID or sequence number so stale resolutions are ignored. ### 4.4 Audit async callbacks in CLI command handlers Verify that async functions passed as callbacks in the CLI command and IPC handler wiring (main.ts lines ~697-707, ~1347, ~1360) are properly awaited or have `.catch()` handlers so rejections aren't silently swallowed. ### 4.5 Drop the `-runtime-service` naming convention After phases 1-3, rename remaining files to just `*-service.ts`. The "runtime" prefix adds no meaning. Do this as a batch rename commit with no logic changes. ### Phase 4 verification ```bash pnpm run build && pnpm run test:core ``` Manually smoke test: launch SubMiner, verify overlay renders, mine a card, toggle field grouping. --- ## Phase 5: Add Tests for Critical Untested Services These are the highest-risk untested modules. Add focused tests that verify real behavior, not just that mocks were called. ### 5.1 `mpv-service.ts` (761 lines, untested) Test the IPC protocol layer: - Socket message parsing (JSON line protocol) - Property change event dispatch - Request/response matching via request IDs - Reconnection behavior on socket close - Subtitle text extraction from property changes ### 5.2 `subsync-service.ts` (427 lines, untested) Test: - Config resolution (alass vs ffsubsync path selection) - Command construction for each sync engine - Timeout and error handling for child processes - Result parsing ### 5.3 `tokenizer-service.ts` (305 lines, untested) Test: - Yomitan parser initialization and readiness - Token extraction from parsed results - Fallback behavior when parser unavailable - Edge cases: empty text, CJK-only, mixed content ### 5.4 `cli-command-service.ts` (204 lines, partially tested) Expand existing tests to cover: - All CLI command dispatch paths - Error propagation from async handlers - Second-instance argument forwarding ### Phase 5 verification ```bash pnpm run test:core ``` All new tests pass. Aim for the 4 services above to each have 5-10 meaningful test cases. --- ## Phase 6 (Optional): Domain-Based Directory Structure After phases 1-5, the service count should be roughly 20-25 files down from 47. If that still feels too flat, group by domain: ``` src/core/ mpv/ mpv-service.ts mpv-render-metrics-service.ts overlay/ overlay-manager-service.ts overlay-visibility-service.ts overlay-window-service.ts overlay-shortcut-service.ts overlay-shortcut-handler.ts mining/ mining-runtime-service.ts field-grouping-service.ts field-grouping-overlay-service.ts startup/ startup-service.ts app-lifecycle-service.ts ipc/ ipc-service.ts ipc-command-service.ts shortcuts/ shortcut-service.ts numeric-shortcut-service.ts services/ tokenizer-service.ts subtitle-position-service.ts subtitle-ws-service.ts texthooker-service.ts yomitan-extension-loader-service.ts yomitan-settings-service.ts secondary-subtitle-service.ts runtime-config-service.ts runtime-options-runtime-service.ts cli-command-service.ts ``` Only do this if the flat directory still feels unwieldy after consolidation. This is cosmetic and low-priority relative to phases 1-5. --- ## Summary | Phase | Files Deleted | Files Modified | Risk | Effort | | -------------------------- | ------------------- | ----------------- | ------ | ------ | | 1. Delete thin wrappers | 16 (9 svc + 7 test) | main.ts, index.ts | Low | Small | | 2. Consolidate DI adapters | 8 (4 svc + 4 test) | 4 services | Medium | Medium | | 3. Merge related services | ~10 | ~5 services | Medium | Medium | | 4. Fix bugs & rename | 0 | ~6 files | Low | Small | | 5. Add critical tests | 0 | 4 new test files | Low | Medium | | 6. Directory restructure | 0 | All imports | Low | Small | **Net result**: ~34 files deleted, service count from 47 → ~22, index.ts from 92 exports → ~45, and the remaining services each have a clear reason to exist.