From a80d6dbea9101b221eea9068b1b9ed674af7cdf5 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 28 Feb 2026 21:15:26 -0800 Subject: [PATCH] update docs/backlog --- backlog/config.yml | 10 +- ...d-bind-visible-overlay-to-mpv-subtitles.md | 5 + ...ransport-for-push-based-auto-enrichment.md | 5 + ...w-full-warning-details-in-native-dialog.md | 5 + ...es-and-optimize-startup-command-runtime.md | 7 + ...ble-warmup-vs-defer-with-low-power-mode.md | 12 +- ...usions-for-N1-and-frequency-annotations.md | 9 +- ...fig-surface-and-keep-hardcoded-defaults.md | 9 +- docs/anki-integration.md | 6 +- docs/configuration.md | 127 +++++++++--------- docs/immersion-tracking.md | 1 - docs/index.assets.test.ts | 20 ++- docs/launcher-script.md | 53 ++++---- docs/mining-workflow.md | 10 +- docs/mpv-plugin.md | 60 ++++----- ...-02-26-secondary-subtitles-main-overlay.md | 5 + docs/public/config.example.jsonc | 82 +++++------ docs/shortcuts.md | 26 ++-- docs/usage.md | 14 +- 19 files changed, 255 insertions(+), 211 deletions(-) diff --git a/backlog/config.yml b/backlog/config.yml index cf450d5..f0233bb 100644 --- a/backlog/config.yml +++ b/backlog/config.yml @@ -1,11 +1,11 @@ -project_name: "SubMiner" -default_status: "To Do" -statuses: ["To Do", "In Progress", "Done"] +project_name: 'SubMiner' +default_status: 'To Do' +statuses: ['To Do', 'In Progress', 'Done'] labels: [] definition_of_done: [] date_format: yyyy-mm-dd max_column_width: 20 -default_editor: "nvim" +default_editor: 'nvim' auto_open_browser: false default_port: 6420 remote_operations: true @@ -13,4 +13,4 @@ auto_commit: false bypass_git_hooks: false check_active_branches: true active_branch_days: 30 -task_prefix: "task" +task_prefix: 'task' diff --git a/backlog/tasks/task-70 - Overlay-runtime-refactor-remove-invisible-mode-and-bind-visible-overlay-to-mpv-subtitles.md b/backlog/tasks/task-70 - Overlay-runtime-refactor-remove-invisible-mode-and-bind-visible-overlay-to-mpv-subtitles.md index c01de78..a271788 100644 --- a/backlog/tasks/task-70 - Overlay-runtime-refactor-remove-invisible-mode-and-bind-visible-overlay-to-mpv-subtitles.md +++ b/backlog/tasks/task-70 - Overlay-runtime-refactor-remove-invisible-mode-and-bind-visible-overlay-to-mpv-subtitles.md @@ -26,15 +26,18 @@ ordinal: 1000 ## Description + Scope: Branch-only commits main..HEAD on refactor-overlay (a14c9da through 9e4e588) rebuilt overlay behavior around visible overlay mode and removed legacy invisible overlay paths. Delivered behavior: + - Removed renderer invisible overlay layout/offset helpers and main hover-highlight runtime code paths. - Added explicit overlay-to-mpv subtitle visibility synchronization so visible overlay state controls primary subtitle visibility consistently. - Hardened overlay runtime/bootstrap lifecycle around modal fallback open state and bridge send path edge cases. - Updated plugin/config/docs defaults to reflect visible-overlay-first behavior and subtitle binding controls. Risk/impact context: + - Large cross-layer refactor touching runtime wiring, renderer event handling, and plugin behavior. - Regression coverage added/updated for overlay runtime, mpv protocol handling, renderer cleanup, and subtitle rendering paths. @@ -42,5 +45,7 @@ Risk/impact context: ## Final Summary + Completed and validated in branch commit set before merge. Refactor reduces dead overlay modes, centralizes subtitle visibility behavior, and documents new defaults/constraints. + diff --git a/backlog/tasks/task-71 - Anki-integration-add-local-AnkiConnect-proxy-transport-for-push-based-auto-enrichment.md b/backlog/tasks/task-71 - Anki-integration-add-local-AnkiConnect-proxy-transport-for-push-based-auto-enrichment.md index 9f76b9b..cc4b2eb 100644 --- a/backlog/tasks/task-71 - Anki-integration-add-local-AnkiConnect-proxy-transport-for-push-based-auto-enrichment.md +++ b/backlog/tasks/task-71 - Anki-integration-add-local-AnkiConnect-proxy-transport-for-push-based-auto-enrichment.md @@ -25,9 +25,11 @@ ordinal: 2000 ## Description + Scope: Current unmerged working-tree changes implement an optional local AnkiConnect-compatible proxy and transport switching for card enrichment. Delivered behavior: + - Added proxy server that forwards AnkiConnect requests and enqueues addNote/addNotes note IDs for post-create enrichment, with de-duplication and loop-configuration protection. - Added follow-up response-shape compatibility handling so proxy enqueue works for both envelope (`{result,error}`) and bare JSON payloads, including `multi` variants. - Added config schema/defaults/resolution for ankiConnect.proxy (enabled, host, port, upstreamUrl) with validation warnings and fallback behavior. @@ -36,6 +38,7 @@ Delivered behavior: - Updated user docs/config examples for proxy mode setup, troubleshooting, and mining workflow behavior. Risk/impact context: + - New network surface on local host/port; correctness depends on safe proxy upstream configuration and robust response handling. - Tests added for proxy queue behavior, config resolution, and parser sync routines. @@ -43,5 +46,7 @@ Risk/impact context: ## Final Summary + Completed implementation in branch working tree; ready to merge once local changes are committed and test gate passes. + diff --git a/backlog/tasks/task-72 - macOS-config-validation-UX-show-full-warning-details-in-native-dialog.md b/backlog/tasks/task-72 - macOS-config-validation-UX-show-full-warning-details-in-native-dialog.md index 533b7bb..731ee52 100644 --- a/backlog/tasks/task-72 - macOS-config-validation-UX-show-full-warning-details-in-native-dialog.md +++ b/backlog/tasks/task-72 - macOS-config-validation-UX-show-full-warning-details-in-native-dialog.md @@ -19,19 +19,24 @@ ordinal: 3000 ## Description + Scope: Commit cc2f9ef improves startup config-warning visibility on macOS by ensuring full details are surfaced in the native UI path and reflected in docs. Delivered behavior: + - Config validation/runtime wiring updated so macOS users can access complete warning details instead of truncated notification-only text. - Added/updated tests around config validation and startup config warning flows. - Updated configuration docs to clarify platform-specific warning presentation behavior. Risk/impact context: + - Low runtime risk; primarily user-facing diagnostics clarity improvement. ## Final Summary + Completed small follow-up fix to reduce config-debug friction on macOS. + diff --git a/backlog/tasks/task-73 - MPV-plugin-split-into-modules-and-optimize-startup-command-runtime.md b/backlog/tasks/task-73 - MPV-plugin-split-into-modules-and-optimize-startup-command-runtime.md index 54cf8ad..922d1bc 100644 --- a/backlog/tasks/task-73 - MPV-plugin-split-into-modules-and-optimize-startup-command-runtime.md +++ b/backlog/tasks/task-73 - MPV-plugin-split-into-modules-and-optimize-startup-command-runtime.md @@ -39,9 +39,11 @@ ordinal: 4000 ## Description + Scope: Replace monolithic `plugin/subminer.lua` with modular plugin runtime; optimize command execution paths; align install/docs/tests; fix launcher smoke instability. Delivered behavior: + - Full plugin cutover to `plugin/subminer/main.lua` + module directory (no runtime compatibility shim with old monolith file). - Process/control command path moved toward async subprocess usage for non-start actions (`stop`, `toggle`, `settings`, restart stop leg), reducing synchronous blocking in mpv script runtime. - AniSkip path guarded: lookup runs only in SubMiner context (launcher metadata, explicit script-message refresh, or detected running app), instead of every opened file. @@ -53,6 +55,7 @@ Delivered behavior: - Playback command cleanup race fixed when mpv exits before exit-listener registration. Risk/impact context: + - mpv plugin loading path changed from single-file to module directory; packaging/install paths must stay consistent with release assets. - Async control/AniSkip path changes reduce blocking but can surface timing differences; regression checks added for cold start, file-load gating, and explicit refresh behavior. @@ -60,18 +63,22 @@ Risk/impact context: ## Final Summary + AniSkip gate/async update delivered in plugin runtime: + - `plugin/subminer/lifecycle.lua`: deferred AniSkip fetch and overlay-start trigger. - `plugin/subminer/aniskip.lua`: async lookup pipeline + context guard + session caches. - `plugin/subminer/environment.lua`: async app-running detection with short cache. - `plugin/subminer/messages.lua`: explicit script-message trigger wiring. Regression coverage updated: + - `scripts/test-plugin-start-gate.lua` now verifies: - no sync `ps`/`curl` on non-context file load - no AniSkip network lookup on non-context file load - script-message refresh forces async AniSkip lookup Validation run: + - `bun run test:plugin:src` pass. diff --git a/backlog/tasks/task-74 - Startup-warmups-configurable-warmup-vs-defer-with-low-power-mode.md b/backlog/tasks/task-74 - Startup-warmups-configurable-warmup-vs-defer-with-low-power-mode.md index 88a9443..2220a9f 100644 --- a/backlog/tasks/task-74 - Startup-warmups-configurable-warmup-vs-defer-with-low-power-mode.md +++ b/backlog/tasks/task-74 - Startup-warmups-configurable-warmup-vs-defer-with-low-power-mode.md @@ -1,9 +1,10 @@ --- id: TASK-74 title: 'Startup warmups: configurable warmup vs defer with low-power mode' -status: In Progress +status: Done assignee: [] created_date: '2026-02-27 21:05' +updated_date: '2026-03-01 04:14' labels: [] dependencies: [] references: @@ -22,14 +23,17 @@ references: - src/main/runtime/startup-warmups-main-deps.test.ts - src/core/services/app-ready.test.ts priority: medium +ordinal: 7000 --- ## Description + Add startup warmup controls to allow per-integration warmup or deferred first-use loading. Scope: + - New config section `startupWarmups` with toggles for `mecab`, `yomitanExtension`, `subtitleDictionaries`, and `jellyfinRemoteSession`. - New `startupWarmups.lowPowerMode` policy: defer everything except Yomitan extension. - Keep default behavior as full warmup. @@ -40,7 +44,9 @@ Scope: ## Final Summary + Implemented: + - Added `startupWarmups` to config types/defaults/options/template/resolve. - Warmup scheduler now uses per-integration gating functions. - Low-power mode now defers MeCab, subtitle dictionaries, and Jellyfin remote session warmups while still warming Yomitan extension. @@ -48,11 +54,13 @@ Implemented: - Added/updated tests across config and runtime warmup modules. Validation: + - `bun run test:config:src` - `bun run test:core:src` - `tsc --noEmit` Follow-up updates: + - Startup now triggers warmups earlier in app-ready flow (right after config validation/log-level setup) instead of waiting for initial args/overlay actions. Goal: tokenization warmup is already done or mostly done by first visible-subs toggle. - Tokenization warmup scheduling consolidated as `subtitle-tokenization` stage; when enabled by toggles, it runs Yomitan extension first, then MeCab/dictionary warmups. - Added per-stage debug logs for warmup progress and skip reasons: @@ -65,4 +73,4 @@ Follow-up updates: - `src/main/runtime/startup-warmups.test.ts` - `src/main/runtime/startup-warmups-main-deps.test.ts` - `src/core/services/app-ready.test.ts` - + diff --git a/backlog/tasks/task-75 - Tokenizer-configurable-POS-exclusions-for-N1-and-frequency-annotations.md b/backlog/tasks/task-75 - Tokenizer-configurable-POS-exclusions-for-N1-and-frequency-annotations.md index 3d0afe6..1b6eede 100644 --- a/backlog/tasks/task-75 - Tokenizer-configurable-POS-exclusions-for-N1-and-frequency-annotations.md +++ b/backlog/tasks/task-75 - Tokenizer-configurable-POS-exclusions-for-N1-and-frequency-annotations.md @@ -4,24 +4,29 @@ title: 'Tokenizer: configurable POS exclusions for N+1 and frequency annotations status: Done assignee: [] created_date: '2026-03-01 01:23' -updated_date: '2026-03-01 01:32' +updated_date: '2026-03-01 04:14' labels: [] dependencies: [] priority: medium +ordinal: 6000 --- ## Description + N+1 and frequency highlighting should ignore non-learning tokens (e.g., particles/auxiliary forms) based on MeCab POS1 tags, while remaining user-configurable. Problem example: for subtitle phrase containing になれば, the highlighted N+1 target should not be the non-useful inflection/token piece when POS indicates an excluded class. Implement configurable exclusion defaults with add/remove overrides so users can tune behavior without code changes. + ## Acceptance Criteria + + - [x] #1 Default exclusion set omits non-useful POS1 classes from both N+1 candidate selection and frequency highlighting. - [x] #2 Users can add extra POS1 exclusions and remove defaults via config. - [x] #3 Tokenizer/annotation tests cover default behavior and config add/remove overrides. @@ -30,5 +35,7 @@ Implement configurable exclusion defaults with add/remove overrides so users can ## Final Summary + Implemented configurable annotation POS exclusions with defaults+add/remove for both MeCab POS1 and POS2, wired to N+1 candidate selection and frequency highlighting. Added POS2 default exclusion (非自立), expanded POS1 defaults for function words, added Yomitan->MeCab enrichment to carry pos2/pos3 metadata, updated config docs/examples, and added regression tests including になれば case. + diff --git a/backlog/tasks/task-76 - Tokenizer-remove-POS-exclusion-config-surface-and-keep-hardcoded-defaults.md b/backlog/tasks/task-76 - Tokenizer-remove-POS-exclusion-config-surface-and-keep-hardcoded-defaults.md index c6a389d..ff345dc 100644 --- a/backlog/tasks/task-76 - Tokenizer-remove-POS-exclusion-config-surface-and-keep-hardcoded-defaults.md +++ b/backlog/tasks/task-76 - Tokenizer-remove-POS-exclusion-config-surface-and-keep-hardcoded-defaults.md @@ -4,22 +4,27 @@ title: 'Tokenizer: remove POS exclusion config surface and keep hardcoded defaul status: Done assignee: [] created_date: '2026-03-01 02:45' -updated_date: '2026-03-01 02:51' +updated_date: '2026-03-01 04:14' labels: [] dependencies: [] priority: medium +ordinal: 5000 --- ## Description + Remove user-facing config keys for annotation POS exclusions. Keep N+1/frequency POS exclusion behavior as built-in defaults with no config required. Scope: remove config parsing/registry/docs/example for annotationFilters.pos1Exclusions/pos2Exclusions while preserving runtime filtering behavior. + ## Acceptance Criteria + + - [x] #1 No user-facing config option exists for annotation POS exclusions. - [x] #2 Runtime N+1/frequency exclusion behavior remains active via built-in defaults. - [x] #3 Config/docs/example/tests updated accordingly. @@ -28,5 +33,7 @@ Scope: remove config parsing/registry/docs/example for annotationFilters.pos1Exc ## Final Summary + Removed user-facing subtitleStyle.annotationFilters POS exclusion configuration (schema/resolver/options/docs/example). POS-based N+1/frequency filtering now always uses built-in defaults in runtime. Preserved robust exclusion behavior including merged-token overlap POS handling and N+1-only MeCab enrichment path. + diff --git a/docs/anki-integration.md b/docs/anki-integration.md index 65c307b..bcc7302 100644 --- a/docs/anki-integration.md +++ b/docs/anki-integration.md @@ -273,8 +273,8 @@ When you mine the same word multiple times, SubMiner can merge the cards instead ### What Gets Merged -| Field | Merge behavior | -| -------- | -------------------------------------------------------------- | +| Field | Merge behavior | +| -------- | --------------------------------------------------------------- | | Sentence | Both sentences preserved (exact duplicate text is deduplicated) | | Audio | Both `[sound:...]` entries kept (exact duplicates deduplicated) | | Image | Both images kept (exact duplicates deduplicated) | @@ -299,7 +299,7 @@ When you mine the same word multiple times, SubMiner can merge the cards instead "enabled": false, "host": "127.0.0.1", "port": 8766, - "upstreamUrl": "http://127.0.0.1:8765" + "upstreamUrl": "http://127.0.0.1:8765", }, "fields": { "audio": "ExpressionAudio", diff --git a/docs/configuration.md b/docs/configuration.md index 0466e71..0615c4b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -172,11 +172,11 @@ This example is intentionally compact. The option table below documents availabl | --------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | `true`, `false` | Enable AnkiConnect integration (default: `false`) | | `url` | string (URL) | AnkiConnect API URL (default: `http://127.0.0.1:8765`) | -| `pollingRate` | number (ms) | How often to check for new cards in polling mode (default: `3000`; ignored for direct proxy `addNote`/`addNotes` updates) | -| `proxy.enabled` | `true`, `false` | Enable local AnkiConnect-compatible proxy for push-based auto-enrichment (default: `true`) | +| `pollingRate` | number (ms) | How often to check for new cards in polling mode (default: `3000`; ignored for direct proxy `addNote`/`addNotes` updates) | +| `proxy.enabled` | `true`, `false` | Enable local AnkiConnect-compatible proxy for push-based auto-enrichment (default: `true`) | | `proxy.host` | string | Bind host for local AnkiConnect proxy (default: `127.0.0.1`) | | `proxy.port` | number | Bind port for local AnkiConnect proxy (default: `8766`) | -| `proxy.upstreamUrl` | string (URL) | Upstream AnkiConnect URL that proxy forwards to (default: `http://127.0.0.1:8765`) | +| `proxy.upstreamUrl` | string (URL) | Upstream AnkiConnect URL that proxy forwards to (default: `http://127.0.0.1:8765`) | | `tags` | array of strings | Tags automatically added to cards mined/updated by SubMiner (default: `['SubMiner']`; set `[]` to disable automatic tagging). | | `deck` | string | Anki deck to monitor for new cards | | `ankiConnect.nPlusOne.decks` | array of strings | Decks used for N+1 known-word cache lookups. When omitted/empty, falls back to `ankiConnect.deck`. | @@ -499,6 +499,7 @@ Jellyfin integration is optional and disabled by default. When enabled, SubMiner | `transcodeVideoCodec` | string | Preferred transcode video codec fallback (default: `h264`) | Jellyfin auth session (`accessToken` + `userId`) is stored in local encrypted storage after login/setup. + - On Linux, token storage defaults to `gnome-libsecret` for `safeStorage`. Override with `--password-store=` on launcher/app invocations when needed. Launcher subcommands: @@ -561,21 +562,21 @@ See `config.example.jsonc` for detailed configuration options and more examples. **Default keybindings:** -| Key | Command | Description | -| ----------------- | -------------------------- | ------------------------------------- | -| `Space` | `["cycle", "pause"]` | Toggle pause | -| `KeyJ` | `["cycle", "sid"]` | Cycle primary subtitle track | -| `Shift+KeyJ` | `["cycle", "secondary-sid"]` | Cycle secondary subtitle track | -| `ArrowRight` | `["seek", 5]` | Seek forward 5 seconds | -| `ArrowLeft` | `["seek", -5]` | Seek backward 5 seconds | -| `ArrowUp` | `["seek", 60]` | Seek forward 60 seconds | -| `ArrowDown` | `["seek", -60]` | Seek backward 60 seconds | -| `Shift+KeyH` | `["sub-seek", -1]` | Jump to previous subtitle | -| `Shift+KeyL` | `["sub-seek", 1]` | Jump to next subtitle | -| `Ctrl+Shift+KeyH` | `["__replay-subtitle"]` | Replay current subtitle, pause at end | -| `Ctrl+Shift+KeyL` | `["__play-next-subtitle"]` | Play next subtitle, pause at end | -| `KeyQ` | `["quit"]` | Quit mpv | -| `Ctrl+KeyW` | `["quit"]` | Quit mpv | +| Key | Command | Description | +| ----------------- | ---------------------------- | ------------------------------------- | +| `Space` | `["cycle", "pause"]` | Toggle pause | +| `KeyJ` | `["cycle", "sid"]` | Cycle primary subtitle track | +| `Shift+KeyJ` | `["cycle", "secondary-sid"]` | Cycle secondary subtitle track | +| `ArrowRight` | `["seek", 5]` | Seek forward 5 seconds | +| `ArrowLeft` | `["seek", -5]` | Seek backward 5 seconds | +| `ArrowUp` | `["seek", 60]` | Seek forward 60 seconds | +| `ArrowDown` | `["seek", -60]` | Seek backward 60 seconds | +| `Shift+KeyH` | `["sub-seek", -1]` | Jump to previous subtitle | +| `Shift+KeyL` | `["sub-seek", 1]` | Jump to next subtitle | +| `Ctrl+Shift+KeyH` | `["__replay-subtitle"]` | Replay current subtitle, pause at end | +| `Ctrl+Shift+KeyL` | `["__play-next-subtitle"]` | Play next subtitle, pause at end | +| `KeyQ` | `["quit"]` | Quit mpv | +| `Ctrl+KeyW` | `["quit"]` | Quit mpv | **Custom keybindings example:** @@ -614,8 +615,14 @@ Use the runtime options palette to toggle settings live while SubMiner is runnin Current runtime options: - `ankiConnect.behavior.autoUpdateNewCards` (`On` / `Off`) +- `ankiConnect.nPlusOne.highlightEnabled` (`On` / `Off`) +- `subtitleStyle.enableJlpt` (`On` / `Off`) +- `subtitleStyle.frequencyDictionary.enabled` (`On` / `Off`) +- `ankiConnect.nPlusOne.matchMode` (`headword` / `surface`) - `ankiConnect.isKiku.fieldGrouping` (`auto` / `manual` / `disabled`) +Annotation toggles (`nPlusOne`, `enableJlpt`, `frequencyDictionary.enabled`) only apply to new subtitle lines after the toggle. The currently displayed line is not re-tokenized in place. + Default shortcut: `Ctrl+Shift+O` Palette controls: @@ -680,21 +687,21 @@ See `config.example.jsonc` for detailed configuration options. } ``` -| Option | Values | Description | -| ------------------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `toggleVisibleOverlayGlobal` | string \| `null` | Global accelerator for toggling visible subtitle overlay (default: `"Alt+Shift+O"`) | -| `copySubtitle` | string \| `null` | Accelerator for copying current subtitle (default: `"CommandOrControl+C"`) | -| `copySubtitleMultiple` | string \| `null` | Accelerator for multi-copy mode (default: `"CommandOrControl+Shift+C"`) | -| `updateLastCardFromClipboard` | string \| `null` | Accelerator for updating card from clipboard (default: `"CommandOrControl+V"`) | -| `triggerFieldGrouping` | string \| `null` | Accelerator for Kiku field grouping on last card (default: `"CommandOrControl+G"`; only active when `behavior.autoUpdateNewCards` is `false`) | -| `triggerSubsync` | string \| `null` | Accelerator for running Subsync (default: `"Ctrl+Alt+S"`) | -| `mineSentence` | string \| `null` | Accelerator for creating sentence card from current subtitle (default: `"CommandOrControl+S"`) | -| `mineSentenceMultiple` | string \| `null` | Accelerator for multi-mine sentence card mode (default: `"CommandOrControl+Shift+S"`) | -| `multiCopyTimeoutMs` | number | Timeout in ms for multi-copy/mine digit input (default: `3000`) | -| `toggleSecondarySub` | string \| `null` | Accelerator for cycling secondary subtitle mode (default: `"CommandOrControl+Shift+V"`) | -| `markAudioCard` | string \| `null` | Accelerator for marking last card as audio card (default: `"CommandOrControl+Shift+A"`) | -| `openRuntimeOptions` | string \| `null` | Opens runtime options palette for live session-only toggles (default: `"CommandOrControl+Shift+O"`) | -| `openJimaku` | string \| `null` | Opens the Jimaku search modal (default: `"Ctrl+Shift+J"`) | +| Option | Values | Description | +| ----------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| `toggleVisibleOverlayGlobal` | string \| `null` | Global accelerator for toggling visible subtitle overlay (default: `"Alt+Shift+O"`) | +| `copySubtitle` | string \| `null` | Accelerator for copying current subtitle (default: `"CommandOrControl+C"`) | +| `copySubtitleMultiple` | string \| `null` | Accelerator for multi-copy mode (default: `"CommandOrControl+Shift+C"`) | +| `updateLastCardFromClipboard` | string \| `null` | Accelerator for updating card from clipboard (default: `"CommandOrControl+V"`) | +| `triggerFieldGrouping` | string \| `null` | Accelerator for Kiku field grouping on last card (default: `"CommandOrControl+G"`; only active when `behavior.autoUpdateNewCards` is `false`) | +| `triggerSubsync` | string \| `null` | Accelerator for running Subsync (default: `"Ctrl+Alt+S"`) | +| `mineSentence` | string \| `null` | Accelerator for creating sentence card from current subtitle (default: `"CommandOrControl+S"`) | +| `mineSentenceMultiple` | string \| `null` | Accelerator for multi-mine sentence card mode (default: `"CommandOrControl+Shift+S"`) | +| `multiCopyTimeoutMs` | number | Timeout in ms for multi-copy/mine digit input (default: `3000`) | +| `toggleSecondarySub` | string \| `null` | Accelerator for cycling secondary subtitle mode (default: `"CommandOrControl+Shift+V"`) | +| `markAudioCard` | string \| `null` | Accelerator for marking last card as audio card (default: `"CommandOrControl+Shift+A"`) | +| `openRuntimeOptions` | string \| `null` | Opens runtime options palette for live session-only toggles (default: `"CommandOrControl+Shift+O"`) | +| `openJimaku` | string \| `null` | Opens the Jimaku search modal (default: `"Ctrl+Shift+J"`) | **See `config.example.jsonc`** for the complete list of shortcut configuration options. @@ -750,27 +757,27 @@ See `config.example.jsonc` for detailed configuration options. } ``` -| Option | Values | Description | -| ---------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------- | -| `fontFamily` | string | CSS font-family value (default: `"M PLUS 1 Medium, Source Han Sans JP, Noto Sans CJK JP"`) | -| `fontSize` | number (px) | Font size in pixels (default: `35`) | -| `fontColor` | string | Any CSS color value (default: `"#cad3f5"`) | -| `fontWeight` | string | CSS font-weight, e.g. `"bold"`, `"normal"`, `"600"` (default: `"600"`) | -| `fontStyle` | string | `"normal"` or `"italic"` (default: `"normal"`) | -| `backgroundColor` | string | Any CSS color, including `"transparent"` (default: `"rgb(30, 32, 48, 0.88)"`) | -| `enableJlpt` | boolean | Enable JLPT level underline styling (`false` by default) | -| `preserveLineBreaks` | boolean | Preserve line breaks in visible overlay subtitle rendering (`false` by default). Enable to mirror mpv line layout. | -| `frequencyDictionary.enabled` | boolean | Enable frequency highlighting from dictionary lookups (`false` by default) | +| Option | Values | Description | +| ---------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- | +| `fontFamily` | string | CSS font-family value (default: `"M PLUS 1 Medium, Source Han Sans JP, Noto Sans CJK JP"`) | +| `fontSize` | number (px) | Font size in pixels (default: `35`) | +| `fontColor` | string | Any CSS color value (default: `"#cad3f5"`) | +| `fontWeight` | string | CSS font-weight, e.g. `"bold"`, `"normal"`, `"600"` (default: `"600"`) | +| `fontStyle` | string | `"normal"` or `"italic"` (default: `"normal"`) | +| `backgroundColor` | string | Any CSS color, including `"transparent"` (default: `"rgb(30, 32, 48, 0.88)"`) | +| `enableJlpt` | boolean | Enable JLPT level underline styling (`false` by default) | +| `preserveLineBreaks` | boolean | Preserve line breaks in visible overlay subtitle rendering (`false` by default). Enable to mirror mpv line layout. | +| `frequencyDictionary.enabled` | boolean | Enable frequency highlighting from dictionary lookups (`false` by default) | | `frequencyDictionary.sourcePath` | string | Path to a local frequency dictionary root. Leave empty or omit to use installed/default frequency-dictionary search paths. | -| `frequencyDictionary.topX` | number | Only color tokens whose frequency rank is `<= topX` (`1000` by default) | -| `frequencyDictionary.mode` | string | `"single"` or `"banded"` (`"single"` by default) | -| `frequencyDictionary.matchMode` | string | `"headword"` or `"surface"` (`"headword"` by default) | -| `frequencyDictionary.singleColor` | string | Color used for all highlighted tokens in single mode | -| `frequencyDictionary.bandedColors` | string[] | Array of five hex colors used for ranked bands in banded mode | -| `nPlusOneColor` | string | Existing n+1 highlight color (default: `#c6a0f6`) | -| `knownWordColor` | string | Existing known-word highlight color (default: `#a6da95`) | -| `jlptColors` | object | JLPT level underline colors object (`N1`..`N5`) | -| `secondary` | object | Override any of the above for secondary subtitles (optional) | +| `frequencyDictionary.topX` | number | Only color tokens whose frequency rank is `<= topX` (`1000` by default) | +| `frequencyDictionary.mode` | string | `"single"` or `"banded"` (`"single"` by default) | +| `frequencyDictionary.matchMode` | string | `"headword"` or `"surface"` (`"headword"` by default) | +| `frequencyDictionary.singleColor` | string | Color used for all highlighted tokens in single mode | +| `frequencyDictionary.bandedColors` | string[] | Array of five hex colors used for ranked bands in banded mode | +| `nPlusOneColor` | string | Existing n+1 highlight color (default: `#c6a0f6`) | +| `knownWordColor` | string | Existing known-word highlight color (default: `#a6da95`) | +| `jlptColors` | object | JLPT level underline colors object (`N1`..`N5`) | +| `secondary` | object | Override any of the above for secondary subtitles (optional) | JLPT underlining is powered by offline term-meta bank files at runtime. See [`docs/jlpt-vocab-bundle.md`](jlpt-vocab-bundle.md) for required files, source/version refresh steps, and deterministic fallback behavior. @@ -852,13 +859,13 @@ Control which startup warmups run in the background versus deferring to first re } ``` -| Option | Values | Description | -| ------------------------ | --------------- | ------------------------------------------------------------------------------------------------ | -| `lowPowerMode` | `true`, `false` | Defer all warmups except Yomitan extension | -| `mecab` | `true`, `false` | Warm up MeCab tokenizer at startup | -| `yomitanExtension` | `true`, `false` | Warm up Yomitan extension at startup | -| `subtitleDictionaries` | `true`, `false` | Warm up JLPT + frequency dictionaries at startup | -| `jellyfinRemoteSession` | `true`, `false` | Warm up Jellyfin remote session at startup (still requires Jellyfin remote auto-connect settings) | +| Option | Values | Description | +| ----------------------- | --------------- | ------------------------------------------------------------------------------------------------- | +| `lowPowerMode` | `true`, `false` | Defer all warmups except Yomitan extension | +| `mecab` | `true`, `false` | Warm up MeCab tokenizer at startup | +| `yomitanExtension` | `true`, `false` | Warm up Yomitan extension at startup | +| `subtitleDictionaries` | `true`, `false` | Warm up JLPT + frequency dictionaries at startup | +| `jellyfinRemoteSession` | `true`, `false` | Warm up Jellyfin remote session at startup (still requires Jellyfin remote auto-connect settings) | Defaults warm everything (`true` for all toggles, `lowPowerMode: false`). Setting a warmup toggle to `false` defers that work until first usage. diff --git a/docs/immersion-tracking.md b/docs/immersion-tracking.md index 3c23166..0573eab 100644 --- a/docs/immersion-tracking.md +++ b/docs/immersion-tracking.md @@ -147,4 +147,3 @@ FROM imm_monthly_rollups ORDER BY rollup_month DESC, video_id DESC LIMIT ?; ``` - diff --git a/docs/index.assets.test.ts b/docs/index.assets.test.ts index d27bfd4..4aa2505 100644 --- a/docs/index.assets.test.ts +++ b/docs/index.assets.test.ts @@ -6,9 +6,19 @@ const docsIndexContents = readFileSync(docsIndexPath, 'utf8'); test('docs demo media uses shared cache-busting asset version token', () => { expect(docsIndexContents).toMatch(/const demoAssetVersion = ['"][^'"]+['"]/); - expect(docsIndexContents).toContain(':poster="`/assets/minecard-poster.jpg?v=${demoAssetVersion}`"'); - expect(docsIndexContents).toContain(''); - expect(docsIndexContents).toContain(''); - expect(docsIndexContents).toContain(''); - expect(docsIndexContents).toContain('SubMiner demo GIF fallback'); + expect(docsIndexContents).toContain( + ':poster="`/assets/minecard-poster.jpg?v=${demoAssetVersion}`"', + ); + expect(docsIndexContents).toContain( + '', + ); + expect(docsIndexContents).toContain( + '', + ); + expect(docsIndexContents).toContain( + '', + ); + expect(docsIndexContents).toContain( + 'SubMiner demo GIF fallback', + ); }); diff --git a/docs/launcher-script.md b/docs/launcher-script.md index 0b74d2a..4c92535 100644 --- a/docs/launcher-script.md +++ b/docs/launcher-script.md @@ -16,10 +16,10 @@ subminer -r -d ~/Anime # recursive search fzf shows video files in a fuzzy-searchable list. If `chafa` is installed, you get thumbnail previews in the right pane. Thumbnails are sourced from the freedesktop thumbnail cache first, then generated on the fly with `ffmpegthumbnailer` or `ffmpeg` as fallback. -| Optional tool | Purpose | -| --------------------- | -------------------------------- | -| `chafa` | Render thumbnails in the terminal | -| `ffmpegthumbnailer` | Generate thumbnails on the fly | +| Optional tool | Purpose | +| ------------------- | --------------------------------- | +| `chafa` | Render thumbnails in the terminal | +| `ffmpegthumbnailer` | Generate thumbnails on the fly | ### rofi @@ -61,38 +61,37 @@ subminer ytsearch:"jp news" # YouTube search ## Subcommands -| Subcommand | Purpose | -| ------------------------- | ---------------------------------------------- | +| Subcommand | Purpose | +| -------------------------- | ---------------------------------------------------------- | | `subminer jellyfin` / `jf` | Jellyfin workflows (`-d` discovery, `-p` play, `-l` login) | -| `subminer yt` / `youtube` | YouTube shorthand (`-o`, `-m`) | -| `subminer doctor` | Dependency + config + socket diagnostics | -| `subminer config path` | Print active config file path | -| `subminer config show` | Print active config contents | -| `subminer mpv status` | Check mpv socket readiness | -| `subminer mpv socket` | Print active socket path | -| `subminer mpv idle` | Launch detached idle mpv instance | -| `subminer texthooker` | Launch texthooker-only mode | -| `subminer app` | Pass arguments directly to SubMiner binary | +| `subminer yt` / `youtube` | YouTube shorthand (`-o`, `-m`) | +| `subminer doctor` | Dependency + config + socket diagnostics | +| `subminer config path` | Print active config file path | +| `subminer config show` | Print active config contents | +| `subminer mpv status` | Check mpv socket readiness | +| `subminer mpv socket` | Print active socket path | +| `subminer mpv idle` | Launch detached idle mpv instance | +| `subminer texthooker` | Launch texthooker-only mode | +| `subminer app` | Pass arguments directly to SubMiner binary | Use `subminer -h` for command-specific help. ## Options -| Flag | Description | -| -------------------- | -------------------------------------------- | -| `-d, --directory` | Video search directory (default: cwd) | -| `-r, --recursive` | Search directories recursively | -| `-R, --rofi` | Use rofi instead of fzf | -| `-S, --start` | Start overlay after mpv launches | -| `-T, --no-texthooker`| Disable texthooker server | -| `-p, --profile` | mpv profile name (default: `subminer`) | -| `-b, --backend` | Force window backend (`hyprland`, `sway`, `x11`) | -| `--log-level` | Logger verbosity (`debug`, `info`, `warn`, `error`) | -| `--dev`, `--debug` | Enable app dev-mode (not tied to log level) | +| Flag | Description | +| --------------------- | --------------------------------------------------- | +| `-d, --directory` | Video search directory (default: cwd) | +| `-r, --recursive` | Search directories recursively | +| `-R, --rofi` | Use rofi instead of fzf | +| `-S, --start` | Start overlay after mpv launches | +| `-T, --no-texthooker` | Disable texthooker server | +| `-p, --profile` | mpv profile name (default: `subminer`) | +| `-b, --backend` | Force window backend (`hyprland`, `sway`, `x11`) | +| `--log-level` | Logger verbosity (`debug`, `info`, `warn`, `error`) | +| `--dev`, `--debug` | Enable app dev-mode (not tied to log level) | ## Logging - Default log level is `info` - `--background` mode defaults to `warn` unless `--log-level` is explicitly set - `--dev` / `--debug` control app behavior, not logging verbosity — use `--log-level` for that - diff --git a/docs/mining-workflow.md b/docs/mining-workflow.md index 58be627..f3af336 100644 --- a/docs/mining-workflow.md +++ b/docs/mining-workflow.md @@ -99,11 +99,11 @@ If you prefer a hands-on approach (animecards-style), you can copy the current s This is useful when auto-update is disabled or when you want explicit control over which subtitle line gets attached to the card. -| Shortcut | Action | Config key | -| --------------------------- | ----------------------------------------- | ------------------------------------- | -| `Ctrl/Cmd+C` | Copy current subtitle | `shortcuts.copySubtitle` | -| `Ctrl/Cmd+Shift+C` + digit | Copy multiple recent lines | `shortcuts.copySubtitleMultiple` | -| `Ctrl/Cmd+V` | Update last card from clipboard | `shortcuts.updateLastCardFromClipboard` | +| Shortcut | Action | Config key | +| -------------------------- | ------------------------------- | --------------------------------------- | +| `Ctrl/Cmd+C` | Copy current subtitle | `shortcuts.copySubtitle` | +| `Ctrl/Cmd+Shift+C` + digit | Copy multiple recent lines | `shortcuts.copySubtitleMultiple` | +| `Ctrl/Cmd+V` | Update last card from clipboard | `shortcuts.updateLastCardFromClipboard` | ### 3. Mine Sentence (Hotkey) diff --git a/docs/mpv-plugin.md b/docs/mpv-plugin.md index 38ef8c1..895d797 100644 --- a/docs/mpv-plugin.md +++ b/docs/mpv-plugin.md @@ -29,16 +29,16 @@ input-ipc-server=/tmp/subminer-socket All keybindings use a `y` chord prefix — press `y`, then the second key: -| Chord | Action | -| ----- | ------------------------ | -| `y-y` | Open menu | -| `y-s` | Start overlay | -| `y-S` | Stop overlay | -| `y-t` | Toggle visible overlay | -| `y-o` | Open settings window | -| `y-r` | Restart overlay | -| `y-c` | Check status | -| `y-k` | Skip intro (AniSkip) | +| Chord | Action | +| ----- | ---------------------- | +| `y-y` | Open menu | +| `y-s` | Start overlay | +| `y-S` | Stop overlay | +| `y-t` | Toggle visible overlay | +| `y-o` | Open settings window | +| `y-r` | Restart overlay | +| `y-c` | Check status | +| `y-k` | Skip intro (AniSkip) | ## Menu @@ -116,26 +116,26 @@ aniskip_button_duration=3 ### Option Reference -| Option | Default | Values | Description | -| ------------------------------ | ---------------------- | ------------------------------------------ | -------------------------------- | -| `binary_path` | `""` (auto-detect) | file path | Path to SubMiner binary | -| `socket_path` | `/tmp/subminer-socket` | file path | MPV IPC socket path | -| `texthooker_enabled` | `yes` | `yes` / `no` | Enable texthooker server | -| `texthooker_port` | `5174` | 1–65535 | Texthooker server port | -| `backend` | `auto` | `auto`, `hyprland`, `sway`, `x11`, `macos` | Window manager backend | -| `auto_start` | `no` | `yes` / `no` | Auto-start overlay on file load when mpv socket matches `socket_path` | -| `auto_start_visible_overlay` | `no` | `yes` / `no` | Show visible layer on auto-start when mpv socket matches `socket_path` | -| `osd_messages` | `yes` | `yes` / `no` | Show OSD status messages | -| `log_level` | `info` | `debug`, `info`, `warn`, `error` | Log verbosity | -| `aniskip_enabled` | `yes` | `yes` / `no` | Enable AniSkip intro detection | -| `aniskip_title` | `""` | string | Override title used for lookup | -| `aniskip_season` | `""` | numeric season | Optional season hint | -| `aniskip_mal_id` | `""` | numeric MAL id | Skip title lookup; use fixed id | -| `aniskip_episode` | `""` | numeric episode | Skip episode parsing; use fixed | -| `aniskip_show_button` | `yes` | `yes` / `no` | Show in-range intro skip prompt | -| `aniskip_button_text` | `You can skip by pressing %s` | string | OSD prompt format (`%s`=key) | -| `aniskip_button_key` | `y-k` | mpv key chord | Primary key for intro skip action (`y-k` always works as fallback) | -| `aniskip_button_duration` | `3` | float seconds | OSD hint duration | +| Option | Default | Values | Description | +| ---------------------------- | ----------------------------- | ------------------------------------------ | ---------------------------------------------------------------------- | +| `binary_path` | `""` (auto-detect) | file path | Path to SubMiner binary | +| `socket_path` | `/tmp/subminer-socket` | file path | MPV IPC socket path | +| `texthooker_enabled` | `yes` | `yes` / `no` | Enable texthooker server | +| `texthooker_port` | `5174` | 1–65535 | Texthooker server port | +| `backend` | `auto` | `auto`, `hyprland`, `sway`, `x11`, `macos` | Window manager backend | +| `auto_start` | `no` | `yes` / `no` | Auto-start overlay on file load when mpv socket matches `socket_path` | +| `auto_start_visible_overlay` | `no` | `yes` / `no` | Show visible layer on auto-start when mpv socket matches `socket_path` | +| `osd_messages` | `yes` | `yes` / `no` | Show OSD status messages | +| `log_level` | `info` | `debug`, `info`, `warn`, `error` | Log verbosity | +| `aniskip_enabled` | `yes` | `yes` / `no` | Enable AniSkip intro detection | +| `aniskip_title` | `""` | string | Override title used for lookup | +| `aniskip_season` | `""` | numeric season | Optional season hint | +| `aniskip_mal_id` | `""` | numeric MAL id | Skip title lookup; use fixed id | +| `aniskip_episode` | `""` | numeric episode | Skip episode parsing; use fixed | +| `aniskip_show_button` | `yes` | `yes` / `no` | Show in-range intro skip prompt | +| `aniskip_button_text` | `You can skip by pressing %s` | string | OSD prompt format (`%s`=key) | +| `aniskip_button_key` | `y-k` | mpv key chord | Primary key for intro skip action (`y-k` always works as fallback) | +| `aniskip_button_duration` | `3` | float seconds | OSD hint duration | ## Binary Auto-Detection diff --git a/docs/plans/2026-02-26-secondary-subtitles-main-overlay.md b/docs/plans/2026-02-26-secondary-subtitles-main-overlay.md index c749751..f9b1700 100644 --- a/docs/plans/2026-02-26-secondary-subtitles-main-overlay.md +++ b/docs/plans/2026-02-26-secondary-subtitles-main-overlay.md @@ -13,10 +13,12 @@ ### Task 1: Add Regression Tests For Main Overlay Secondary Rendering **Files:** + - Modify: `src/renderer/subtitle-render.test.ts` - Modify: `src/renderer/error-recovery.test.ts` **Step 1: Write failing tests** + - Assert stylesheet no longer hides secondary subtitles in `layer-visible`. - Assert renderer platform resolution ignores legacy `secondary` overlay layer. @@ -28,12 +30,14 @@ Expected: FAIL on secondary subtitle hide rule + legacy secondary layer handling ### Task 2: Remove Secondary-Window CSS/Routing Assumptions **Files:** + - Modify: `src/renderer/style.css` - Modify: `src/renderer/utils/platform.ts` - Modify: `src/renderer/error-recovery.ts` - Modify: `src/types.ts` **Step 1: Implement minimal changes** + - Remove legacy forced hide on `#secondarySubContainer`. - Remove obsolete layer-specific secondary-subtitle CSS blocks. - Drop legacy `secondary` overlay-layer parsing path from renderer platform resolver. @@ -47,6 +51,7 @@ Expected: PASS. ### Task 3: Validate Wider Related Surface **Files:** + - No additional code changes required. **Step 1: Run broader related tests** diff --git a/docs/public/config.example.jsonc b/docs/public/config.example.jsonc index 42475bc..b6b2d4f 100644 --- a/docs/public/config.example.jsonc +++ b/docs/public/config.example.jsonc @@ -5,7 +5,6 @@ * 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. @@ -17,7 +16,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. // ========================================== @@ -27,7 +26,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. // ========================================== @@ -36,7 +35,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. // ========================================== @@ -57,7 +56,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. // ========================================== @@ -77,7 +76,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. // ========================================== @@ -88,7 +87,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. // ========================================== @@ -96,7 +95,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. // ========================================== @@ -129,7 +128,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 @@ -138,13 +137,7 @@ "mode": "single", // single: use one color for all matching tokens. banded: use color ramp by frequency band. Values: single | banded "matchMode": "headword", // Frequency lookup text selection mode. Values: headword | surface "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": { "fontFamily": "Manrope, Inter", // Font family setting. @@ -159,8 +152,8 @@ "backgroundColor": "transparent", // Background color setting. "backdropFilter": "blur(6px)", // Backdrop filter setting. "fontWeight": "normal", // Font weight setting. - "fontStyle": "normal" // Font style setting. - } // Secondary setting. + "fontStyle": "normal", // Font style setting. + }, // Secondary setting. }, // Primary and secondary subtitle styling. // ========================================== @@ -177,17 +170,15 @@ "enabled": true, // Enable local AnkiConnect-compatible proxy for push-based auto-enrichment. Values: true | false "host": "127.0.0.1", // Bind host for local AnkiConnect proxy. "port": 8766, // Bind port for local AnkiConnect proxy. - "upstreamUrl": "http://127.0.0.1:8765" // Upstream AnkiConnect URL proxied by local AnkiConnect proxy. + "upstreamUrl": "http://127.0.0.1:8765", // Upstream AnkiConnect URL proxied by local AnkiConnect proxy. }, // Proxy setting. - "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 @@ -196,7 +187,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 @@ -209,7 +200,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 @@ -217,7 +208,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 @@ -226,20 +217,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. // ========================================== @@ -249,7 +240,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. // ========================================== @@ -260,10 +251,7 @@ "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. // ========================================== @@ -272,7 +260,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. // ========================================== @@ -296,16 +284,8 @@ "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. // ========================================== @@ -316,7 +296,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. // ========================================== @@ -338,7 +318,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. } diff --git a/docs/shortcuts.md b/docs/shortcuts.md index bed4f5e..088e1fd 100644 --- a/docs/shortcuts.md +++ b/docs/shortcuts.md @@ -6,10 +6,10 @@ All shortcuts are configurable in `config.jsonc` under `shortcuts` and `keybindi These work system-wide regardless of which window has focus. -| Shortcut | Action | Configurable | -| ------------- | ------------------------ | ---------------------------------------- | -| `Alt+Shift+O` | Toggle visible overlay | `shortcuts.toggleVisibleOverlayGlobal` | -| `Alt+Shift+Y` | Open Yomitan settings | Fixed (not configurable) | +| Shortcut | Action | Configurable | +| ------------- | ---------------------- | -------------------------------------- | +| `Alt+Shift+O` | Toggle visible overlay | `shortcuts.toggleVisibleOverlayGlobal` | +| `Alt+Shift+Y` | Open Yomitan settings | Fixed (not configurable) | ::: tip Global shortcuts are registered with the OS. If they conflict with another application, update them in `shortcuts` config and restart SubMiner. @@ -81,15 +81,15 @@ Enter edit mode to fine-tune subtitle alignment. When the mpv plugin is installed, all commands use a `y` chord prefix — press `y`, then the second key within 1 second. -| Chord | Action | -| ----- | --------------------------------------- | -| `y-y` | Open SubMiner menu (OSD) | -| `y-s` | Start overlay | -| `y-S` | Stop overlay | -| `y-t` | Toggle visible overlay | -| `y-o` | Open Yomitan settings | -| `y-r` | Restart overlay | -| `y-c` | Check overlay status | +| Chord | Action | +| ----- | ------------------------ | +| `y-y` | Open SubMiner menu (OSD) | +| `y-s` | Start overlay | +| `y-S` | Stop overlay | +| `y-t` | Toggle visible overlay | +| `y-o` | Open Yomitan settings | +| `y-r` | Restart overlay | +| `y-c` | Check overlay status | When the overlay has focus, press `y` then `d` to toggle DevTools (debugging helper). diff --git a/docs/usage.md b/docs/usage.md index e7a094a..2d0a8d6 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -2,9 +2,9 @@ There are two ways to use SubMiner — the `subminer` wrapper script or the mpv plugin: -| Approach | Best For | -| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **subminer script** | All-in-one solution. Handles video selection, launches MPV with the correct socket, and manages app commands. Overlay start is explicit (`--start`, `-S`, or `y-s`). | +| Approach | Best For | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **subminer script** | All-in-one solution. Handles video selection, launches MPV with the correct socket, and manages app commands. Overlay start is explicit (`--start`, `-S`, or `y-s`). | | **MPV plugin** | When you launch MPV yourself or from other tools. Provides in-MPV chord keybindings (e.g. `y-y` for menu) to control overlay visibility. Requires `--input-ipc-server=/tmp/subminer-socket`. | You can use both together—install the plugin for on-demand control, but use `subminer` when you want the streamlined workflow. @@ -167,10 +167,10 @@ Notes: ### Global Shortcuts -| Keybind | Action | -| ------------- | ------------------------ | -| `Alt+Shift+O` | Toggle visible overlay | -| `Alt+Shift+Y` | Open Yomitan settings | +| Keybind | Action | +| ------------- | ---------------------- | +| `Alt+Shift+O` | Toggle visible overlay | +| `Alt+Shift+Y` | Open Yomitan settings | `Alt+Shift+Y` is a fixed global shortcut; it is not part of `shortcuts` config.