refactor runtime deps wiring and docs/config updates

This commit is contained in:
2026-02-10 02:44:35 -08:00
parent 6dd5f4e606
commit bb956d75bf
35 changed files with 372 additions and 1042 deletions

View File

@@ -28,10 +28,10 @@ pnpm run docs:build
- Full config file reference and option details
- [Development](/development)
- Contributor notes
- Architecture migration overview
- Architecture and extension rules
- Environment variables
- License and acknowledgments
- [Architecture](/architecture)
- Composability migration status
- Core runtime structure
- Service-oriented runtime structure
- Composition and lifecycle model
- Extension design rules

View File

@@ -1,51 +1,75 @@
# Architecture
SubMiner is migrating from a single, monolithic `src/main.ts` runtime toward a composable architecture with clear extension points.
SubMiner uses a service-oriented Electron main-process architecture where `src/main.ts` acts as the composition root and behavior lives in small runtime services under `src/core/services`.
## Goals
- Keep behavior stable while reducing coupling.
- Prefer small, single-purpose units that can be tested in isolation.
- Keep `main.ts` focused on wiring and state ownership, not implementation detail.
- Follow Unix-style composability:
- each service does one job
- services compose through explicit inputs/outputs
- orchestration is separate from implementation
## Current Structure
- `src/main.ts`: bootstrap/composition root plus remaining legacy orchestration.
- `src/core/`: shared runtime primitives:
- `app-orchestrator.ts`: lifecycle wiring for ready/activate/quit hooks.
- `action-bus.ts`: typed action dispatch path.
- `actions.ts`: canonical app action types.
- `module.ts` / `module-registry.ts`: module lifecycle contract.
- `services/`: extracted runtime services (IPC, shortcuts, subtitle, overlay, MPV command routing).
- `src/ipc/`: shared IPC contract + wrappers used by main, preload, and renderer.
- `src/subtitle/`: staged subtitle pipeline (`normalize` -> `tokenize` -> `merge`).
- `src/modules/`: feature modules:
- `runtime-options`
- `jimaku`
- `subsync`
- `anki`
- `texthooker`
- provider registries:
- `src/window-trackers/index.ts`
- `src/tokenizers/index.ts`
- `src/token-mergers/index.ts`
- `src/translators/index.ts`
- `src/subsync/engines.ts`
- `src/main.ts`
- Composition root for lifecycle wiring and non-overlay runtime state.
- Owns long-lived process state for trackers, runtime flags, and client instances.
- Delegates behavior to services.
- `src/core/services/overlay-manager-service.ts`
- Owns overlay/window state (`mainWindow`, `invisibleWindow`, visible/invisible overlay flags).
- Provides a narrow state API used by `main.ts` and overlay services.
- `src/core/services/*`
- Stateless or narrowly stateful units for a specific responsibility.
- Examples: startup bootstrap, app lifecycle hooks, CLI command handling, IPC registration, overlay visibility, MPV IPC behavior, shortcut registration, subtitle websocket, jimaku/subsync helpers.
- `src/core/utils/*`
- Pure helpers and coercion/config utilities.
- `src/cli/*`
- CLI parsing and help output.
- `src/config/*`
- Config schema/definitions, defaults, validation, and template generation.
- `src/window-trackers/*`
- Backend-specific tracker implementations plus selection index.
- `src/jimaku/*`, `src/subsync/*`
- Domain-specific integration helpers.
## Migration Status
## Composition Pattern
- Completed:
- Action bus wired for CLI, shortcuts, and IPC-triggered commands.
- Command/action mapping covered by focused core tests.
- Shared IPC channel contract adopted across main/preload/renderer.
- Runtime options extracted into module lifecycle.
- Provider registries replace hardcoded backend selection.
- Subtitle tokenization/merge/enrich flow moved to staged pipeline.
- Stage-level subtitle pipeline tests added for deterministic behavior.
- Jimaku, Subsync, Anki, and texthooker/websocket flows moduleized.
- In progress:
- Further shrink `src/main.ts` by moving orchestration into dedicated services/orchestrator files.
- Continue moduleizing remaining integrations with complex lifecycle coupling.
Most runtime code follows a dependency-injection pattern:
## Design Rules
1. Define a service interface in `src/core/services/*`.
2. Keep core logic in pure or side-effect-bounded functions.
3. Build runtime deps in `main.ts`; use `*-deps-runtime-service.ts` helpers only when they add real adaptation logic.
4. Call the service from lifecycle/command wiring points.
- New feature behavior should be added as:
- a module (`src/modules/*`) and/or
- a provider registration (`src/*/index.ts` registries),
instead of editing unrelated branches in `src/main.ts`.
- New command triggers should dispatch `AppAction` and reuse existing action handlers.
- New IPC channels should be added only via `src/ipc/contract.ts`.
This keeps side effects explicit and makes behavior easy to unit-test with fakes.
## Lifecycle Model
- Startup:
- `startup-bootstrap-runtime-service` handles initial argv/env/backend setup and decides generate-config flow vs app lifecycle start.
- `app-lifecycle-service` handles Electron single-instance + lifecycle event registration.
- `startup-lifecycle-hooks-runtime-service` wires app-ready and app-shutdown hooks.
- Runtime:
- CLI/shortcut/IPC events map to service calls.
- Overlay and MPV state sync through dedicated services.
- Runtime options and mining flows are coordinated via service boundaries.
- Shutdown:
- `app-shutdown-runtime-service` coordinates cleanup ordering (shortcuts, sockets, trackers, integrations).
## Why This Design
- Smaller blast radius: changing one feature usually touches one service.
- Better testability: most behavior can be tested without Electron windows/mpv.
- Better reviewability: PRs can be scoped to one subsystem.
- Backward compatibility: CLI flags and IPC channels can remain stable while internals evolve.
## Extension Rules
- Add behavior to an existing service or a new `src/core/services/*` file, not as ad-hoc logic in `main.ts`.
- Keep service APIs explicit and narrowly scoped.
- Prefer additive changes that preserve existing CLI flags and IPC channel behavior.
- Add/update unit tests for each service extraction or behavior change.
- For cross-cutting changes, extract-first then refactor internals after parity is verified.

View File

@@ -1,6 +1,6 @@
# Configuration
Settings are stored in `~/.config/SubMiner/config.jsonc`
Settings are stored in `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc` when `XDG_CONFIG_HOME` is unset). For backward compatibility, SubMiner also reads existing configs from lowercase `subminer` directories.
### Configuration File

View File

@@ -4,7 +4,12 @@ To add or change a config option, update `src/config/definitions.ts` first. Defa
## Architecture
The composability migration state and extension-point guidelines are documented in [`architecture.md`](/architecture).
The current runtime design, composition model, and extension guidelines are documented in [`architecture.md`](/architecture).
Contributor guidance:
- Overlay window/visibility state is owned by `src/core/services/overlay-manager-service.ts`.
- Prefer direct inline deps objects in `main.ts` for simple pass-through wiring.
- Add a `*-deps-runtime-service.ts` helper only when it performs meaningful adaptation (not identity mapping).
## Environment Variables

View File

@@ -34,10 +34,10 @@ features:
details: Build, test, and package SubMiner with the development notes in this docs set.
---
## Documentation Sections
<!-- ## Documentation Sections
- [Installation](/installation)
- [Usage](/usage)
- [Configuration](/configuration)
- [Development](/development)
- [Architecture](/architecture)
- [Architecture](/architecture) -->

View File

@@ -127,7 +127,7 @@ mpv --input-ipc-server=/tmp/subminer-socket video.mkv
**Config Location:**
Settings are stored in `~/.config/SubMiner/config.jsonc` (same as Linux).
Settings are stored in `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc`, same as Linux).
**MeCab Installation Paths:**

View File

@@ -2,7 +2,7 @@
* SubMiner Example Configuration File
*
* This file is auto-generated from src/config/definitions.ts.
* Copy to ~/.config/SubMiner/config.jsonc and edit as needed.
* Copy to $XDG_CONFIG_HOME/SubMiner/config.jsonc (or ~/.config/SubMiner/config.jsonc) and edit as needed.
*/
{

View File

@@ -7,8 +7,6 @@ There are two ways to use SubMiner:
| **subminer script** | All-in-one solution. Handles video selection, launches MPV with the correct socket, starts the overlay automatically, and cleans up on exit. |
| **MPV plugin** | When you launch MPV yourself or from other tools. Provides in-MPV chord keybindings (e.g. `y-y` for menu) to control visible and invisible overlay layers. Requires `--input-ipc-server=/tmp/subminer-socket`. |
Jimaku modal shortcut is an overlay shortcut, not an MPV plugin chord: default `Ctrl+Alt+J` via `shortcuts.openJimaku`.
You can use both together—install the plugin for on-demand control, but use `subminer` when you want the streamlined workflow.
`subminer` is implemented as a Bun script and runs directly via shebang (no `bun run` needed), for example: `subminer video.mkv`.
@@ -85,7 +83,7 @@ Notes:
- Secondary target languages come from `secondarySub.secondarySubLanguages` (defaults to English if unset).
- `subminer` prefers subtitle tracks from yt-dlp first, then falls back to local `whisper.cpp` (`whisper-cli`) when tracks are missing.
- Whisper translation fallback currently only supports English secondary targets; non-English secondary targets rely on yt-dlp subtitle availability.
- Configure defaults in `~/.config/SubMiner/config.jsonc` under `youtubeSubgen` and `secondarySub`, or override mode/tool paths via CLI flags/environment variables.
- Configure defaults in `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc`) under `youtubeSubgen` and `secondarySub`, or override mode/tool paths via CLI flags/environment variables.
## Keybindings
@@ -117,12 +115,6 @@ Notes:
These keybindings only work when the overlay window has focus. See [Configuration](/configuration) for customization.
### Overlay Chord Shortcuts
| Chord | Action |
| --------- | ------------------------- |
| `y``j` | Open Jimaku subtitle menu |
## How It Works
1. MPV runs with an IPC socket at `/tmp/subminer-socket`