Files
SubMiner/docs/architecture.md
sudacode 781e6dd4fa docs: overhaul documentation and add four new pages
- Add mining-workflow.md: end-to-end sentence mining guide
- Add anki-integration.md: AnkiConnect setup, field mapping, media generation, field grouping
- Add mpv-plugin.md: chord keybindings, subminer.conf options, script messages
- Add troubleshooting.md: common issues and solutions by category
- Rewrite architecture.md to reflect current ~1,400-line main.ts and ~35 services
- Expand development.md from ~25 lines to full dev guide
- Fix URLs to ksyasuda/SubMiner, version to v0.1.0, AppImage naming
- Update VitePress sidebar with three-group layout (Getting Started, Reference, Development)
- Update navigation in index.md, README.md, docs/README.md
- Remove obsolete planning artifacts (plan.md, investigation.md, comparison.md, composability.md, refactor-main-checklist.md)
2026-02-11 09:33:47 -08:00

182 lines
7.7 KiB
Markdown

# Architecture
SubMiner uses a service-oriented Electron main-process architecture where `src/main.ts` (~1,400 lines) acts as the composition root and behavior lives in focused services under `src/core/services/` (~35 service files).
## 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
## Project Structure
```text
src/
main.ts # Composition root — lifecycle wiring and state ownership
preload.ts # Electron preload bridge
types.ts # Shared type definitions
core/
services/ # ~35 focused service modules (see below)
utils/ # Pure helpers and coercion/config utilities
cli/ # CLI parsing and help output
config/ # Config schema, defaults, validation, template generation
renderer/ # Overlay renderer (HTML/CSS/JS)
window-trackers/ # Backend-specific tracker implementations (Hyprland, X11, macOS)
jimaku/ # Jimaku API integration helpers
subsync/ # Subtitle sync (alass/ffsubsync) helpers
subtitle/ # Subtitle processing utilities
tokenizers/ # Tokenizer implementations
token-mergers/ # Token merge strategies
translators/ # AI translation providers
```
### Service Layer (`src/core/services/`)
- **Startup** — `startup-service`, `app-lifecycle-service`
- **Overlay** — `overlay-manager-service`, `overlay-window-service`, `overlay-visibility-service`, `overlay-bridge-service`, `overlay-runtime-init-service`
- **Shortcuts** — `shortcut-service`, `overlay-shortcut-service`, `overlay-shortcut-handler`, `shortcut-fallback-service`, `numeric-shortcut-service`
- **MPV** — `mpv-service`, `mpv-control-service`, `mpv-render-metrics-service`
- **IPC** — `ipc-service`, `ipc-command-service`, `runtime-options-ipc-service`
- **Mining** — `mining-service`, `field-grouping-service`, `field-grouping-overlay-service`, `anki-jimaku-service`, `anki-jimaku-ipc-service`
- **Subtitles** — `subtitle-ws-service`, `subtitle-position-service`, `secondary-subtitle-service`, `tokenizer-service`
- **Integrations** — `jimaku-service`, `subsync-service`, `subsync-runner-service`, `texthooker-service`, `yomitan-extension-loader-service`, `yomitan-settings-service`
- **Config** — `runtime-config-service`, `cli-command-service`
## Flow Diagram
```mermaid
flowchart TD
classDef root fill:#1f2937,stroke:#111827,color:#f9fafb,stroke-width:1.5px;
classDef orchestration fill:#334155,stroke:#0f172a,color:#e2e8f0;
classDef domain fill:#1d4ed8,stroke:#1e3a8a,color:#dbeafe;
classDef boundary fill:#065f46,stroke:#064e3b,color:#d1fae5;
subgraph Entry["Entrypoint"]
Main["src/main.ts\ncomposition root"]
end
class Main root;
subgraph Boot["Startup Orchestration"]
Startup["startup-service"]
Lifecycle["app-lifecycle-service"]
AppReady["app-ready flow"]
end
class Startup,Lifecycle,AppReady orchestration;
subgraph Runtime["Runtime Domains"]
OverlayMgr["overlay-manager-service"]
OverlayWindow["overlay-window-service"]
OverlayVisibility["overlay-visibility-service"]
Ipc["ipc-service\nipc-command-service"]
RuntimeOpts["runtime-options-ipc-service"]
Mpv["mpv-service\nmpv-control-service"]
Subtitle["subtitle-ws-service\nsecondary-subtitle-service"]
Shortcuts["shortcut-service\noverlay-shortcut-service"]
end
class OverlayMgr,OverlayWindow,OverlayVisibility,Ipc,RuntimeOpts,Mpv,Subtitle,Shortcuts domain;
subgraph Adapters["External Boundaries"]
Config["src/config/*"]
Cli["src/cli/*"]
Trackers["src/window-trackers/*"]
Integrations["src/jimaku/*\nsrc/subsync/*"]
end
class Config,Cli,Trackers,Integrations boundary;
Main -->|bootstraps| Startup
Main -->|registers lifecycle hooks| Lifecycle
Lifecycle -->|triggers| AppReady
Main -->|wires| OverlayMgr
Main -->|wires| Ipc
Main -->|wires| Mpv
Main -->|wires| Shortcuts
Main -->|wires| RuntimeOpts
Main -->|wires| Subtitle
Main -->|loads| Config
Main -->|parses| Cli
Main -->|delegates backend state| Trackers
Main -->|calls integrations| Integrations
OverlayMgr -->|creates window| OverlayWindow
OverlayMgr -->|applies visibility policy| OverlayVisibility
Ipc -->|updates| RuntimeOpts
Mpv -->|feeds timing + subtitle context| Subtitle
Shortcuts -->|drives overlay actions| OverlayMgr
```
## Composition Pattern
Most runtime code follows a dependency-injection pattern:
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`; extract an adapter/helper only when it adds meaningful behavior or reuse.
4. Call the service from lifecycle/command wiring points.
This keeps side effects explicit and makes behavior easy to unit-test with fakes.
## Lifecycle Model
- **Startup:**
- `startup-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.
- App-ready flow performs ready-time initialization (config load, websocket policy, tokenizer/tracker setup, overlay auto-init decisions).
- **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-lifecycle-service` registers cleanup hooks (`will-quit`) while teardown behavior stays delegated to focused services from `main.ts`.
```mermaid
flowchart TD
classDef phase fill:#334155,stroke:#0f172a,color:#e2e8f0;
classDef decision fill:#7c2d12,stroke:#431407,color:#ffedd5;
classDef runtime fill:#0369a1,stroke:#0c4a6e,color:#e0f2fe;
classDef shutdown fill:#14532d,stroke:#052e16,color:#dcfce7;
Args["CLI args / env"] --> Startup["startup-service"]
class Args,Startup phase;
Startup --> Decision{"generate-config?"}
class Decision decision;
Decision -->|yes| WriteConfig["write config + exit"]
Decision -->|no| Lifecycle["app-lifecycle-service"]
class WriteConfig,Lifecycle phase;
Lifecycle --> Ready["app-ready flow\n(config + websocket policy + tracker/tokenizer init)"]
class Ready phase;
Ready --> RuntimeBus["event loop:\nIPC + shortcuts + mpv events"]
RuntimeBus --> Overlay["overlay visibility + mining actions"]
RuntimeBus --> Subtitle["subtitle + secondary-subtitle processing"]
RuntimeBus --> Subsync["subsync / jimaku integration actions"]
class RuntimeBus,Overlay,Subtitle,Subsync runtime;
RuntimeBus --> WillQuit["Electron will-quit"]
WillQuit --> Cleanup["service-level teardown\n(unregister hooks, close resources)"]
class WillQuit,Cleanup shutdown;
```
## 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.