* refactor(main): move the YouTube domain into an assembly createYoutubeAssembly owns the YouTube flow, playback, media cache, whisper subtitles, primary-subtitle notifications, and the in-app browser, plus the browser quit-on-close state. main.ts builds it once where the first YouTube runtime used to be and calls through the result. Siblings built later are passed as getters or callbacks; tests pin that construction calls none of them. * docs(architecture): describe per-domain assemblies * test(e2e): blank the line before mining the sub-delay cue The previous test leaves the same cue on screen, so the overlay check passed before the seek landed and --mine-sentence could read mpv's empty mid-seek sub-text and skip the card. * refactor(main): move the Jellyfin domain into an assembly createJellyfinAssembly owns the Jellyfin composer call, its playback and remote-progress state, the subtitle cache IO, mpv defaults, and the tray discovery deps. main.ts builds it where the composer call used to be and reads state through getActivePlayback/getQuitOnDisconnectArmed. * refactor(main): move the AniList domain into an assembly createAnilistAssembly owns the AniList setup and tracking composer calls, the setup window, the state runtime, and the token, media-guess, in-flight and attempted-update state they share. main.ts builds it where the setup composer call used to be and keeps the rate limiter, which the stats cover-art fetcher also uses. * refactor(main): move config hot-reload, settings, and sync UI into assemblies createSettingsAssembly owns config hot-reload and the settings window, which share the hot-reload-applied handler; main.ts still supplies that handler's cross-domain deps and the Yomitan deck-name helper. createSyncAssembly owns the sync window, sync IPC runtime, and auto-sync scheduler. Both are built where their blocks were; main.ts keeps the registerHandlers()/start() calls at the same points. * refactor(main): move dictionary runtimes into assemblies createDictionariesAssembly owns the JLPT and frequency lookups and their dictionary-root builders; main.ts still resolves the root paths at the same point. createCharacterDictionaryAssembly owns the character dictionary runtime, Yomitan auto-sync, and the portrait and name-candidate lookups. Both are built where their blocks were; later siblings (Yomitan profile policy, notifications, OSD, subtitle refresh) come in as getters or callbacks. * refactor(main): move overlay modal openers into an assembly createOverlayModalsAssembly owns the shared open-with-notification helper and the runtime options, Jimaku, TsukiHime, subtitle generation, session help, changelog, character dictionary manager, controller, and playlist browser openers. main.ts builds it where the openers used to live. * refactor(logger): export the Logger type for assembly deps The build emits declarations, and the Jellyfin assembly returns the logger in its tray discovery deps, so tsc needs a nameable Logger type. Assemblies now Pick from it directly.
8.6 KiB
Domain Ownership
Status: active Last verified: 2026-07-15 Owner: Kyle Yasuda Read when: you need to find the owner module for a behavior or test surface
Runtime Domains
- Desktop app runtime:
src/main.ts,src/main/,src/core/services/ - Overlay renderer:
src/renderer/ - Launcher CLI:
launcher/ - mpv plugin:
plugin/subminer/
Product / Integration Domains
- Config system:
src/config/; Anki resolution is composed bysrc/config/resolve/anki-connect.tsfrom focused resolvers insrc/config/resolve/anki-connect/Config hot-reload and the settings window:src/main/assemblies/settings.ts - Overlay/window state:
src/core/services/overlay-*,src/main/overlay-*.tsOverlay-hosted modal openers:src/main/assemblies/overlay-modals.ts - MPV runtime and protocol:
src/core/services/mpv*.tsWindows executable lookup and detached process creation are shared insrc/main/runtime/mpv-process.ts. The Windows launcher and Jellyfin handlers retain their own playback and connection workflows. - Subtitle/token pipeline:
src/core/services/subtitle-*.ts,src/core/services/tokenizer*,src/core/services/tokenizer/,src/subsync/JLPT and frequency annotation lookups:src/main/jlpt-runtime.ts,src/main/frequency-dictionary-runtime.ts, assembled bysrc/main/assemblies/dictionaries.ts - Anki workflow:
src/anki-integration/,src/core/services/anki-jimaku*.ts - Media timing review:
src/main/runtime/media-timing-review.tsowns the review and hiddenMediaTimingPreviewSession. The session waits for mpv readiness and acknowledges playback commands; itstime-posobservations reach the modal throughmedia-timing-review:preview-position. The renderer places the cursor from those timestamps and uses an inactivity timeout instead of timing the clip itself. - Immersion tracking:
src/core/services/immersion-tracker/Includes stats storage/query schema such asimm_videos,imm_media_art, andimm_youtube_videosfor per-video and YouTube-specific library metadata. Library-entry identity aliases and merge recommendations are persisted alongside this schema; the stats HTTP and SPA layers only expose and present those domain decisions.delete-maintenance-scheduler.tscoalesces and serializes stats deletes; the expensive work runs indelete-maintenance-worker-thread.tswhile the tracker queues playback writes. Each batch uses one transaction, lexical update, rollup refresh, and incremental lifetime subtraction (planLifetimeRemovals/applyLifetimeRemovalsinlifetime.ts). Merges, moves, AniList reassignments, andstats cleanup -luserepairLifetimeSummariesFromMedia(recompute from the per-video media ledger). The full lifetime rebuild survives only as the empty-table bootstrap; anywhere else it would collapse lifetime totals to the session retention window. - Immersion sync:
src/core/services/stats-sync/, bound bysrc/main/sync-cli.ts. The sync window and auto-sync scheduler live insrc/main/assemblies/sync.ts.snapshot-transfer.tsselects compressed rsync or scp.transfer-cache.tsatomically retains the last successfully received snapshot per hashed peer/database identity under the config directory'ssync-transfer-cache/. Cache copies seed isolated transfer directories; rsync verifies reconstructed files before the existing merge engine runs. The--make-temp/--remove-temphelpers accept an internal--transfer-cachekey, with a fallback for older peers that do not recognize it. - AniList tracking + character dictionary:
src/core/services/anilist/,src/main/runtime/composers/anilist-*(called bysrc/main/assemblies/anilist.ts),src/main/character-dictionary-runtime.ts,src/main/character-dictionary-runtime/, assembled bysrc/main/assemblies/character-dictionary.ts - TMDB live-action metadata:
src/core/services/tmdb/(client + exact-title resolver),src/core/services/immersion-tracker/live-action-link.ts(links an entry to a TMDB title and merges other holders of the same title). The AniList cover-art fetcher calls the resolver as its fallback;imm_anime.media_kindmarks the result and keeps the entry out of AniList season repair. - Jellyfin integration:
src/core/services/jellyfin*.ts,src/main/runtime/composers/jellyfin-*, assembled bysrc/main/assemblies/jellyfin.ts - YouTube playback, subtitles, media cache, and in-app browser:
src/core/services/youtube/,src/main/runtime/youtube-*, assembled bysrc/main/assemblies/youtube.ts - User plugins:
src/core/services/plugins/(host,ctx,exec),src/main/runtime/plugin-panel-runtime.ts(panel modal),src/main/runtime/plugin-window-runtime.ts(plugin windows),src/renderer/modals/plugin-panel*.ts; examples inassets/plugins/ - Window trackers:
src/window-trackers/ - Stats HTTP app:
src/core/services/stats-server.ts, with route groups and shared route support insrc/core/services/stats-server/ - Stats SPA:
stats/ - Public docs site:
docs-site/
Shared Contract Entry Points
Automatic mpv keyboard discovery uses the get-mpv-input-bindings IPC request and
MpvInputBindingsSnapshot in src/types/session-bindings.ts.
src/main/runtime/mpv-input-bindings.ts queries the connected player and preserves
configured keys, including disabled entries. src/shared/mpv-input-bindings.ts
validates discovered keys and translates browser input. The renderer's
handlers/mpv-input-forwarding.ts keeps the session lookup, coalesces asynchronous
refreshes, and releases held keys on blur or disposal. Discovered WHEEL_* bindings forward
as keypress <key> <notches> (Chromium reports 120 px per notch), which matches mpv's own
precise-scroll scaling. MBTN_* buttons forward as held keydown/keyup when mpv binds the
button or its _DBL variant, so mpv synthesizes double-clicks itself. Configured keybindings and
the built-in right-click pause run first. Forwarding is the only way pointer input reaches mpv on
Hyprland, where the overlay cannot be made click-through. handlers/keyboard.ts runs
this fallback after SubMiner controls and refreshes on startup, a delayed startup
pass, focus, binding reload, and the mpv-input-bindings:changed event. Main sends
that event from session-bindings-runtime.ts when mpv's discovered key set changes,
including the first discovery after connecting, because the overlay often loads
before mpv connects and the compiled session bindings may not change. Discovery does not enter compiled session bindings,
the plugin artifact, persistent config, or session help.
The subtitle sidebar consumes parsed cues through SubtitleSidebarSnapshot. Its sourceKey
identifies the media and subtitle source so renderer selections are invalidated on source changes,
including changes whose cue text and timings are identical. While open, the sidebar polls the
cheap SubtitleSidebarPlaybackState (cached time, current line, sourceKey, cuesRevision; no
mpv round-trips) every 80 ms for active-cue tracking. It fetches the full snapshot, which resolves
the source over mpv IPC and carries every cue plus config, only on open, config reload, a
sourceKey/cuesRevision change, and a ~2 s fallback. Native selection and clean clipboard
serialization live in src/renderer/modals/subtitle-sidebar-selection.ts. Electron lets standard
Copy input reach the renderer, where sidebar selection takes priority over the live-subtitle binding.
The preload bridge writes selections through Electron's clipboard API so copying does not depend
on Chromium document focus or require activating the overlay window.
- Config + app-state contracts:
src/types/config.ts - Subtitle/token/media annotation contracts:
src/types/subtitle.ts - Runtime/window/controller/Electron bridge contracts:
src/types/runtime.ts - Anki-specific contracts:
src/types/anki.ts - External integration contracts:
src/types/integrations.ts - Runtime-option contracts:
src/types/runtime-options.ts - Settings UI contracts:
src/types/settings.ts - Session-binding contracts:
src/types/session-bindings.ts - Stats HTTP wire contracts:
src/types/stats-wire.ts,src/types/stats-http-contract.ts - Compatibility-only barrel:
src/types.ts
Ownership Heuristics
- Runtime wiring or dependency setup: start in
src/main/ - Business logic or service behavior: start in
src/core/services/ - UI interaction or overlay DOM behavior: start in
src/renderer/ - Command parsing or mpv launch flow: start in
launcher/ - Shared contract changes: add or edit the narrowest
src/types/<domain>.tsentrypoint; only touchsrc/types.tsfor compatibility exports. - User-facing docs:
docs-site/ - Internal process/docs:
docs/