mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
fix(plugin): allow cold-start overlay launch without running process
This commit is contained in:
@@ -125,65 +125,108 @@ src/renderer/
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
The main process has three layers: `main.ts` delegates to composition modules that wire together domain services. The renderer runs in a separate Electron process, connected through `preload.ts`.
|
||||
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
|
||||
classDef entry fill:#c6a0f6,stroke:#363a4f,color:#24273a,stroke-width:2px
|
||||
classDef comp fill:#b7bdf8,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef svc fill:#8aadf4,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef bridge fill:#f5a97f,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef rend fill:#8bd5ca,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef ext fill:#a6da95,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
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
|
||||
classDef bridge fill:#f5a97f,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
classDef rend fill:#8bd5ca,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
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"]:::entry
|
||||
Main["main.ts — composition root"]:::entry
|
||||
|
||||
subgraph Comp["Composition — src/main/"]
|
||||
Startup["Startup & Lifecycle<br/>startup · app-lifecycle<br/>startup-lifecycle · state"]:::comp
|
||||
Wiring["Runtime Wiring<br/>ipc-runtime · cli-runtime<br/>overlay-runtime · subsync-runtime"]:::comp
|
||||
direction TB
|
||||
Startup["Startup & Lifecycle<br/>startup · app-lifecycle · startup-lifecycle · state"]:::comp
|
||||
Wiring["Runtime Wiring<br/>ipc-runtime · cli-runtime · overlay-runtime · subsync-runtime"]:::comp
|
||||
Composers["Composers<br/>mpv-runtime · anilist-tracking · jellyfin-runtime"]:::comp
|
||||
end
|
||||
|
||||
subgraph Svc["Services — src/core/services/"]
|
||||
direction LR
|
||||
Mpv["MPV Stack<br/>transport · protocol<br/>state · properties"]:::svc
|
||||
Overlay["Overlay<br/>manager · window<br/>visibility · bridge"]:::svc
|
||||
Mining["Mining & Subtitles<br/>mining · field-grouping<br/>subtitle-ws · tokenizer"]:::svc
|
||||
Integrations["Integrations<br/>jimaku · subsync<br/>texthooker · yomitan"]:::svc
|
||||
direction TB
|
||||
subgraph SvcRow1[" "]
|
||||
direction LR
|
||||
Mpv["MPV Stack<br/>transport · protocol<br/>properties · render-metrics"]:::svc
|
||||
Overlay["Overlay Manager<br/>window · geometry<br/>visibility · bridge"]:::svc
|
||||
end
|
||||
subgraph SvcRow2[" "]
|
||||
direction LR
|
||||
Mining["Mining & Subtitles<br/>mining · field-grouping<br/>subtitle-ws · tokenizer"]:::svc
|
||||
Integrations["Integrations<br/>jimaku · subsync · texthooker<br/>yomitan · discord-presence"]:::svc
|
||||
end
|
||||
subgraph SvcRow3[" "]
|
||||
direction LR
|
||||
Tracking["Tracking<br/>anilist · jellyfin-remote<br/>immersion-tracker"]:::svc
|
||||
Config["Config & Runtime<br/>config-hot-reload<br/>runtime-options"]:::svc
|
||||
end
|
||||
end
|
||||
|
||||
Bridge(["preload.ts — Electron IPC"]):::bridge
|
||||
Bridge(["preload.ts — Electron IPC bridge"]):::bridge
|
||||
|
||||
subgraph Rend["Renderer — src/renderer/"]
|
||||
Orchestration["renderer.ts<br/>orchestration · IPC wiring"]:::rend
|
||||
UI["subtitle-render · positioning<br/>handlers · modals"]:::rend
|
||||
direction TB
|
||||
subgraph Windows["Three overlay windows"]
|
||||
direction LR
|
||||
Visible["Visible<br/>interactive Yomitan lookups"]:::rend
|
||||
Invisible["Invisible<br/>mpv-matched positioning"]:::rend
|
||||
Secondary["Secondary<br/>secondary subtitle bar"]:::rend
|
||||
end
|
||||
UI["subtitle-render · positioning · handlers · modals"]:::rend
|
||||
end
|
||||
|
||||
subgraph Ext["External Systems"]
|
||||
direction LR
|
||||
mpv["mpv"]:::ext
|
||||
Anki["AnkiConnect"]:::ext
|
||||
Jimaku["Jimaku API"]:::ext
|
||||
Tracker["Window Tracker"]:::ext
|
||||
mpvExt["mpv player"]:::ext
|
||||
AnkiExt["AnkiConnect"]:::ext
|
||||
JimakuExt["Jimaku API"]:::ext
|
||||
TrackerExt["Window Tracker<br/>Hyprland · Sway · X11 · macOS"]:::ext
|
||||
AnilistExt["AniList API"]:::ext
|
||||
JellyfinExt["Jellyfin"]:::ext
|
||||
DiscordExt["Discord RPC"]:::ext
|
||||
end
|
||||
|
||||
Main -->|delegates| Comp
|
||||
Startup -->|initializes| Svc
|
||||
Wiring -->|dispatches to| Svc
|
||||
subgraph ExtRt["External Runtimes"]
|
||||
direction LR
|
||||
Launcher["launcher/<br/>CLI command dispatch"]:::extrt
|
||||
Plugin["subminer.lua<br/>mpv plugin"]:::extrt
|
||||
end
|
||||
|
||||
Overlay <--> Bridge
|
||||
Main -->|"delegates"| Comp
|
||||
Startup -->|"initializes"| Svc
|
||||
Wiring -->|"dispatches to"| Svc
|
||||
Composers -->|"wires"| Svc
|
||||
|
||||
Overlay <-->Bridge
|
||||
Mining <--> Bridge
|
||||
Bridge <--> Orchestration
|
||||
Orchestration --> UI
|
||||
Bridge <--> Visible
|
||||
Bridge <--> Invisible
|
||||
Bridge <--> Secondary
|
||||
Windows --> UI
|
||||
|
||||
Mpv <-->|JSON socket| mpv
|
||||
Mining -->|HTTP| Anki
|
||||
Integrations -->|HTTP| Jimaku
|
||||
Overlay --> Tracker
|
||||
Mpv <-->|"JSON IPC socket"| mpvExt
|
||||
Mining -->|"HTTP"| AnkiExt
|
||||
Integrations -->|"HTTP"| JimakuExt
|
||||
Overlay -->|"platform API"| TrackerExt
|
||||
Tracking -->|"HTTP"| AnilistExt
|
||||
Tracking -->|"HTTP"| JellyfinExt
|
||||
Integrations -->|"RPC"| DiscordExt
|
||||
|
||||
Launcher -->|"CLI passthrough"| Main
|
||||
Plugin -->|"IPC socket"| mpvExt
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## Composition Pattern
|
||||
@@ -242,52 +285,81 @@ For domains migrated to reducer-style transitions (for example AniList token/que
|
||||
|
||||
## Program Lifecycle
|
||||
|
||||
- **Startup:** `startup.ts` parses CLI args and detects the compositor backend. If `--generate-config` is passed, it writes the template and exits. Otherwise `app-lifecycle.ts` acquires the single-instance lock and registers Electron lifecycle hooks.
|
||||
- **Initialization:** Once `app.whenReady()` fires, `composeAppReadyRuntime()` runs the critical path first (strict config reload, runtime options + keybindings, mpv client creation, overlay/IPC setup). Non-critical warmups are launched asynchronously (`mecab`, `yomitan-extension`, dictionary prewarm, optional Jellyfin remote session).
|
||||
- **Runtime:** Event-driven. mpv events, IPC messages, CLI commands, overlay shortcuts, hot-reload notifications, and integration callbacks route through runtime handlers/composers, update `AppState`, and broadcast to overlay windows.
|
||||
- **Overlay window model:** runtime manages three overlay windows: `visible`, `invisible`, and `secondary`. `splitOverlayGeometryForSecondaryBar()` reserves the top 20% for the secondary subtitle bar and routes the remaining area to the active primary overlay layer.
|
||||
- **Shutdown:** `onWillQuitCleanup` tears down tray + watchers + integrations, stops subtitle/texthooker servers, flushes buffered MPV OSD log writes, closes token/session windows, and stops Jellyfin/Discord runtime services.
|
||||
- **Module-level init:** Before `app.ready`, the composition root registers protocols, sets platform flags, constructs all services, and wires dependency injection. `runAndApplyStartupState()` parses CLI args and detects the compositor backend.
|
||||
- **Startup:** If `--generate-config` is passed, it writes the template and exits. Otherwise `app-lifecycle.ts` acquires the single-instance lock and registers Electron lifecycle hooks.
|
||||
- **Critical-path init:** Once `app.whenReady()` fires, `composeAppReadyRuntime()` runs strict config reload, resolves keybindings, creates the `MpvIpcClient` (which immediately connects and subscribes to 26 properties), and initializes the `RuntimeOptionsManager`, `SubtitleTimingTracker`, and `ImmersionTrackerService`.
|
||||
- **Overlay runtime:** `initializeOverlayRuntime()` creates three overlay windows — **visible** (interactive Yomitan lookups), **invisible** (mpv-matched subtitle positioning), and **secondary** (secondary subtitle bar, top 20% via `splitOverlayGeometryForSecondaryBar`) — then registers global shortcuts and sets initial bounds from the window tracker.
|
||||
- **Background warmups:** Non-critical services are launched asynchronously: MeCab tokenizer check, Yomitan extension load, JLPT + frequency dictionary prewarm, optional Jellyfin remote session, Discord presence service, and AniList token refresh.
|
||||
- **Runtime:** Event-driven. mpv property changes, IPC messages, CLI commands, overlay shortcuts, and hot-reload notifications route through runtime handlers/composers. Subtitle text flows through `SubtitlePipeline` (normalize → tokenize → merge), and results broadcast to all overlay windows.
|
||||
- **Shutdown:** `onWillQuitCleanup` destroys tray + config watcher, unregisters shortcuts, stops WebSocket + texthooker servers, closes the mpv socket + flushes OSD log, stops the window tracker, closes the Yomitan parser window, flushes the immersion tracker (SQLite), stops Jellyfin/Discord services, and cleans Anki/AniList state.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
classDef start fill:#c6a0f6,stroke:#363a4f,color:#24273a,stroke-width:2px
|
||||
classDef phase fill:#b7bdf8,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef decision fill:#f5a97f,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef init fill:#8aadf4,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef runtime fill:#8bd5ca,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef shutdown fill:#ed8796,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
flowchart LR
|
||||
classDef start fill:#c6a0f6,stroke:#494d64,color:#24273a,stroke-width:2px,font-weight:bold
|
||||
classDef phase fill:#b7bdf8,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
classDef decision fill:#f5a97f,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
classDef init fill:#8aadf4,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
classDef runtime fill:#8bd5ca,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
classDef shutdown fill:#ed8796,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
classDef warmup fill:#eed49f,stroke:#494d64,color:#24273a,stroke-width:1.5px
|
||||
|
||||
CLI["CLI args & environment"]:::start
|
||||
CLI --> Parse["startup.ts<br/>Parse argv · detect backend · resolve config"]:::phase
|
||||
Parse --> GenCheck{"--generate-config?"}:::decision
|
||||
GenCheck -->|yes| GenExit["Write config template & exit"]:::phase
|
||||
GenCheck -->|no| Lifecycle["app-lifecycle.ts<br/>Acquire single-instance lock<br/>Register Electron lifecycle hooks"]:::phase
|
||||
Lifecycle -->|"app.whenReady()"| Ready["startup-lifecycle.ts"]:::phase
|
||||
CLI["CLI args &<br/>environment"]:::start
|
||||
CLI --> Proto["Module-level init<br/>register protocols<br/>construct services<br/>wire deps"]:::phase
|
||||
Proto --> Parse["startup.ts<br/>parse argv<br/>detect backend"]:::phase
|
||||
Parse --> GenCheck{"--generate<br/>-config?"}:::decision
|
||||
GenCheck -->|"yes"| GenExit["Write template<br/>& exit"]:::phase
|
||||
GenCheck -->|"no"| Lock["app-lifecycle.ts<br/>single-instance lock<br/>lifecycle hooks"]:::phase
|
||||
|
||||
Ready --> Init
|
||||
subgraph Init["Initialization"]
|
||||
direction LR
|
||||
Config["Load config<br/>resolve keybindings"]:::init
|
||||
Runtime["Create mpv client<br/>init runtime options"]:::init
|
||||
Platform["Start window tracker<br/>WebSocket policy"]:::init
|
||||
end
|
||||
Lock -->|"app.whenReady()"| Ready["composeAppReady<br/>Runtime()"]:::phase
|
||||
|
||||
Init --> Create["Create overlay window<br/>Establish IPC bridge"]:::phase
|
||||
Create --> Warm["Background warmups<br/>MeCab · Yomitan · dictionaries · Jellyfin"]:::phase
|
||||
Ready --> Config["Config reload<br/>keybindings<br/>log level"]:::init
|
||||
Ready --> MpvInit["MpvIpcClient<br/>connect socket<br/>subscribe 26 props"]:::init
|
||||
Ready --> Platform["RuntimeOptions<br/>timing tracker<br/>immersion tracker"]:::init
|
||||
|
||||
Config --> OverlayInit
|
||||
MpvInit --> OverlayInit
|
||||
Platform --> OverlayInit
|
||||
|
||||
OverlayInit["initializeOverlay<br/>Runtime()"]:::phase
|
||||
|
||||
OverlayInit --> VisWin["Visible window<br/>Yomitan lookups"]:::init
|
||||
OverlayInit --> InvWin["Invisible window<br/>mpv positioning"]:::init
|
||||
OverlayInit --> SecWin["Secondary window<br/>subtitle bar"]:::init
|
||||
OverlayInit --> Shortcuts["Register global<br/>shortcuts"]:::init
|
||||
|
||||
VisWin --> Warmups
|
||||
InvWin --> Warmups
|
||||
SecWin --> Warmups
|
||||
Shortcuts --> Warmups
|
||||
|
||||
Warmups["Background<br/>warmups"]:::phase
|
||||
|
||||
Warmups --> W1["MeCab"]:::warmup
|
||||
Warmups --> W2["Yomitan"]:::warmup
|
||||
Warmups --> W3["JLPT + freq<br/>dictionaries"]:::warmup
|
||||
Warmups --> W4["Jellyfin"]:::warmup
|
||||
Warmups --> W5["Discord"]:::warmup
|
||||
Warmups --> W6["AniList"]:::warmup
|
||||
|
||||
W1 & W2 & W3 & W4 & W5 & W6 --> Loop
|
||||
|
||||
Warm --> Loop
|
||||
subgraph Loop["Runtime — event-driven"]
|
||||
direction LR
|
||||
Events["mpv · IPC · CLI<br/>shortcut events"]:::runtime
|
||||
Dispatch["Route to service<br/>via composition layer"]:::runtime
|
||||
State["Update state<br/>broadcast to renderer"]:::runtime
|
||||
Events --> Dispatch --> State
|
||||
direction TB
|
||||
MpvEvt["mpv events: subtitle · timing · metrics"]:::runtime
|
||||
IpcEvt["IPC: renderer requests · CLI commands"]:::runtime
|
||||
ExtEvt["Shortcuts · config hot-reload"]:::runtime
|
||||
MpvEvt & IpcEvt & ExtEvt --> Route["Route via composers"]:::runtime
|
||||
Route --> Process["SubtitlePipeline<br/>normalize → tokenize → merge"]:::runtime
|
||||
Process --> Broadcast["Update AppState<br/>broadcast to windows"]:::runtime
|
||||
end
|
||||
|
||||
Loop -->|"app close"| Quit["Electron will-quit"]:::shutdown
|
||||
Quit --> Teardown["Close mpv socket · unregister shortcuts<br/>Stop WebSocket & texthooker<br/>Destroy tracker · clean Anki state"]:::shutdown
|
||||
Loop -->|"quit signal"| Quit["will-quit"]:::shutdown
|
||||
|
||||
Quit --> T1["Tray · config watcher<br/>global shortcuts"]:::shutdown
|
||||
Quit --> T2["WebSocket · texthooker<br/>mpv socket · OSD log"]:::shutdown
|
||||
Quit --> T3["Window tracker<br/>Yomitan parser"]:::shutdown
|
||||
Quit --> T4["Immersion tracker<br/>Jellyfin · Discord<br/>Anki · AniList"]:::shutdown
|
||||
|
||||
style Init fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style Loop fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
```
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copy to $XDG_CONFIG_HOME/SubMiner/config.jsonc (or ~/.config/SubMiner/config.jsonc) and edit as needed.
|
||||
*/
|
||||
{
|
||||
|
||||
// ==========================================
|
||||
// Overlay Auto-Start
|
||||
// When overlay connects to mpv, automatically show overlay and hide mpv subtitles.
|
||||
@@ -23,7 +24,7 @@
|
||||
// Control whether browser opens automatically for texthooker.
|
||||
// ==========================================
|
||||
"texthooker": {
|
||||
"openBrowser": true, // Open browser setting. Values: true | false
|
||||
"openBrowser": true // Open browser setting. Values: true | false
|
||||
}, // Control whether browser opens automatically for texthooker.
|
||||
|
||||
// ==========================================
|
||||
@@ -33,7 +34,7 @@
|
||||
// ==========================================
|
||||
"websocket": {
|
||||
"enabled": "auto", // Built-in subtitle websocket server mode. Values: auto | true | false
|
||||
"port": 6677, // Built-in subtitle websocket server port.
|
||||
"port": 6677 // Built-in subtitle websocket server port.
|
||||
}, // Built-in WebSocket server broadcasts subtitle text to connected clients.
|
||||
|
||||
// ==========================================
|
||||
@@ -42,7 +43,7 @@
|
||||
// Set to debug for full runtime diagnostics.
|
||||
// ==========================================
|
||||
"logging": {
|
||||
"level": "info", // Minimum log level for runtime logging. Values: debug | info | warn | error
|
||||
"level": "info" // Minimum log level for runtime logging. Values: debug | info | warn | error
|
||||
}, // Controls logging verbosity.
|
||||
|
||||
// ==========================================
|
||||
@@ -64,7 +65,7 @@
|
||||
"toggleSecondarySub": "CommandOrControl+Shift+V", // Toggle secondary sub setting.
|
||||
"markAudioCard": "CommandOrControl+Shift+A", // Mark audio card setting.
|
||||
"openRuntimeOptions": "CommandOrControl+Shift+O", // Open runtime options setting.
|
||||
"openJimaku": "Ctrl+Shift+J", // Open jimaku setting.
|
||||
"openJimaku": "Ctrl+Shift+J" // Open jimaku setting.
|
||||
}, // Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
||||
|
||||
// ==========================================
|
||||
@@ -74,7 +75,7 @@
|
||||
// This edit-mode shortcut is fixed and is not currently configurable.
|
||||
// ==========================================
|
||||
"invisibleOverlay": {
|
||||
"startupVisibility": "platform-default", // Startup visibility setting.
|
||||
"startupVisibility": "platform-default" // Startup visibility setting.
|
||||
}, // Startup behavior for the invisible interactive subtitle mining layer.
|
||||
|
||||
// ==========================================
|
||||
@@ -94,7 +95,7 @@
|
||||
"secondarySub": {
|
||||
"secondarySubLanguages": [], // Secondary sub languages setting.
|
||||
"autoLoadSecondarySub": false, // Auto load secondary sub setting. Values: true | false
|
||||
"defaultMode": "hover", // Default mode setting.
|
||||
"defaultMode": "hover" // Default mode setting.
|
||||
}, // Dual subtitle track options.
|
||||
|
||||
// ==========================================
|
||||
@@ -105,7 +106,7 @@
|
||||
"defaultMode": "auto", // Subsync default mode. Values: auto | manual
|
||||
"alass_path": "", // Alass path setting.
|
||||
"ffsubsync_path": "", // Ffsubsync path setting.
|
||||
"ffmpeg_path": "", // Ffmpeg path setting.
|
||||
"ffmpeg_path": "" // Ffmpeg path setting.
|
||||
}, // Subsync engine and executable paths.
|
||||
|
||||
// ==========================================
|
||||
@@ -113,7 +114,7 @@
|
||||
// Initial vertical subtitle position from the bottom.
|
||||
// ==========================================
|
||||
"subtitlePosition": {
|
||||
"yPercent": 10, // Y percent setting.
|
||||
"yPercent": 10 // Y percent setting.
|
||||
}, // Initial vertical subtitle position from the bottom.
|
||||
|
||||
// ==========================================
|
||||
@@ -138,7 +139,7 @@
|
||||
"N2": "#f5a97f", // N2 setting.
|
||||
"N3": "#f9e2af", // N3 setting.
|
||||
"N4": "#a6e3a1", // N4 setting.
|
||||
"N5": "#8aadf4", // N5 setting.
|
||||
"N5": "#8aadf4" // N5 setting.
|
||||
}, // Jlpt colors setting.
|
||||
"frequencyDictionary": {
|
||||
"enabled": false, // Enable frequency-dictionary-based highlighting based on token rank. Values: true | false
|
||||
@@ -146,7 +147,13 @@
|
||||
"topX": 1000, // Only color tokens with frequency rank <= topX (default: 1000).
|
||||
"mode": "single", // single: use one color for all matching tokens. banded: use color ramp by frequency band. Values: single | banded
|
||||
"singleColor": "#f5a97f", // Color used when frequencyDictionary.mode is `single`.
|
||||
"bandedColors": ["#ed8796", "#f5a97f", "#f9e2af", "#a6e3a1", "#8aadf4"], // Five colors used for rank bands when mode is `banded` (from most common to least within topX).
|
||||
"bandedColors": [
|
||||
"#ed8796",
|
||||
"#f5a97f",
|
||||
"#f9e2af",
|
||||
"#a6e3a1",
|
||||
"#8aadf4"
|
||||
] // Five colors used for rank bands when mode is `banded` (from most common to least within topX).
|
||||
}, // Frequency dictionary setting.
|
||||
"secondary": {
|
||||
"fontSize": 24, // Font size setting.
|
||||
@@ -154,8 +161,8 @@
|
||||
"backgroundColor": "transparent", // Background color setting.
|
||||
"fontWeight": "normal", // Font weight setting.
|
||||
"fontStyle": "normal", // Font style setting.
|
||||
"fontFamily": "M PLUS 1, Noto Sans CJK JP Regular, Noto Sans CJK JP, Hiragino Sans, Hiragino Kaku Gothic ProN, Yu Gothic, Arial Unicode MS, Arial, sans-serif", // Font family setting.
|
||||
}, // Secondary setting.
|
||||
"fontFamily": "M PLUS 1, Noto Sans CJK JP Regular, Noto Sans CJK JP, Hiragino Sans, Hiragino Kaku Gothic ProN, Yu Gothic, Arial Unicode MS, Arial, sans-serif" // Font family setting.
|
||||
} // Secondary setting.
|
||||
}, // Primary and secondary subtitle styling.
|
||||
|
||||
// ==========================================
|
||||
@@ -168,13 +175,15 @@
|
||||
"enabled": false, // Enable AnkiConnect integration. Values: true | false
|
||||
"url": "http://127.0.0.1:8765", // Url setting.
|
||||
"pollingRate": 3000, // Polling interval in milliseconds.
|
||||
"tags": ["SubMiner"], // Tags to add to cards mined or updated by SubMiner. Provide an empty array to disable automatic tagging.
|
||||
"tags": [
|
||||
"SubMiner"
|
||||
], // Tags to add to cards mined or updated by SubMiner. Provide an empty array to disable automatic tagging.
|
||||
"fields": {
|
||||
"audio": "ExpressionAudio", // Audio setting.
|
||||
"image": "Picture", // Image setting.
|
||||
"sentence": "Sentence", // Sentence setting.
|
||||
"miscInfo": "MiscInfo", // Misc info setting.
|
||||
"translation": "SelectionText", // Translation setting.
|
||||
"translation": "SelectionText" // Translation setting.
|
||||
}, // Fields setting.
|
||||
"ai": {
|
||||
"enabled": false, // Enabled setting. Values: true | false
|
||||
@@ -183,7 +192,7 @@
|
||||
"model": "openai/gpt-4o-mini", // Model setting.
|
||||
"baseUrl": "https://openrouter.ai/api", // Base url setting.
|
||||
"targetLanguage": "English", // Target language setting.
|
||||
"systemPrompt": "You are a translation engine. Return only the translated text with no explanations.", // System prompt setting.
|
||||
"systemPrompt": "You are a translation engine. Return only the translated text with no explanations." // System prompt setting.
|
||||
}, // Ai setting.
|
||||
"media": {
|
||||
"generateAudio": true, // Generate audio setting. Values: true | false
|
||||
@@ -196,7 +205,7 @@
|
||||
"animatedCrf": 35, // Animated crf setting.
|
||||
"audioPadding": 0.5, // Audio padding setting.
|
||||
"fallbackDuration": 3, // Fallback duration setting.
|
||||
"maxMediaDuration": 30, // Max media duration setting.
|
||||
"maxMediaDuration": 30 // Max media duration setting.
|
||||
}, // Media setting.
|
||||
"behavior": {
|
||||
"overwriteAudio": true, // Overwrite audio setting. Values: true | false
|
||||
@@ -204,7 +213,7 @@
|
||||
"mediaInsertMode": "append", // Media insert mode setting.
|
||||
"highlightWord": true, // Highlight word setting. Values: true | false
|
||||
"notificationType": "osd", // Notification type setting.
|
||||
"autoUpdateNewCards": true, // Automatically update newly added cards. Values: true | false
|
||||
"autoUpdateNewCards": true // Automatically update newly added cards. Values: true | false
|
||||
}, // Behavior setting.
|
||||
"nPlusOne": {
|
||||
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
||||
@@ -213,20 +222,20 @@
|
||||
"decks": [], // Decks used for N+1 known-word cache scope. Supports one or more deck names.
|
||||
"minSentenceWords": 3, // Minimum sentence word count required for N+1 targeting (default: 3).
|
||||
"nPlusOne": "#c6a0f6", // Color used for the single N+1 target token highlight.
|
||||
"knownWord": "#a6da95", // Color used for legacy known-word highlights.
|
||||
"knownWord": "#a6da95" // Color used for legacy known-word highlights.
|
||||
}, // N plus one setting.
|
||||
"metadata": {
|
||||
"pattern": "[SubMiner] %f (%t)", // Pattern setting.
|
||||
"pattern": "[SubMiner] %f (%t)" // Pattern setting.
|
||||
}, // Metadata setting.
|
||||
"isLapis": {
|
||||
"enabled": false, // Enabled setting. Values: true | false
|
||||
"sentenceCardModel": "Japanese sentences", // Sentence card model setting.
|
||||
"sentenceCardModel": "Japanese sentences" // Sentence card model setting.
|
||||
}, // Is lapis setting.
|
||||
"isKiku": {
|
||||
"enabled": false, // Enabled setting. Values: true | false
|
||||
"fieldGrouping": "disabled", // Kiku duplicate-card field grouping mode. Values: auto | manual | disabled
|
||||
"deleteDuplicateInAuto": true, // Delete duplicate in auto setting. Values: true | false
|
||||
}, // Is kiku setting.
|
||||
"deleteDuplicateInAuto": true // Delete duplicate in auto setting. Values: true | false
|
||||
} // Is kiku setting.
|
||||
}, // Automatic Anki updates and media generation options.
|
||||
|
||||
// ==========================================
|
||||
@@ -236,7 +245,7 @@
|
||||
"jimaku": {
|
||||
"apiBaseUrl": "https://jimaku.cc", // Api base url setting.
|
||||
"languagePreference": "ja", // Preferred language used in Jimaku search. Values: ja | en | none
|
||||
"maxEntryResults": 10, // Maximum Jimaku search results returned.
|
||||
"maxEntryResults": 10 // Maximum Jimaku search results returned.
|
||||
}, // Jimaku API configuration and defaults.
|
||||
|
||||
// ==========================================
|
||||
@@ -247,7 +256,10 @@
|
||||
"mode": "automatic", // YouTube subtitle generation mode for the launcher script. Values: automatic | preprocess | off
|
||||
"whisperBin": "", // Path to whisper.cpp CLI used as fallback transcription engine.
|
||||
"whisperModel": "", // Path to whisper model used for fallback transcription.
|
||||
"primarySubLanguages": ["ja", "jpn"], // Comma-separated primary subtitle language priority used by the launcher.
|
||||
"primarySubLanguages": [
|
||||
"ja",
|
||||
"jpn"
|
||||
] // Comma-separated primary subtitle language priority used by the launcher.
|
||||
}, // Defaults for subminer YouTube subtitle extraction/transcription mode.
|
||||
|
||||
// ==========================================
|
||||
@@ -256,7 +268,7 @@
|
||||
// ==========================================
|
||||
"anilist": {
|
||||
"enabled": false, // Enable AniList post-watch progress updates. Values: true | false
|
||||
"accessToken": "", // Optional explicit AniList access token override; leave empty to use locally stored token from setup.
|
||||
"accessToken": "" // Optional explicit AniList access token override; leave empty to use locally stored token from setup.
|
||||
}, // Anilist API credentials and update behavior.
|
||||
|
||||
// ==========================================
|
||||
@@ -280,8 +292,16 @@
|
||||
"pullPictures": false, // Enable Jellyfin poster/icon fetching for launcher menus. Values: true | false
|
||||
"iconCacheDir": "/tmp/subminer-jellyfin-icons", // Directory used by launcher for cached Jellyfin poster icons.
|
||||
"directPlayPreferred": true, // Try direct play before server-managed transcoding when possible. Values: true | false
|
||||
"directPlayContainers": ["mkv", "mp4", "webm", "mov", "flac", "mp3", "aac"], // Container allowlist for direct play decisions.
|
||||
"transcodeVideoCodec": "h264", // Preferred transcode video codec when direct play is unavailable.
|
||||
"directPlayContainers": [
|
||||
"mkv",
|
||||
"mp4",
|
||||
"webm",
|
||||
"mov",
|
||||
"flac",
|
||||
"mp3",
|
||||
"aac"
|
||||
], // Container allowlist for direct play decisions.
|
||||
"transcodeVideoCodec": "h264" // Preferred transcode video codec when direct play is unavailable.
|
||||
}, // Optional Jellyfin integration for auth, browsing, and playback launch.
|
||||
|
||||
// ==========================================
|
||||
@@ -292,7 +312,7 @@
|
||||
"discordPresence": {
|
||||
"enabled": false, // Enable optional Discord Rich Presence updates. Values: true | false
|
||||
"updateIntervalMs": 3000, // Minimum interval between presence payload updates.
|
||||
"debounceMs": 750, // Debounce delay used to collapse bursty presence updates.
|
||||
"debounceMs": 750 // Debounce delay used to collapse bursty presence updates.
|
||||
}, // Optional Discord Rich Presence activity card updates for current playback/study session.
|
||||
|
||||
// ==========================================
|
||||
@@ -314,7 +334,7 @@
|
||||
"telemetryDays": 30, // Telemetry retention window in days.
|
||||
"dailyRollupsDays": 365, // Daily rollup retention window in days.
|
||||
"monthlyRollupsDays": 1825, // Monthly rollup retention window in days.
|
||||
"vacuumIntervalDays": 7, // Minimum days between VACUUM runs.
|
||||
}, // Retention setting.
|
||||
}, // Enable/disable immersion tracking.
|
||||
"vacuumIntervalDays": 7 // Minimum days between VACUUM runs.
|
||||
} // Retention setting.
|
||||
} // Enable/disable immersion tracking.
|
||||
}
|
||||
|
||||
@@ -95,4 +95,6 @@ Read first. Keep concise.
|
||||
| `codex-docs-video-thumb-cache-20260223T033929Z-k8p2` | `codex-docs-video-thumb-cache` | `Fix docs landing page demo video thumbnail staleness after direct asset replacement.` | `handoff` | `docs/subagents/agents/codex-docs-video-thumb-cache-20260223T033929Z-k8p2.md` | `2026-02-23T03:44:04Z` |
|
||||
| `codex-development-docs-review-20260223T034520Z-2ebb` | `codex-development-docs-review` | `Review codebase and refresh docs/development.md to match current project state.` | `done` | `docs/subagents/agents/codex-development-docs-review-20260223T034520Z-2ebb.md` | `2026-02-23T03:49:16Z` |
|
||||
| `opencode-bun-migration-20260223T043000Z-k9m2` | `opencode-bun-migration` | `Execute TASK-115 Bun-only migration plan: parity map, dist/utility script migration, CI/docs cutover.` | `handoff` | `docs/subagents/agents/opencode-bun-migration-20260223T043000Z-k9m2.md` | `2026-02-23T04:36:00Z` |
|
||||
| `opencode-initial-release-plan-20260223T044059Z-p7k2` | `opencode-initial-release-plan` | `Analyze main history and draft copy/paste initial-release history-cleanup plan.` | `planning` | `docs/subagents/agents/opencode-initial-release-plan-20260223T044059Z-p7k2.md` | `2026-02-23T04:40:59Z` |
|
||||
| `opencode-initial-release-plan-20260223T044059Z-p7k2` | `opencode-initial-release-plan` | `Analyze main history and draft copy/paste initial-release history-cleanup plan.` | `handoff` | `docs/subagents/agents/opencode-initial-release-plan-20260223T044059Z-p7k2.md` | `2026-02-23T04:47:30Z` |
|
||||
| `codex-plugin-start-overlay-gate-20260223T044347Z-k3n8` | `codex-plugin-start-overlay-gate` | `Fix mpv plugin regression where start is refused if SubMiner process is not already running (TASK-117).` | `handoff` | `docs/subagents/agents/codex-plugin-start-overlay-gate-20260223T044347Z-k3n8.md` | `2026-02-23T04:47:40Z` |
|
||||
| `codex-commit-changes-20260223T050731Z-rer4` | `codex-commit-changes` | `Commit current working tree changes with a conventional commit message derived from content.` | `in_progress` | `docs/subagents/agents/codex-commit-changes-20260223T050731Z-rer4.md` | `2026-02-23T05:07:31Z` |
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Agent: `codex-commit-changes-20260223T050731Z-rer4`
|
||||
|
||||
- alias: `codex-commit-changes`
|
||||
- mission: `Commit current working tree changes requested by user with a content-derived conventional commit message.`
|
||||
- status: `in_progress`
|
||||
- branch: `main`
|
||||
- started_at: `2026-02-23T05:07:31Z`
|
||||
- heartbeat_minutes: `5`
|
||||
|
||||
## Current Work (newest first)
|
||||
- [2026-02-23T05:07:31Z] intent: inspect diff scope, preserve existing user/agent edits, stage all current changes, and commit once with conventional format.
|
||||
|
||||
## Files Touched
|
||||
- `docs/subagents/INDEX.md`
|
||||
- `docs/subagents/collaboration.md`
|
||||
- `docs/subagents/agents/codex-commit-changes-20260223T050731Z-rer4.md`
|
||||
|
||||
## Assumptions
|
||||
- User request "commit changes" means commit all current working-tree changes.
|
||||
- No additional code edits are required beyond required coordination bookkeeping.
|
||||
|
||||
## Open Questions / Blockers
|
||||
- None.
|
||||
|
||||
## Next Step
|
||||
- Stage all changes and create one conventional commit message based on the diff.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Agent: `codex-plugin-start-overlay-gate-20260223T044347Z-k3n8`
|
||||
|
||||
- alias: `codex-plugin-start-overlay-gate`
|
||||
- mission: `Fix mpv plugin regression where start is refused if SubMiner process is not already running (TASK-117).`
|
||||
- status: `handoff`
|
||||
- branch: `main`
|
||||
- started_at: `2026-02-23T04:49:00Z`
|
||||
- heartbeat_minutes: `5`
|
||||
|
||||
## Current Work (newest first)
|
||||
- [2026-02-23T04:47:40Z] completed: patched cold-start gate in `plugin/subminer.lua`, added Lua regression harness, validated red->green + syntax + focused launcher mpv tests.
|
||||
- [2026-02-23T04:49:00Z] intent: reproduce root cause from user logs; add red test first; patch plugin start gate with minimal behavior change.
|
||||
|
||||
## Files Touched
|
||||
- `plugin/subminer.lua`
|
||||
- `scripts/test-plugin-start-gate.lua`
|
||||
- `backlog/tasks/task-117 - Fix-mpv-plugin-overlay-start-gate-when-SubMiner-is-not-running.md`
|
||||
- `docs/subagents/INDEX.md`
|
||||
- `docs/subagents/collaboration.md`
|
||||
- `docs/subagents/agents/codex-plugin-start-overlay-gate-20260223T044347Z-k3n8.md`
|
||||
|
||||
## Assumptions
|
||||
- Regression introduced by `is_subminer_ipc_ready()` check at start of `start_overlay()`.
|
||||
- Cold-start should not require pre-existing process; socket checks remain valid once process exists.
|
||||
|
||||
## Open Questions / Blockers
|
||||
- None.
|
||||
|
||||
## Next Step
|
||||
- User runtime verification: launch with `SUBMINER_APPIMAGE_PATH=... ./dist/launcher/subminer --log-level debug ...` and trigger `y-s`.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Agent: `opencode-initial-release-plan-20260223T044059Z-p7k2`
|
||||
|
||||
- alias: `opencode-initial-release-plan`
|
||||
- mission: `Analyze main history and draft copy/paste initial-release history-cleanup plan.`
|
||||
- status: `handoff`
|
||||
- branch: `main`
|
||||
- started_at: `2026-02-23T04:40:59Z`
|
||||
- heartbeat_minutes: `5`
|
||||
|
||||
## Current Work (newest first)
|
||||
|
||||
- [2026-02-23T04:47:30Z] handoff: added `initial-release.md` with orphan-branch + curated-commit cutover commands, backup refs, validation checks, force-with-lease push, and team resync instructions.
|
||||
- [2026-02-23T04:44:30Z] progress: analyzed `main` history (325 commits; high refactor/noise skew) and confirmed release-shaped repo layout for snapshot regrouping.
|
||||
- [2026-02-23T04:40:59Z] intent: inspect commit history and current repository state, then write `initial-release.md` with exact cutover commands.
|
||||
|
||||
## Files Touched
|
||||
|
||||
- `docs/subagents/agents/opencode-initial-release-plan-20260223T044059Z-p7k2.md`
|
||||
- `docs/subagents/INDEX.md`
|
||||
- `docs/subagents/collaboration.md`
|
||||
- `initial-release.md`
|
||||
- `backlog/tasks/task-116 - Analyze-git-history-and-draft-initial-release-cleanup-plan.md`
|
||||
|
||||
## Assumptions
|
||||
|
||||
- history rewrite is allowed because repo is private and pre-release.
|
||||
- recommendation should prefer low-risk deterministic workflow over complex interactive rebase.
|
||||
|
||||
## Open Questions / Blockers
|
||||
|
||||
- none.
|
||||
|
||||
## Next Step
|
||||
|
||||
- share recommendation + path to `initial-release.md`; execute cutover during freeze window.
|
||||
@@ -148,6 +148,8 @@ Shared notes. Append-only.
|
||||
|
||||
## 2026-02-23
|
||||
|
||||
- [2026-02-23T04:49:00Z] [codex-plugin-start-overlay-gate-20260223T044347Z-k3n8|codex-plugin-start-overlay-gate] overlap note: touching `plugin/subminer.lua` start gate path for cold-start regression (`Refusing to start overlay: SubMiner process not running`); adding TASK-117 + focused regression harness.
|
||||
- [2026-02-23T04:47:40Z] [codex-plugin-start-overlay-gate-20260223T044347Z-k3n8|codex-plugin-start-overlay-gate] completed TASK-117: `start_overlay()` now allows cold-start when process absent; added `scripts/test-plugin-start-gate.lua` red/green regression harness; verified with `luac -p plugin/subminer.lua` and `bun test launcher/mpv.test.ts`.
|
||||
- [2026-02-23T01:10:27Z] [opencode-task109-discord-presence-20260223T011027Z-j9r4|opencode-task109-discord-presence] starting TASK-109 closure pass via Backlog MCP + writing-plans/executing-plans; scope validate existing Discord config/runtime/docs changes, close remaining DoD evidence, and finalize task status if gates pass.
|
||||
- [2026-02-23T01:15:39Z] [opencode-task109-discord-presence-20260223T011027Z-j9r4|opencode-task109-discord-presence] user feedback from real Discord session: status resumed to Playing with noticeable delay; tuned default `discordPresence.updateIntervalMs` from 15000 to 3000 in defaults/docs/examples and updated focused config expectations; reran focused config + discord presence tests green.
|
||||
- [2026-02-23T01:27:55Z] [codex-task88-yomitan-flow-20260223T012755Z-x4m2|codex-task88-yomitan-flow] starting TASK-88 via Backlog MCP + writing-plans/executing-plans; expected overlap in tokenizer modules (`src/core/services/tokenizer*`, Yomitan flow wiring/tests); will keep scope to MeCab fallback removal and token flow simplification.
|
||||
@@ -169,3 +171,5 @@ Shared notes. Append-only.
|
||||
- [2026-02-23T04:30:00Z] [opencode-bun-migration-20260223T043000Z-k9m2|opencode-bun-migration] starting TASK-115 Bun-only migration execution; initial scope `package.json`, CI/release workflows, and setup docs to remove Node requirements after parity checks.
|
||||
- [2026-02-23T04:36:00Z] [opencode-bun-migration-20260223T043000Z-k9m2|opencode-bun-migration] completed TASK-115 Bun-only migration pass: dist/utility commands moved off direct Node invocation, CI/release Node setup removed, Bun parity matrix + docs updates landed, full Bun validation gate suite passed, and TASK-115 + child tasks finalized Done in Backlog.
|
||||
- [2026-02-23T04:40:59Z] [opencode-initial-release-plan-20260223T044059Z-p7k2|opencode-initial-release-plan] starting user-requested release-history cleanup planning pass; scope git-history analysis + current-state review + `initial-release.md` command playbook.
|
||||
- [2026-02-23T04:47:30Z] [opencode-initial-release-plan-20260223T044059Z-p7k2|opencode-initial-release-plan] completed planning pass: analyzed commit history/current state and added `initial-release.md` with recommended orphan-history + 7-commit split plan, validation, cutover, and teammate resync commands.
|
||||
- [2026-02-23T05:07:31Z] [codex-commit-changes-20260223T050731Z-rer4|codex-commit-changes] starting user-requested commit pass for current working tree; scope is stage all existing tracked/untracked changes and create conventional commit without additional behavior edits.
|
||||
|
||||
Reference in New Issue
Block a user