diff --git a/docs/architecture.md b/docs/architecture.md index 0f39e58..90084da 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -128,7 +128,7 @@ src/renderer/ The main process has three layers: `main.ts` delegates to composition modules that wire together domain services. Three overlay windows (visible, invisible, secondary) run in separate Electron renderer processes, connected through `preload.ts`. External runtimes (launcher CLI and mpv plugin) operate independently and communicate via IPC socket or CLI passthrough. ```mermaid -flowchart TD +flowchart LR classDef entry fill:#c6a0f6,stroke:#494d64,color:#24273a,stroke-width:2px,font-weight:bold classDef comp fill:#b7bdf8,stroke:#494d64,color:#24273a,stroke-width:1.5px classDef svc fill:#8aadf4,stroke:#494d64,color:#24273a,stroke-width:1.5px @@ -137,94 +137,70 @@ flowchart TD classDef ext fill:#a6da95,stroke:#494d64,color:#24273a,stroke-width:1.5px classDef extrt fill:#eed49f,stroke:#494d64,color:#24273a,stroke-width:1.5px - Main["main.ts — composition root"]:::entry - - subgraph Comp["Composition — src/main/"] - direction TB - Startup["Startup & Lifecycle
startup · app-lifecycle · startup-lifecycle · state"]:::comp - Wiring["Runtime Wiring
ipc-runtime · cli-runtime · overlay-runtime · subsync-runtime"]:::comp - Composers["Composers
mpv-runtime · anilist-tracking · jellyfin-runtime"]:::comp - end - - subgraph Svc["Services — src/core/services/"] - direction TB - subgraph SvcRow1[" "] - direction LR - Mpv["MPV Stack
transport · protocol
properties · render-metrics"]:::svc - Overlay["Overlay Manager
window · geometry
visibility · bridge"]:::svc - end - subgraph SvcRow2[" "] - direction LR - Mining["Mining & Subtitles
mining · field-grouping
subtitle-ws · tokenizer"]:::svc - Integrations["Integrations
jimaku · subsync · texthooker
yomitan · discord-presence"]:::svc - end - subgraph SvcRow3[" "] - direction LR - Tracking["Tracking
anilist · jellyfin-remote
immersion-tracker"]:::svc - Config["Config & Runtime
config-hot-reload
runtime-options"]:::svc - end - end - - Bridge(["preload.ts — Electron IPC bridge"]):::bridge - - subgraph Rend["Renderer — src/renderer/"] - direction TB - subgraph Windows["Three overlay windows"] - direction LR - Visible["Visible
interactive Yomitan lookups"]:::rend - Invisible["Invisible
mpv-matched positioning"]:::rend - Secondary["Secondary
secondary subtitle bar"]:::rend - end - UI["subtitle-render · positioning · handlers · modals"]:::rend + subgraph ExtRt["External Runtimes"] + Launcher["launcher/
CLI dispatch"]:::extrt + Plugin["subminer.lua
mpv plugin"]:::extrt end subgraph Ext["External Systems"] - direction LR mpvExt["mpv player"]:::ext AnkiExt["AnkiConnect"]:::ext JimakuExt["Jimaku API"]:::ext - TrackerExt["Window Tracker
Hyprland · Sway · X11 · macOS"]:::ext + TrackerExt["Window Tracker
Hyprland · Sway
X11 · macOS"]:::ext AnilistExt["AniList API"]:::ext JellyfinExt["Jellyfin"]:::ext DiscordExt["Discord RPC"]:::ext end - subgraph ExtRt["External Runtimes"] - direction LR - Launcher["launcher/
CLI command dispatch"]:::extrt - Plugin["subminer.lua
mpv plugin"]:::extrt + Main["main.ts
composition root"]:::entry + + subgraph Comp["Composition — src/main/"] + Startup["Startup & Lifecycle
startup · app-lifecycle
startup-lifecycle · state"]:::comp + Wiring["Runtime Wiring
ipc-runtime · cli-runtime
overlay-runtime"]:::comp + Composers["Composers
mpv · anilist
jellyfin"]:::comp end - Main -->|"delegates"| Comp - Startup -->|"initializes"| Svc - Wiring -->|"dispatches to"| Svc - Composers -->|"wires"| Svc + subgraph Svc["Services — src/core/services/"] + Mpv["MPV Stack
transport · protocol
properties · metrics"]:::svc + Overlay["Overlay Manager
window · geometry
visibility · bridge"]:::svc + Mining["Mining & Subtitles
mining · field-grouping
subtitle-ws · tokenizer"]:::svc + Integrations["Integrations
jimaku · subsync
texthooker · yomitan"]:::svc + Tracking["Tracking
anilist · jellyfin
immersion · discord"]:::svc + Config["Config & Runtime
hot-reload
runtime-options"]:::svc + end - Overlay <-->Bridge - Mining <--> Bridge - Bridge <--> Visible - Bridge <--> Invisible - Bridge <--> Secondary - Windows --> UI + Bridge(["preload.ts
Electron IPC"]):::bridge - Mpv <-->|"JSON IPC socket"| mpvExt - Mining -->|"HTTP"| AnkiExt - Integrations -->|"HTTP"| JimakuExt - Overlay -->|"platform API"| TrackerExt - Tracking -->|"HTTP"| AnilistExt - Tracking -->|"HTTP"| JellyfinExt - Integrations -->|"RPC"| DiscordExt + subgraph Rend["Renderer — src/renderer/"] + Visible["Visible window
Yomitan lookups"]:::rend + Invisible["Invisible window
mpv positioning"]:::rend + Secondary["Secondary window
subtitle bar"]:::rend + UI["subtitle-render
positioning
handlers · modals"]:::rend + end - Launcher -->|"CLI passthrough"| Main - Plugin -->|"IPC socket"| mpvExt + Launcher -->|"CLI"| Main + Plugin -->|"IPC"| mpvExt + + Main --> Comp + Comp --> Svc + + mpvExt <-->|"JSON socket"| Mpv + AnkiExt <-->|"HTTP"| Mining + JimakuExt <-->|"HTTP"| Integrations + TrackerExt <-->|"platform"| Overlay + AnilistExt <-->|"HTTP"| Tracking + JellyfinExt <-->|"HTTP"| Tracking + DiscordExt <-->|"RPC"| Integrations + + Overlay & Mining --> Bridge + Bridge --> Visible + Bridge --> Invisible + Bridge --> Secondary + Visible & Invisible & Secondary --> UI style Comp fill:#363a4f,stroke:#494d64,color:#cad3f5 style Svc fill:#363a4f,stroke:#494d64,color:#cad3f5 - style SvcRow1 fill:transparent,stroke:none - style SvcRow2 fill:transparent,stroke:none - style SvcRow3 fill:transparent,stroke:none style Rend fill:#363a4f,stroke:#494d64,color:#cad3f5 - style Windows fill:#1e2030,stroke:#494d64,color:#cad3f5 style Ext fill:#363a4f,stroke:#494d64,color:#cad3f5 style ExtRt fill:#363a4f,stroke:#494d64,color:#cad3f5 ``` diff --git a/flow-diagram-fullview.png b/flow-diagram-fullview.png new file mode 100644 index 0000000..6b608df Binary files /dev/null and b/flow-diagram-fullview.png differ diff --git a/flow-review-full.png b/flow-review-full.png new file mode 100644 index 0000000..70c2623 Binary files /dev/null and b/flow-review-full.png differ diff --git a/lifecycle-diagram-fullview.png b/lifecycle-diagram-fullview.png new file mode 100644 index 0000000..55c21a9 Binary files /dev/null and b/lifecycle-diagram-fullview.png differ diff --git a/plugin/subminer.lua b/plugin/subminer.lua index 2955518..d1a8649 100644 --- a/plugin/subminer.lua +++ b/plugin/subminer.lua @@ -1595,10 +1595,7 @@ local function start_overlay_from_script_message(...) end local function stop_overlay() - if not is_subminer_app_running() then - return - end - if not state.binary_available then + if not ensure_binary_available() then subminer_log("error", "binary", "SubMiner binary not found") show_osd("Error: binary not found") return @@ -1626,10 +1623,7 @@ local function stop_overlay() end local function toggle_overlay() - if not is_subminer_app_running() then - return - end - if not state.binary_available then + if not ensure_binary_available() then subminer_log("error", "binary", "SubMiner binary not found") show_osd("Error: binary not found") return @@ -1653,10 +1647,7 @@ local function toggle_overlay() end local function toggle_invisible_overlay() - if not is_subminer_app_running() then - return - end - if not state.binary_available then + if not ensure_binary_available() then subminer_log("error", "binary", "SubMiner binary not found") show_osd("Error: binary not found") return @@ -1684,10 +1675,7 @@ local function toggle_invisible_overlay() end local function show_invisible_overlay() - if not is_subminer_app_running() then - return - end - if not state.binary_available then + if not ensure_binary_available() then subminer_log("error", "binary", "SubMiner binary not found") show_osd("Error: binary not found") return @@ -1715,10 +1703,7 @@ local function show_invisible_overlay() end local function hide_invisible_overlay() - if not is_subminer_app_running() then - return - end - if not state.binary_available then + if not ensure_binary_available() then subminer_log("error", "binary", "SubMiner binary not found") show_osd("Error: binary not found") return @@ -1811,10 +1796,7 @@ local function show_menu() end restart_overlay = function() - if not is_subminer_app_running() then - return - end - if not state.binary_available then + if not ensure_binary_available() then subminer_log("error", "binary", "SubMiner binary not found") show_osd("Error: binary not found") return diff --git a/src/main.ts b/src/main.ts index 39a5e10..dbe3ef8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2926,11 +2926,30 @@ function handleMineSentenceDigit(count: number): void { handleMineSentenceDigitHandler(count); } +function ensureOverlayWindowsReadyForVisibilityActions(): void { + if (!appState.overlayRuntimeInitialized) { + initializeOverlayRuntime(); + return; + } + + const mainWindow = overlayManager.getMainWindow(); + if (!mainWindow || mainWindow.isDestroyed()) { + createMainWindow(); + } + + const invisibleWindow = overlayManager.getInvisibleWindow(); + if (!invisibleWindow || invisibleWindow.isDestroyed()) { + createInvisibleWindow(); + } +} + function setVisibleOverlayVisible(visible: boolean): void { + ensureOverlayWindowsReadyForVisibilityActions(); setVisibleOverlayVisibleHandler(visible); } function setInvisibleOverlayVisible(visible: boolean): void { + ensureOverlayWindowsReadyForVisibilityActions(); setInvisibleOverlayVisibleHandler(visible); if (visible) { subtitleProcessingController.refreshCurrentSubtitle(appState.currentSubText); @@ -2938,9 +2957,11 @@ function setInvisibleOverlayVisible(visible: boolean): void { } function toggleVisibleOverlay(): void { + ensureOverlayWindowsReadyForVisibilityActions(); toggleVisibleOverlayHandler(); } function toggleInvisibleOverlay(): void { + ensureOverlayWindowsReadyForVisibilityActions(); toggleInvisibleOverlayHandler(); } function setOverlayVisible(visible: boolean): void {