diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index ab01c93..86b2a6f 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -111,6 +111,27 @@ async function getMermaid() { mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', + theme: 'base', + themeVariables: { + background: '#24273a', + primaryColor: '#363a4f', + primaryTextColor: '#cad3f5', + primaryBorderColor: '#c6a0f6', + secondaryColor: '#494d64', + secondaryTextColor: '#cad3f5', + secondaryBorderColor: '#b7bdf8', + tertiaryColor: '#5b6078', + tertiaryTextColor: '#cad3f5', + tertiaryBorderColor: '#8aadf4', + lineColor: '#939ab7', + textColor: '#cad3f5', + mainBkg: '#363a4f', + nodeBorder: '#c6a0f6', + clusterBkg: '#1e2030', + clusterBorder: '#494d64', + edgeLabelBackground: '#24273a', + labelTextColor: '#cad3f5', + }, }); return mermaid; }); diff --git a/docs/architecture.md b/docs/architecture.md index a1ae55b..c20345f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -74,10 +74,10 @@ src/renderer/ ```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; + classDef root fill:#c6a0f6,stroke:#181926,color:#181926,stroke-width:2px; + classDef orchestration fill:#494d64,stroke:#b7bdf8,color:#cad3f5,stroke-width:2px; + classDef domain fill:#494d64,stroke:#8aadf4,color:#cad3f5,stroke-width:2px; + classDef boundary fill:#494d64,stroke:#a6da95,color:#cad3f5,stroke-width:2px; subgraph Entry["Entrypoint"] Main["src/main.ts\ncomposition root"] @@ -160,10 +160,10 @@ This keeps side effects explicit and makes behavior easy to unit-test with fakes ```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; + classDef phase fill:#494d64,stroke:#b7bdf8,color:#cad3f5,stroke-width:2px; + classDef decision fill:#494d64,stroke:#f5a97f,color:#cad3f5,stroke-width:2px; + classDef runtime fill:#494d64,stroke:#8aadf4,color:#cad3f5,stroke-width:2px; + classDef shutdown fill:#494d64,stroke:#a6da95,color:#cad3f5,stroke-width:2px; Args["CLI args / env"] --> Startup["startup-service"] class Args,Startup phase; diff --git a/docs/development.md b/docs/development.md index 559d25a..7684647 100644 --- a/docs/development.md +++ b/docs/development.md @@ -41,12 +41,22 @@ pnpm run dev # builds + launches with --start --dev flags ## Testing ```bash -pnpm run test:config # Config schema and validation tests -pnpm run test:core # Core service tests (~67 tests) -pnpm run test:subtitle # Subtitle pipeline tests +pnpm run test:config # Config schema and validation tests (build + run) +pnpm run test:core # Core service tests (~67 tests) (build + run) +pnpm run test:subtitle # Subtitle pipeline tests (build + run) ``` -All test commands build first, then run via Node's built-in test runner (`node --test`). +All legacy test commands build first, then run via Node's built-in test runner (`node --test`). + +For faster iteration while editing test code: + +```bash +pnpm run build # one-time compile +pnpm run test:config:dist # no rebuild +pnpm run test:core:dist # no rebuild +pnpm run test:subtitle:dist # no rebuild +pnpm run test:fast # run all tests without rebuild (assumes build is already current) +``` ## Config Generation @@ -95,6 +105,7 @@ Run `make help` for a full list of targets. Key ones: | Variable | Description | | --- | --- | | `SUBMINER_APPIMAGE_PATH` | Override AppImage location for subminer script | +| `SUBMINER_BINARY_PATH` | Alias for `SUBMINER_APPIMAGE_PATH` | | `SUBMINER_YT_SUBGEN_MODE` | Override `youtubeSubgen.mode` for launcher | | `SUBMINER_WHISPER_BIN` | Override `youtubeSubgen.whisperBin` for launcher | | `SUBMINER_WHISPER_MODEL` | Override `youtubeSubgen.whisperModel` for launcher | diff --git a/docs/structure-roadmap.md b/docs/structure-roadmap.md new file mode 100644 index 0000000..4249b22 --- /dev/null +++ b/docs/structure-roadmap.md @@ -0,0 +1,141 @@ +# Structure Roadmap for TASK-27 + +Date: 2026-02-14 + +## 1) Oversized refactor candidates (>=400 LOC) + +| File | Concern | Status | Reason | +| --- | --- | --- | --- | +| `src/main.ts` | Bootstrap / composition root / orchestration | Active | Main entrypoint owns startup, lifecycle orchestration, service construction, state mutation surfaces, and IPC bindings | +| `src/anki-integration.ts` | Domain service orchestration / integrations | Active | 2.6k+ LOC, high cyclomatic coupling to mpv/subtitle timing and mining flows | +| `src/core/services/mpv-service.ts` | MPV protocol + app state bridge | Active | ~780 LOC, large protocol and behavior mix, 22-entry dep interface | +| `src/core/services/subsync-service.ts` | Subsync orchestration (ffsubsync/alass workflows) | Active | ~494 LOC, file IO + mpv command orchestration + result shaping | +| `src/renderer/positioning.ts` | Renderer positioning layout policy | Active (downstream of TASK-27.5) | 513 LOC, layout/rules + platform-specific behavior in one module | +| `src/config/service.ts` | Config load/validation | Active support | ~601 LOC, central schema validation + mutation APIs | +| `src/types.ts` | Shared contract surface | Active support | ~640 LOC, foundational type exports driving module boundaries | +| `src/config/definitions.ts` | Config schema/registry definitions | Active support | ~480 LOC, dense constants/interfaces used by config runtime and docs | +| `src/media-generator.ts` | Media generation helpers | Active support | ~431 LOC, utility-heavy with multiple generation flows | + +## 2) API contracts by target file + +### `src/main.ts` (bootstrap + composition root) + +- **Entry points**: Electron main process boot path (executed by package `main` entry). +- **Contract responsibilities**: + - parse CLI/env and select startup flow through `runStartupBootstrapRuntimeService` + - register app-level lifecycle and command handlers (`runAppLifecycleService`, `handleCliCommandService`) + - instantiate core services (mpv, overlay runtime, IPC handlers, shortcuts, tokenizer, config services) + - hold mutable application runtime state: + - parser/Yomitan windows and extension state + - mpv client and tracker/state + - overlay/app bootstrap flags and modal/shortcut state + - runtime option and anki integration containers +- **Primary callers/integration points**: + - Electron event loop (`app.whenReady`, `process` signals) + - startup/bootstrap services, service adapters in `src/core/services` + +### `src/core/services/mpv-service.ts` (protocol + facade) + +- **Core exports**: `MpvIpcClient`, `MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY`, `MpvIpcClientDeps` +- **Primary responsibilities / API**: + - connection management (`connect`, `setSocketPath`, `request`, `send`, `requestProperty`) + - protocol message handling (`processBuffer`, private message handlers for `property-change` and request IDs) + - reconnection lifecycle (`scheduleReconnect`, `failPendingRequests`) + - mpv control actions (`setSubVisibility`, `replayCurrentSubtitle`, `playNextSubtitle`) + - state restoration (`restorePreviousSecondarySubVisibility`) +- **Current caller set**: + - `src/main.ts` (construction + lifecycle + service invocations) + - `src/core/services/mpv-control-service.ts` (runtime control API) + - `src/core/services/subsync-service.ts` (`requestProperty`, request ID usage) + - tests under `src/core/services/mpv-service.test.ts` +- **Observed coupling risk**: + - `MpvIpcClientDeps` mixes protocol config with app-level side effects (subtitle broadcast, tokenizer, overlay updates, config reads) + +### `src/anki-integration.ts` + +- **Class export**: `AnkiIntegration` +- **Primary public operations (validated from external callsites)**: + - lifecycle: `start`, `stop`, `destroy` + - card flow: `createSentenceCard`, `updateLastAddedFromClipboard`, `triggerFieldGroupingForLastAddedCard`, `markLastCardAsAudioCard` + - runtime patching: `applyRuntimeConfigPatch` +- **State dependencies (constructor)**: + - config, subtitle timing tracker, mpv client, OSD/notification callbacks +- **Primary callers**: + - `src/core/services/overlay-runtime-init-service.ts` (initial integration creation) + - `src/core/services/anki-jimaku-service.ts` (enable/disable and field-grouping RPC) + - `src/core/services/mining-service.ts` (delegates mining actions) + +### `src/core/services/subsync-service.ts` + +- **Exports**: `runSubsyncManualService`, `openSubsyncManualPickerService`, `triggerSubsyncFromConfigService` +- **Caller set**: + - `src/core/services/subsync-runner-service.ts` (runtime wrappers) + - `src/core/services/mpv-jimaku/`? (through runtime services and IPC command handlers) + +### `src/config/service.ts` + +- **Class export**: `ConfigService` +- **Primary API**: + - `reloadConfig`, `saveRawConfig`, `patchRawConfig` + - read methods: `getConfig`, `getRawConfig`, `getWarnings`, `getConfigPath` +- **Primary callers**: + - `src/main.ts`, `src/core/services/cli-command` and runtime config consumers + +### `src/renderer/positioning.ts` + +- **Public style/runtime contract** + - exported calculation helpers for subtitle Y-position and inset offsets + - event handlers for window geometry and subtitle-size changes +- **Primary callers**: + - `src/renderer/handlers/*` + - `src/renderer/subtitle-render.ts` + +## 3) Split sequence / dependency ordering + +Adopted sequence (from TASK-27 parent): + +1. `TASK-27.3` (Anki integration split) +2. `TASK-27.2` (main.ts composition-root split) — depends on TASK-7 and TASK-27.3 +3. `TASK-27.4` (mpv-service protocol/transport/property/facade split) — depends on TASK-27.1 and absorbs TASK-8 +4. `TASK-27.5` (renderer positioning split) — downstream + +## 4) Compatibility and migration risks per split + +### TASK-27.3 / anki integration surface +- Risk: interface breakage in field-grouping preview/build/enable flow +- Mitigation: keep constructor params and public methods stable; preserve IPC payload shapes + +### TASK-27.2 (composition root) +- Risk: lifecycle/cleanup regressions from moving startup hooks and shutdown behavior +- Mitigation: preserve service construction order and keep existing event registration boundaries + +### TASK-27.4 (mpv-service) +- Risk: request/deps interface changes could break control and subsync interactions +- Mitigation: preserve public `MpvClient` methods, request semantics, and reconnect events while splitting internal modules + +### TASK-27.4 expected event flow snapshot (current) +- `connect()` establishes socket and starts `observe_property` subscriptions via `subscribeToProperties()`. +- Protocol frames are dispatched through `processBuffer()` → `handleMessage()`. +- For `property-change` and request responses, handling remains in `MpvIpcClient` today but is a target for extraction: + - subtitle text/ASS/timing updates → `deps.setCurrentSubText`, `deps.setCurrentSubAssText`, subtitle timing tracker, overlay broadcast hooks + - media/path/title updates → `deps.updateCurrentMediaPath`, optional `deps.updateCurrentMediaTitle` + - property update commands (`sub-pos`, `sub-font*`, `sub-scale*`, etc.) → `deps.updateMpvSubtitleRenderMetrics` + - secondary-sub visibility tracking → `deps.setPreviousSecondarySubVisibility` + local `restorePreviousSecondarySubVisibility()` +- Reconnect path stays behavior-critical: + - socket close/error clears pending requests and calls `scheduleReconnect()` + - timer callback calls `connect()` after exponential-ish delay + +### TASK-27.5 (renderer positioning) +- Risk: UI placement drift on platform edge cases +- Mitigation: keep existing DOM state updates and geometry math in place while refactoring module boundaries + +## 5) Global smoke checklist (required for all TASK-27 subtasks) + +- App starts and connects to MPV +- Subtitle text appears in overlay +- Card mining creates a note in Anki +- Field grouping modal opens and resolves +- Global shortcuts work (mine, toggle overlay, copy subtitle) +- Secondary subtitle display works +- TypeScript compiles with no new errors +- Manual smoke checklist executed for runtime behavior