Compare commits

..

25 Commits

Author SHA1 Message Date
4c95b57885 chore: prepare v0.9.3 release 2026-03-25 23:58:31 -07:00
242402b253 refactor: move youtube primary subtitle config to youtube 2026-03-25 23:53:56 -07:00
61d15f9431 docs: apply M PLUS 1 and Manrope font defaults for docs content 2026-03-25 23:52:29 -07:00
508864bcbb chore(backlog): complete backlog cleanup 2026-03-25 22:42:21 -07:00
23c54bb01e chore: prepare v0.9.2 release 2026-03-25 22:37:59 -07:00
ec667c64e8 fix: cancel stale autoplay fallback retries 2026-03-25 22:18:26 -07:00
39b2ccad8e fix: address latest coderabbit review 2026-03-25 22:18:26 -07:00
23815945bf chore: remove stale release notes artifact 2026-03-25 22:18:26 -07:00
9dca83acd9 fix: delay youtube overlay bootstrap until yomitan loads 2026-03-25 22:18:26 -07:00
55300e2d8c Refine YouTube playback launch preparation 2026-03-25 22:18:26 -07:00
28afd15134 Tighten YouTube playback launch readiness 2026-03-25 22:18:26 -07:00
58304757aa Fix YouTube playback PR review issues 2026-03-25 22:18:26 -07:00
c95518e94a Fix Windows YouTube playback flow and overlay pointer tracking 2026-03-25 22:18:26 -07:00
5ee4617607 Fix Windows overlay scaling bounds (#34) 2026-03-25 16:30:11 -07:00
842008b089 chore: prepare v0.9.1 release 2026-03-24 00:55:03 -07:00
6f56a0bcf6 fix(renderer): keep controller input active with sidebar open 2026-03-24 00:23:00 -07:00
5feed360ca feat: add app-owned YouTube subtitle flow with absPlayer-style parsing (#31)
* fix: harden preload argv parsing for popup windows

* fix: align youtube playback with shared overlay startup

* fix: unwrap mpv youtube streams for anki media mining

* docs: update docs for youtube subtitle and mining flow

* refactor: unify cli and runtime wiring for startup and youtube flow

* feat: update subtitle sidebar overlay behavior

* chore: add shared log-file source for diagnostics

* fix(ci): add changelog fragment for immersion changes

* fix: address CodeRabbit review feedback

* fix: persist canonical title from youtube metadata

* style: format stats library tab

* fix: address latest review feedback

* style: format stats library files

* test: stub launcher youtube deps in CI

* test: isolate launcher youtube flow deps

* test: stub launcher youtube deps in failing case

* test: force x11 backend in launcher ci harness

* test: address latest review feedback

* fix(launcher): preserve user YouTube ytdl raw options

* docs(backlog): update task tracking notes

* fix(immersion): special-case youtube media paths in runtime and tracking

* feat(stats): improve YouTube media metadata and picker key handling

* fix(ci): format stats media library hook

* fix: address latest CodeRabbit review items

* docs: update youtube release notes and docs

* feat: auto-load youtube subtitles before manual picker

* fix: restore app-owned youtube subtitle flow

* docs: update youtube playback docs and config copy

* refactor: remove legacy youtube launcher mode plumbing

* fix: refine youtube subtitle startup binding

* docs: clarify youtube subtitle startup behavior

* fix: address PR #31 latest review follow-ups

* fix: address PR #31 follow-up review comments

* test: harden youtube picker test harness

* udpate backlog

* fix: add timeout to youtube metadata probe

* docs: refresh youtube and stats docs

* update backlog

* update backlog

* chore: release v0.9.0
2026-03-24 00:01:24 -07:00
c17f0a4080 Fix tokenizer annotations for explanatory contrast ending (#33) 2026-03-23 09:25:17 -07:00
0317c7f011 docs: add WebSocket & Texthooker API integration guide (#30) 2026-03-22 02:48:54 -07:00
13797b5005 docs: align v0.8.0 release notes with subtitle sidebar changes 2026-03-22 00:07:05 -07:00
b24d9d7487 fix(release): make changelog build idempotent for re-run tagged releases 2026-03-21 23:50:27 -07:00
3a01cffc6b feat(subtitle-sidebar): add sidebar config surface (#28) 2026-03-21 23:37:42 -07:00
eddf6f0456 docs: document release changelog recovery path 2026-03-20 03:15:05 -07:00
f6c024d61e fix: build changelog artifacts in release job 2026-03-20 03:00:40 -07:00
6749ff843c feat(stats): add v1 immersion stats dashboard (#19) 2026-03-20 02:43:28 -07:00
558 changed files with 34174 additions and 4715 deletions

View File

@@ -89,14 +89,17 @@ jobs:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/texthooker-ui/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: bun install --frozen-lockfile
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Build texthooker-ui
run: |
@@ -144,9 +147,10 @@ jobs:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/texthooker-ui/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
@@ -171,7 +175,9 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Install dependencies
run: bun install --frozen-lockfile
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Build texthooker-ui
run: |
@@ -216,14 +222,17 @@ jobs:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/texthooker-ui/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: bun install --frozen-lockfile
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Build texthooker-ui
shell: powershell
@@ -325,6 +334,14 @@ jobs:
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
- name: Build changelog artifacts for release
run: |
if find changes -maxdepth 1 -name '*.md' -not -name README.md -print -quit | grep -q .; then
bun run changelog:build --version "${{ steps.version.outputs.VERSION }}"
else
echo "No pending changelog fragments found."
fi
- name: Verify changelog is ready for tagged release
run: bun run changelog:check --version "${{ steps.version.outputs.VERSION }}"

5
.gitignore vendored
View File

@@ -1,6 +1,9 @@
# Dependencies
node_modules/
# Superpowers brainstorming
.superpowers/
# Electron build output
out/
dist/
@@ -22,9 +25,7 @@ Thumbs.db
.idea/
*.swp
*.swo
**/CLAUDE.md
environment.toml
**/CLAUDE.md
.env
.vscode/*

View File

@@ -1,5 +1,142 @@
# Changelog
## v0.9.3 (2026-03-25)
### Changed
- Launcher: Moved YouTube primary subtitle language defaults to `youtube.primarySubLanguages`.
- Launcher: Removed the placeholder YouTube subtitle retime step and now uses downloaded primary subtitle tracks directly, so there is no fake path rewrite before playback/sidebar loading.
- YouTube: Removed the `src/core/services/youtube/retime` helper and its tests after retiring the internal retime strategy.
- Docs: Clarified optional `alass` / `ffsubsync` subtitle-sync requirements and setup steps, including fallback behavior when sync tools are absent.
- Launcher: Removed the old `youtubeSubgen.primarySubLanguages` config path from the generated config and docs.
## v0.9.2 (2026-03-25)
### Fixed
- Overlay: Fixed overlay pointer tracking so Windows click-through toggles immediately when the cursor enters or leaves subtitle regions, without waiting for a later hover resync.
- Overlay: Fixed Windows overlay window tracking on scaled displays by converting native tracked window bounds to Electron DIP coordinates before applying overlay bounds.
- Launcher: Fixed Windows direct `--youtube-play` startup so MPV boots reliably, stays paused until the app-owned subtitle flow is ready, and reuses an already-running SubMiner instance when available.
- Launcher: Fixed standalone Windows `--youtube-play` sessions so closing MPV fully exits SubMiner instead of leaving hidden overlay windows or a background process behind.
- Overlay: Fixed `subminer <youtube-url>` on Linux so the YouTube playback flow waits for Yomitan to load before creating the overlay window, avoiding the broken lookup popup state that previously required a manual overlay refresh.
## v0.9.1 (2026-03-24)
### Changed
- Release: Reduced packaged release size by excluding duplicate `extraResources` payload and pruning docs, tests, sourcemaps, and other source-only files from Electron bundles.
### Fixed
- Overlay: Restored controller navigation and lookup/mining controls while the subtitle sidebar is open, while keeping true modal dialogs blocking controller actions.
- Tokenizer: Fixed subtitle annotation clearing so explanatory contrast endings like `んですけど` are excluded consistently across the shared tokenizer filter and annotation stage.
## v0.9.0 (2026-03-23)
### Added
- Docs: Added a new WebSocket / Texthooker API and integration guide covering WebSocket payloads, custom client patterns, mpv plugin automation, and webhook-style relay examples. Linked from configuration and mining workflow docs for easier discovery.
### Changed
- Launcher: Added an app-owned YouTube subtitle flow that pauses mpv, uses absPlayer-style YouTube timedtext parsing/conversion to download subtitle tracks, and injects them as external files before playback resumes.
- Launcher: Changed YouTube subtitle startup to auto-load the best-available primary and secondary subtitle tracks at launch instead of forcing the picker modal first. Secondary subtitle failures no longer block playback resume.
- Launcher: Added `Ctrl+Alt+C` as the default keybinding to manually open the YouTube subtitle picker during active YouTube playback.
- Launcher: Added yt-dlp metadata probing so YouTube playback and immersion tracking record canonical video title and channel metadata.
- Launcher: Stopped forcing `--ytdl-raw-options=` before user-provided mpv options so existing YouTube cookie integrations in user `--args` are no longer clobbered.
- Launcher: Disabled mpv native YouTube subtitle auto-loading for the app-owned flow so injected external subtitle files remain authoritative.
- Launcher: Added OSD status messages for YouTube playback startup, subtitle acquisition, and subtitle loading so the flow stays visible before and during the picker.
- Subtitle Sidebar: Added startup-auto-open controls and resume positioning improvements so the sidebar jumps directly to the first resolved active cue.
- Subtitle Sidebar: Improved subtitle prefetch and embedded overlay passthrough sync so sidebar and overlay subtitle states stay consistent across media transitions.
- Subtitle Sidebar: Updated scroll handling, embedded layout styling, and active-cue visual behavior.
- Stats: Stats Library tab now displays YouTube video title, channel name, and channel thumbnail for YouTube media entries, with retry logic to fill in metadata that arrives after initial load.
### Fixed
- Launcher: Fixed Anki media mining for mpv YouTube streams by unwrapping the stream URL so audio and screenshot capture work correctly for YouTube playback sessions.
- Immersion: Fixed YouTube media path handling in the immersion runtime and tracking so YouTube sessions record correct media references, AniList guessing skips YouTube URLs, and post-watch state transitions do not fire for YouTube media.
- Launcher: Fixed startup-launched YouTube playback so primary subtitle overlay updates continue after auto-load completes.
- Launcher: Fixed auto-loaded YouTube primary subtitles so parsed cues appear in the subtitle sidebar without needing a manual picker retry.
- Launcher: Fixed the YouTube picker to guard against duplicate subtitle submissions and tightened YouTube URL detection so follow-up runtime flows only treat real YouTube hosts as YouTube playback.
- Launcher: Fixed primary subtitle failure notifications being shown while app-owned YouTube subtitle probing and downloads are still in flight.
- Launcher: Preserved existing authoritative YouTube subtitle tracks when available; downloaded tracks are used only to fill missing sides, and native mpv secondary subtitle rendering is hidden so the overlay remains the sole secondary display.
## v0.8.0 (2026-03-22)
### Added
- Overlay: Added the subtitle sidebar feature with a new `subtitleSidebar` configuration surface and rendered sidebar modal with cue list rendering, click-to-seek, active-cue highlighting, and embedded layout support.
- IPC: Added sidebar snapshot plumbing between renderer and main process for overlay/sidebar synchronization.
### Changed
- Config: Added hot-reloadable sidebar options for enablement, layout, visibility, typography, opacity, sizing, and interaction behavior (`autoOpen`, `pauseOnHover`, `autoScroll`, toggle key).
- Docs: Added full `subtitleSidebar` documentation coverage, including sample config, option table, and toggle shortcut notes.
- Runtime: Improved subtitle prefetch/rendering flow so sidebar and overlay subtitle states stay in sync across media transitions.
### Fixed
- Overlay: Kept sidebar cue tracking stable across playback transitions and timing edge cases.
- Overlay: Improved sidebar resume/start behavior to jump directly to the first resolved active cue.
- Overlay: Stopped stale subtitle refreshes from regressing active-cue and text state.
## v0.7.0 (2026-03-19)
### Added
- Immersion: Added Mine Word, Mine Sentence, and Mine Audio buttons to word detail example lines in the stats dashboard.
- Immersion: Mine Word creates a full Yomitan card (definition, reading, pitch accent) via the hidden search page bridge, then enriches with sentence audio, screenshot, and metadata extracted from the source video.
- Immersion: Mine Sentence and Mine Audio create cards directly with appropriate Lapis/Kiku flags, sentence highlighting, and media from the source file.
- Immersion: Media generation (audio + image/AVIF) runs in parallel and respects all AnkiConnect config options.
- Immersion: Added word exclusion list to the Vocabulary tab with localStorage persistence and a management modal.
- Immersion: Fixed truncated readings in the frequency rank table (e.g. お前 now shows おまえ instead of まえ).
- Immersion: Clicking a bar in the Top Repeated Words chart now opens the word detail panel.
- Immersion: Secondary subtitle text is now stored alongside primary subtitle lines for use as translation when mining cards from the stats page.
- Stats: Added `subminer stats -b` to start or reuse a dedicated background stats server without blocking normal SubMiner instances.
- Stats: Added `subminer stats -s` to stop the dedicated background stats server without closing browser tabs.
- Stats: Stats server startup now reuses a running background stats daemon instead of trying to bind a second local server in another SubMiner instance.
- Launcher: Added launcher passthrough for `-a/--args` so mpv receives raw extra launch flags (`--fs`, `--ytdl-format`, custom audio/video settings, etc.) from the `subminer` command.
- Launcher: Added `subminer stats` to launch the local stats dashboard, force-start the stats server on demand, and open the dashboard in your browser.
- Launcher: Added `subminer stats cleanup` to backfill vocabulary metadata and prune stale or excluded immersion rows on demand.
- Launcher: Added `stats.autoOpenBrowser` so browser launch after `subminer stats` can be enabled or disabled explicitly.
- Immersion: Added a local stats dashboard for immersion tracking with Overview, Anime, Trends, Vocabulary, and Sessions views.
- Immersion: Added anime progress, episode completion, Anki card links, and occurrence drill-down across the stats dashboard.
- Immersion: Added richer session timelines with new-word activity, cumulative totals, and pause/seek/card event markers.
- Immersion: Added completed-episodes and completed-anime totals to the Overview tracking snapshot.
### Changed
- Anki: Changed known-word cache settings to live under `ankiConnect.knownWords` instead of mixing them into `ankiConnect.nPlusOne`.
- Anki: Kept legacy `ankiConnect.nPlusOne` known-word keys and older `ankiConnect.behavior.nPlusOne*` keys as deprecated compatibility fallbacks.
- Stats: Added session deletion to the Sessions tab with the same confirmation prompt used by anime episode/session deletes, and removed all associated session rows from the stats database.
- Immersion: Kept immersion tracking history by default while preserving daily/monthly rollup maintenance.
- Immersion: Added exact lifetime summary reads for overview/anime/media stats so dashboard totals no longer depend on rescanning raw telemetry.
- Immersion: Reduced tracker storage overhead by removing duplicated subtitle text from subtitle-line event payloads.
- Immersion: Deduplicated episode cover-art blobs through a shared blob store and updated cover-art reads/writes to resolve shared images correctly.
- Immersion: Added indexes for large-history session, telemetry, vocabulary, kanji, and cover-art queries to keep dashboard reads fast as the SQLite database grows.
- Immersion: Renamed the stats dashboard's Anime tab to Library so the media browser label matches non-anime sources like YouTube and other yt-dlp-backed content.
- Anilist: Standardized episode completion threshold by introducing `DEFAULT_MIN_WATCH_RATIO` and using it for both local watched state transitions and AniList post-watch progress updates.
- Anilist: Episode auto-marking now uses the same threshold as AniList (`85%`), removing divergent completion behavior.
- Overlay: Excluded interjections and sound-effect tokens from subtitle annotation styling so they no longer inherit misleading lexical highlight treatment while still remaining visible and hoverable as plain subtitle tokens.
- Overlay: Expanded subtitle annotation noise filtering to also strip annotation metadata from standalone grammar-only helper tokens such as particles, auxiliaries, adnominals, common explanatory endings like `んです` / `のだ`, and merged trailing quote-particle forms like `...って` while keeping them tokenized for hover lookup.
### Fixed
- Launcher: Fixed mpv Lua plugin binary auto-detection on Linux to also search `/usr/bin/subminer` and `/usr/local/bin/subminer` (lowercase), matching the conventional Unix wrapper name used by packaged installs such as the AUR package.
- Stats: Fixed the in-app stats overlay so it connects to the configured `stats.serverPort` instead of falling back to the default port.
- Overlay: Fixed subtitle frequency tagging for merged lookup-backed tokens like `陰に` by falling back to exact surface-form Yomitan frequencies when the normalized headword lookup misses.
- Overlay: Fixed MeCab merged-token position mapping across line breaks so merged content-plus-particle tokens like `陰に` keep their matched Yomitan frequency instead of inheriting shifted POS tags.
- Overlay: Fixed grouped frequency parsing in both Yomitan and fallback frequency-dictionary lookups so display values like `118,121` use the leading rank instead of collapsing the rank and occurrence count into `118121`.
- Overlay: Fixed frequency-rank ingestion to ignore Yomitan dictionaries explicitly marked `occurrence-based`, so raw occurrence counts are no longer treated as subtitle rank values.
- Overlay: Fixed inflected headword frequency tagging to prefer ranks from the selected Yomitan `termsFind` popup entry itself, ordered by configured dictionary priority, so forms like `潜み` use primary-dictionary ranks like `4073` before falling back to lower-priority raw lemma metadata such as `CC100`.
- Overlay: Fixed annotation-stage frequency filtering so exact kanji noun tokens like `者` keep their matched rank even when MeCab labels them `名詞/非自立`, instead of dropping the highlight after scan-time frequency lookup succeeds.
- Anki: Fixed repeated character-dictionary startup work by scheduling auto-sync only from mpv media-path changes instead of also re-triggering it from connection and media-title events for the same title.
- Overlay: Fixed macOS fullscreen overlay stability by keeping the passive visible overlay from stealing focus, re-raising the overlay window when reasserting its macOS topmost level, and tolerating one transient macOS tracker/helper miss before hiding the overlay.
- Overlay: Kept subtitle tokenization warmup one-shot for the lifetime of the app so later fullscreen/media churn on macOS does not replay the startup warmup gate after the first file is ready.
- Overlay: Added a bounded macOS tracker loss-grace window so fullscreen enter/leave transitions do not immediately hide and reload the overlay when the helper briefly loses the mpv window.
- Overlay: Skipped subtitle/tokenization refresh invalidation on character-dictionary auto-sync completion when the dictionary was already current, preventing startup flash/reload loops on unchanged media.
- Stats: Fixed session stats so known-word counts track real known-word occurrences without collapsing subtitle-line gaps.
- Stats: Fixed session word totals in session-facing stats views to prefer token counts when available, preventing known words from exceeding total words in the session chart.
- Stats: Fixed the stats Vocabulary tab blank-screen regression caused by a hook-order crash after vocabulary data finished loading.
- Anki: Fixed card-mine OSD feedback so the final mine result stops the Anki spinner first, then shows a single-line `✓`/`x` status without being overwritten by a later spinner tick.
- Stats: Removed the misleading `New words` series from expanded session charts; session detail now shows only the real total-word and known-word lines.
- Stats: Restored the cross-anime word table behavior in stats vocabulary surfaces so shared vocabulary entries no longer disappear or merge incorrectly across related media.
- Stats: `subminer stats -b` now runs as a standalone background stats daemon instead of reusing the main SubMiner app process, so the overlay app can still be launched separately for normal video watching.
- Stats: Dashboard word mining still works against the background daemon by using a short-lived hidden helper for the Yomitan add-note flow.
- Stats: Load full session timelines by default in stats session detail views so long sessions preserve complete telemetry history instead of being truncated by a fixed sample limit.
- Stats: Replaced heuristic stats word counts with Yomitan token counts, so session, media, anime, and trend subtitle totals now come directly from parsed subtitle tokens.
- Stats: Updated stats UI labels and lookup-rate copy to refer to tokens instead of words where those counts are shown.
- Overlay: Reduced repeated `Overlay loading...` popups on macOS when fullscreen tracker flaps briefly hide and recover the visible overlay.
- Stats: Scaled expanded session-detail known-word charts to the session's actual percentage range so small changes no longer render as a nearly flat line.
- Jlpt: Reduced JLPT dictionary startup log noise by summarizing duplicate surface-form collisions instead of logging one line per duplicate entry.
## v0.6.5 (2026-03-15)
### Internal

1
CLAUDE.md Symbolic link
View File

@@ -0,0 +1 @@
AGENTS.md

290
README.md
View File

@@ -1,44 +1,171 @@
<div align="center">
<img src="assets/SubMiner.png" width="169" alt="SubMiner logo">
<h1>SubMiner</h1>
<strong>Look up words, mine to Anki, and enrich cards with context — without leaving mpv.</strong>
<br /><br />
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Linux](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-informational)]()
[![Docs](https://img.shields.io/badge/docs-docs.subminer.moe-blueviolet)](https://docs.subminer.moe)
<img src="assets/SubMiner.png" width="160" alt="SubMiner logo">
# SubMiner
## Turn mpv into a sentence-mining workstation.
Look up words with Yomitan, export to Anki in one key, track your immersion — all without leaving mpv.
[![License: GPL v3](https://img.shields.io/badge/license-GPLv3-1a1a2e?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0)
[![Platform](https://img.shields.io/badge/platform-Linux%20·%20macOS%20·%20Windows-1a1a2e?style=flat-square)](https://github.com/ksyasuda/SubMiner)
[![Docs](https://img.shields.io/badge/docs-docs.subminer.moe-e6a817?style=flat-square)](https://docs.subminer.moe)
[![AUR](https://img.shields.io/aur/version/subminer-bin?style=flat-square&color=1a1a2e)](https://aur.archlinux.org/packages/subminer-bin)
[![SubMiner demo](./assets/minecard.webp)](./assets/minecard.mp4)
</div>
<br />
## How It Works
SubMiner runs as an invisible Electron overlay on top of mpv. Subtitles render as an interactive layer. Move your cursor over any word and trigger a [Yomitan](https://github.com/yomidevs/yomitan) lookup. Press one key to snapshot the sentence, audio, and screenshot into Anki via AnkiConnect.
## Features
### Dictionary Lookups
Yomitan runs inside the overlay. Trigger a lookup on any word for full dictionary popups — definitions, pitch accent, frequency data — without ever leaving mpv.
<div align="center">
[![SubMiner demo (Animated preview)](./assets/minecard.webp)](./assets/minecard.mp4)
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan dictionary popup over annotated subtitles in mpv">
</div>
<br />
<br>
## What it does
### Instant Anki Mining
SubMiner is an Electron overlay that sits on top of mpv. It turns your video player into a full sentence-mining workstation:
Create an Anki card with the sentence, audio clip, screenshot, and machine translation from the exact playback moment with one key press, click, or controller input.
- **Look up words as you watch** — Yomitan dictionary popups on hover or keyboard-driven token-by-token navigation
- **One-key Anki mining** — Creates cards with sentence, audio, screenshot, and translation; optional local AnkiConnect proxy auto-enriches Yomitan cards instantly
- **Reading annotations** — N+1 targeting, frequency-dictionary highlighting, JLPT underlining, and character name dictionary for anime/manga proper nouns
- **Immersion stats** — Optional local dashboard and overlay for watch time, anime progress, session drill-down, vocabulary growth, mining throughput, and card mining directly from example sentences; exact lifetime totals are kept locally in SQLite by default
- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync
- **Jellyfin & AniList integration** — Remote playback, cast device mode, and automatic episode progress tracking
- **Texthooker & API** — Built-in texthooker page and annotated websocket feed for external clients
<div align="center">
<img src="docs-site/public/screenshots/one-key-mining.png" width="800" alt="Anki card created from SubMiner with sentence, audio, and screenshot">
</div>
## Quick start
<br>
### Reading Annotations
Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Known words fade back; new words stand out. Grammar-only tokens render as plain text so you focus on what matters.
<div align="center">
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets">
</div>
<br>
### Immersion Dashboard
Local stats dashboard — watch time, anime library, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking.
<div align="center">
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
</div>
<br>
### Integrations
<table>
<tr>
<td><b>YouTube</b></td>
<td>Auto-loaded yt-dlp subtitle tracks at startup with a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
</tr>
<tr>
<td><b>AniList</b></td>
<td>Automatic episode tracking and progress sync</td>
</tr>
<tr>
<td><b>Jellyfin</b></td>
<td>Browse and launch media from your Jellyfin server</td>
</tr>
<tr>
<td><b>Jimaku</b></td>
<td>Search and download Japanese subtitles</td>
</tr>
<tr>
<td><b>alass / ffsubsync</b></td>
<td>Automatic subtitle retiming — requires <code>alass</code> or <code>ffsubsync</code> on your <code>PATH</code> (optional; subtitle syncing is disabled without them)</td>
</tr>
<tr>
<td><b>WebSocket</b></td>
<td>Annotated subtitle feed for external clients (texthooker pages, custom tools)</td>
</tr>
</table>
<div align="center">
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page receiving annotated subtitle lines via WebSocket">
</div>
<br>
---
## Requirements
| | Required | Optional |
| -------------- | --------------------------------------- | -------------------------------------- |
| **Player** | [`mpv`](https://mpv.io) with IPC socket | — |
| **Processing** | `ffmpeg`, `mecab` + `mecab-ipadic` | `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) |
| **Media** | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` |
| **Selection** | — | `fzf` / `rofi` |
> [!NOTE]
> [`bun`](https://bun.sh) is required if building from source or using the CLI wrapper: `subminer`. Pre-built releases (AppImage, DMG, installer) do not require it.
**Platform-specific:**
| Linux | macOS | Windows |
| ----------------------------------- | ------------------------ | ------------- |
| `hyprctl` or `xdotool` + `xwininfo` | Accessibility permission | No extra deps |
<details>
<summary><b>Arch Linux</b></summary>
```bash
paru -S --needed mpv ffmpeg mecab-git mecab-ipadic
# Optional
paru -S --needed yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo
# Optional: subtitle sync (install at least one for subtitle syncing to work)
paru -S --needed alass python-ffsubsync
# X11 / XWAYLAND
paru -S --needed xdotool xorg-xwininfo
```
</details>
<details>
<summary><b>macOS</b></summary>
```bash
brew install mpv ffmpeg mecab mecab-ipadic
# Optional
brew install yt-dlp fzf rofi chafa ffmpegthumbnailer
# Optional: subtitle sync (install at least one for subtitle syncing to work)
brew install alass
pip install ffsubsync
```
Grant Accessibility permission to SubMiner in **System Settings > Privacy & Security > Accessibility**.
</details>
<details>
<summary><b>Windows</b></summary>
Install [`mpv`](https://mpv.io/installation/) and [`ffmpeg`](https://ffmpeg.org/download.html) and ensure both are on your `PATH`.
For MeCab, install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary.
</details>
---
## Quick Start
### 1. Install
**Arch Linux (AUR):**
Install [`subminer-bin`](https://aur.archlinux.org/packages/subminer-bin) from the AUR. It installs the packaged AppImage plus the `subminer` wrapper:
<details>
<summary><b>Arch Linux (AUR)</b></summary>
```bash
paru -S subminer-bin
@@ -47,84 +174,85 @@ paru -S subminer-bin
Or manually:
```bash
git clone https://aur.archlinux.org/subminer-bin.git
cd subminer-bin
makepkg -si
git clone https://aur.archlinux.org/subminer-bin.git && cd subminer-bin && makepkg -si
```
**Linux (AppImage):**
</details>
<details>
<summary><b>Linux (AppImage)</b></summary>
```bash
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage
chmod +x ~/.local/bin/SubMiner.AppImage
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer
chmod +x ~/.local/bin/subminer
mkdir -p ~/.local/bin
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage \
&& chmod +x ~/.local/bin/SubMiner.AppImage
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer \
&& chmod +x ~/.local/bin/subminer
```
> [!NOTE]
> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
**macOS (DMG/ZIP):** download the latest packaged build from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
</details>
**Windows (Installer/ZIP):** download the latest `SubMiner-<version>.exe` installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Keep `mpv` installed and available on `PATH`.
<details>
<summary><b>macOS</b></summary>
**From source** — initialize submodules first (`git submodule update --init --recursive`). Bundled Yomitan is built from the `vendor/subminer-yomitan` submodule into `build/yomitan` during `bun run build`, so source builds only need Bun for the JS toolchain. Packaged macOS and Windows installs do not require Bun. Windows installer builds go through `electron-builder`; its bundled `app-builder-lib` NSIS templates already use the third-party `WinShell` plugin for shortcut AppUserModelID assignment, and the `WinShell.dll` binary is supplied by electron-builder's cached `nsis-resources` bundle, so `bun run build:win` does not need a separate repo-local plugin install step. Full install guide: [docs.subminer.moe/installation#from-source](https://docs.subminer.moe/installation#from-source).
Download the latest DMG or ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
### 2. Launch the app once
</details>
<details>
<summary><b>Windows</b></summary>
Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Make sure `mpv` is on your `PATH`.
</details>
<details>
<summary><b>From source</b></summary>
See the [build-from-source guide](https://docs.subminer.moe/installation#from-source).
</details>
### 2. First Launch
Run the app. On first launch SubMiner starts in the system tray, creates a default config, and opens a setup popup to install the mpv plugin and configure Yomitan dictionaries.
### 3. Mine
```bash
# Linux
SubMiner.AppImage
subminer video.mkv # play video with overlay
subminer --start video.mkv # explicit overlay start
subminer stats # open immersion dashboard
subminer stats -b # stats daemon in background
subminer stats -s # stop background stats daemon
```
On macOS, launch `SubMiner.app`. On Windows, launch `SubMiner.exe` from the Start menu or install directory.
On first launch, SubMiner:
- starts in the tray/background
- creates the default config directory and `config.jsonc`
- opens a compact setup popup
- can install the mpv plugin to the default mpv scripts location for you
- links directly to Yomitan settings so you can install dictionaries before finishing setup
### 3. Finish setup
- click `Install mpv plugin` if you want the default plugin auto-start flow
- click `Open Yomitan Settings` and install at least one dictionary
- click `Refresh status`
- click `Finish setup`
The mpv plugin step is optional. Yomitan must report at least one installed dictionary before setup can be completed.
### 4. Mine
```bash
subminer video.mkv # default plugin config auto-starts visible overlay + resumes playback when ready
subminer --start video.mkv # optional explicit overlay start when plugin auto_start=no
subminer stats # open the local stats dashboard in your browser
```
## Requirements
| Required | Optional |
| ------------------------------------------ | -------------------------------------------------- |
| `bun` (source builds, Linux `subminer`) | |
| `mpv` with IPC socket | `yt-dlp` |
| `ffmpeg` | `guessit` (better AniSkip title/episode detection) |
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
| macOS: Accessibility permission | |
Windows builds use native window tracking and do not require the Linux compositor helper tools.
---
## Documentation
For full guides on configuration, Anki, Jellyfin, immersion tracking/stats, and more, see [docs.subminer.moe](https://docs.subminer.moe). The VitePress source for that site lives in [`docs-site/`](./docs-site/).
Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more: **[docs.subminer.moe](https://docs.subminer.moe)**
---
## Acknowledgments
Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary). Subtitles powered by [Jimaku.cc](https://jimaku.cc). Dictionary lookups via [Yomitan](https://github.com/yomidevs/yomitan), and JLPT tags from [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab).
SubMiner builds on the work of these open-source projects:
| Project | Role |
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |
## License

View File

@@ -7,14 +7,14 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- enhancement
- overlay
- mpv
- aniskip
dependencies: []
ordinal: 42500
ordinal: 43500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-08 18:24'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- bug
- macos
@@ -19,7 +19,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/scripts/get-mpv-window-macos.swift
priority: high
ordinal: 52500
ordinal: 53500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- ci
- release
@@ -18,7 +18,7 @@ references:
- src/release-workflow.test.ts
- 'https://github.com/ksyasuda/SubMiner/actions/runs/22836585479'
priority: high
ordinal: 51500
ordinal: 52500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-08 20:24'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- release
- patch
@@ -16,7 +16,7 @@ references:
- CHANGELOG.md
- release/release-notes.md
priority: high
ordinal: 50500
ordinal: 51500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-08 20:41'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- ci
- release
@@ -18,7 +18,7 @@ references:
- build/signpath-windows-artifact-config.xml
- src/release-workflow.test.ts
priority: high
ordinal: 48500
ordinal: 49500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-08 20:44'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- release
- patch
@@ -16,7 +16,7 @@ references:
- CHANGELOG.md
- release/release-notes.md
priority: high
ordinal: 49500
ordinal: 50500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- release
- windows
@@ -15,7 +15,7 @@ references:
- package.json
- src/release-workflow.test.ts
priority: high
ordinal: 44500
ordinal: 45500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- release
- patch
@@ -16,7 +16,7 @@ references:
- CHANGELOG.md
- release/release-notes.md
priority: high
ordinal: 45500
ordinal: 46500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Fix guessit title parsing for character dictionary sync
status: Done
assignee: []
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- dictionary
- anilist
@@ -17,7 +17,7 @@ references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/core/services/anilist/anilist-updater.test.ts
priority: high
ordinal: 43500
ordinal: 44500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Refresh current subtitle after character dictionary sync completes
status: Done
assignee: []
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- dictionary
- overlay
@@ -15,7 +15,7 @@ references:
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
priority: high
ordinal: 41500
ordinal: 42500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Show character dictionary auto-sync progress on OSD
status: Done
assignee: []
created_date: '2026-03-09 01:10'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- dictionary
- overlay
@@ -17,7 +17,7 @@ references:
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync-notifications.ts
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
priority: medium
ordinal: 40500
ordinal: 41500
---
## Description

View File

@@ -6,7 +6,7 @@ title: >-
status: Done
assignee: []
created_date: '2026-03-09 10:40'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- startup
- overlay
@@ -21,7 +21,7 @@ references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync-notifications.ts
priority: medium
ordinal: 36500
ordinal: 37500
---
## Description

View File

@@ -5,14 +5,14 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- bug
- overlay
- aniskip
- linux
dependencies: []
ordinal: 46500
ordinal: 47500
---
## Description

View File

@@ -5,14 +5,14 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 00:00'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- windows
- plugin
- regression
dependencies: []
priority: medium
ordinal: 47500
ordinal: 48500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 01:10'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- release
- patch
@@ -25,7 +25,7 @@ references:
- scripts/build-changelog.test.ts
- docs/RELEASING.md
priority: high
ordinal: 38500
ordinal: 39500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-09 01:11'
updated_date: '2026-03-16 05:13'
updated_date: '2026-03-18 05:28'
labels:
- tooling
- formatting
@@ -20,7 +20,7 @@ references:
- scripts/build-win-unsigned.mjs
- src
priority: medium
ordinal: 39500
ordinal: 40500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-11 08:28'
updated_date: '2026-03-16 06:25'
updated_date: '2026-03-18 05:28'
labels:
- linux
- packaging
@@ -20,6 +20,7 @@ references:
- docs-site/launcher-script.md
- README.md
priority: medium
ordinal: 116500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- Codex
created_date: '2026-03-17 18:10'
updated_date: '2026-03-17 18:14'
updated_date: '2026-03-18 05:28'
labels:
- release
- packaging
@@ -16,9 +16,12 @@ references:
- /home/sudacode/projects/japanese/SubMiner/.github/workflows/release.yml
- /home/sudacode/projects/japanese/SubMiner/scripts/update-aur-package.sh
- /home/sudacode/projects/japanese/SubMiner/scripts/update-aur-package.test.ts
- /home/sudacode/projects/japanese/SubMiner/packaging/aur/subminer-bin/PKGBUILD
- /home/sudacode/projects/japanese/SubMiner/packaging/aur/subminer-bin/.SRCINFO
- >-
/home/sudacode/projects/japanese/SubMiner/packaging/aur/subminer-bin/PKGBUILD
- >-
/home/sudacode/projects/japanese/SubMiner/packaging/aur/subminer-bin/.SRCINFO
priority: medium
ordinal: 107500
---
## Description

View File

@@ -11,6 +11,7 @@ labels:
- stats
- database
- anilist
milestone: m-1
dependencies: []
references:
- >-

View File

@@ -1,11 +1,12 @@
---
id: TASK-170
title: 'Fix imm_words POS filtering and add stats cleanup maintenance command'
title: Fix imm_words POS filtering and add stats cleanup maintenance command
status: Done
assignee: []
created_date: '2026-03-13 00:00'
updated_date: '2026-03-14 18:31'
updated_date: '2026-03-18 05:31'
labels: []
milestone: m-1
dependencies: []
priority: high
ordinal: 9010
@@ -14,25 +15,19 @@ ordinal: 9010
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
`imm_words` is currently populated from raw subtitle text instead of tokenized subtitle metadata, so ignored functional/noise tokens leak into stats and no POS metadata is stored. Fix live persistence to follow the existing token annotation exclusion rules and add an on-demand stats cleanup command to remove stale bad vocabulary rows from the stats DB.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 New `imm_words` inserts use tokenized subtitle data, persist POS metadata, and skip tokens excluded by existing POS-based vocabulary ignore rules.
- [x] #2 `subminer stats cleanup` supports `-v` / `--vocab`, defaults to vocab cleanup, and removes stale bad `imm_words` rows on demand.
- [x] #3 Regression coverage exists for persistence filtering, cleanup behavior, and stats cleanup CLI wiring.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed `imm_words` persistence so the tracker now consumes tokenized subtitle data, stores POS metadata (`part_of_speech`, `pos1`, `pos2`, `pos3`), preserves distinct surface/lemma fields (`word` vs `headword`) when tokenization provides them, and skips vocabulary rows excluded by the existing POS/noise rules instead of mining raw subtitle fragments. Added `subminer stats cleanup` with default vocab cleanup plus `-v/--vocab`; the cleanup pass now repairs stale `headword`, `reading`, and `part_of_speech` values, attempts best-effort MeCab backfill for legacy rows, and removes rows that still have no usable POS metadata or fail the vocab filters.
Verification:
@@ -41,5 +36,4 @@ Verification:
- `bun test src/core/services/immersion-tracker-service.test.ts src/core/services/immersion-tracker/__tests__/query.test.ts src/core/services/immersion-tracker/storage-session.test.ts launcher/parse-args.test.ts launcher/commands/command-modules.test.ts src/main/runtime/stats-cli-command.test.ts src/main/runtime/mpv-main-event-main-deps.test.ts src/core/services/cli-command.test.ts`
- `bun run docs:test`
- `bun run docs:build`
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -5,20 +5,24 @@ status: Done
assignee:
- '@codex'
created_date: '2026-03-16 10:45'
updated_date: '2026-03-16 23:04'
updated_date: '2026-03-18 05:28'
labels:
- bug
- macos
- overlay
dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-window.ts
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-visibility.ts
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-runtime-init.ts
- /Users/sudacode/projects/japanese/SubMiner/src/window-trackers/macos-tracker.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-window.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-visibility.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-runtime-init.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/window-trackers/macos-tracker.ts
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
priority: high
ordinal: 53000
ordinal: 54500
---
## Description

View File

@@ -8,6 +8,7 @@ updated_date: '2026-03-16 05:13'
labels:
- stats
- ui
milestone: m-1
dependencies: []
priority: low
ordinal: 17500

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-15 10:18'
updated_date: '2026-03-16 06:46'
updated_date: '2026-03-18 05:28'
labels:
- bug
- tokenizer
@@ -20,6 +20,7 @@ references:
- /Users/sudacode/projects/japanese/SubMiner/scripts/get_frequency.ts
- /Users/sudacode/projects/japanese/SubMiner/scripts/test-yomitan-parser.ts
priority: high
ordinal: 115500
---
## Description

View File

@@ -5,11 +5,12 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 09:15'
updated_date: '2026-03-17 09:41'
updated_date: '2026-03-18 05:28'
labels:
- stats
- immersion-tracking
- yomitan
milestone: m-1
dependencies: []
references:
- vendor/subminer-yomitan/ext/js/app/frontend.js
@@ -23,6 +24,7 @@ references:
documentation:
- docs/plans/2026-03-17-yomitan-lookup-stats-design.md
priority: medium
ordinal: 114500
---
## Description

View File

@@ -5,11 +5,12 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 14:59'
updated_date: '2026-03-17 15:13'
updated_date: '2026-03-18 05:28'
labels:
- pr-review
- immersion-tracker
- stats
milestone: m-1
dependencies: []
references:
- >-
@@ -21,6 +22,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/__tests__/query.test.ts
priority: medium
ordinal: 113500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 15:15'
updated_date: '2026-03-17 15:19'
updated_date: '2026-03-18 05:28'
labels:
- sqlite
- immersion-tracking
@@ -15,6 +15,7 @@ documentation:
- >-
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-17-sqlite-tuning.md
priority: medium
ordinal: 111500
---
## Description

View File

@@ -5,11 +5,12 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 15:16'
updated_date: '2026-03-17 15:18'
updated_date: '2026-03-18 05:28'
labels:
- launcher
- stats
- tests
milestone: m-1
dependencies: []
references:
- >-
@@ -18,6 +19,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/launcher/commands/command-modules.test.ts
priority: medium
ordinal: 112500
---
## Description

View File

@@ -5,13 +5,15 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 15:31'
updated_date: '2026-03-17 15:55'
updated_date: '2026-03-18 05:28'
labels:
- cli
- launcher
- stats
milestone: m-1
dependencies: []
priority: medium
ordinal: 110500
---
## Description

View File

@@ -2,10 +2,11 @@
id: TASK-182
title: Fix session stats chart known-word totals exceeding total words
status: Done
milestone: m-1
assignee:
- codex
created_date: '2026-03-17 16:07'
updated_date: '2026-03-17 16:19'
updated_date: '2026-03-18 05:28'
labels: []
dependencies: []
references:
@@ -15,6 +16,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/__tests__/stats-server.test.ts
- /Users/sudacode/projects/japanese/SubMiner/stats/src/hooks/useSessions.ts
ordinal: 109500
---
## Description

View File

@@ -5,11 +5,12 @@ status: Done
assignee:
- '@codex'
created_date: '2026-03-18 01:41'
updated_date: '2026-03-18 01:45'
updated_date: '2026-03-18 05:28'
labels:
- bug
- stats
- ui
milestone: m-1
dependencies: []
references:
- >-
@@ -19,6 +20,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/lib/media-session-list.test.tsx
parent_task_id: TASK-182
ordinal: 101500
---
## Description

View File

@@ -2,10 +2,11 @@
id: TASK-183
title: Fix blank stats vocabulary page regression
status: Done
milestone: m-1
assignee:
- codex
created_date: '2026-03-17 16:23'
updated_date: '2026-03-17 16:29'
updated_date: '2026-03-18 05:28'
labels: []
dependencies: []
references:
@@ -13,6 +14,7 @@ references:
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/vocabulary/VocabularyTab.tsx
- /Users/sudacode/projects/japanese/SubMiner/stats/src/App.tsx
- /Users/sudacode/projects/japanese/SubMiner/stats/src/lib/api-client.ts
ordinal: 108500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- Codex
created_date: '2026-03-17 19:28'
updated_date: '2026-03-17 19:31'
updated_date: '2026-03-18 05:28'
labels:
- stabilization
- ci
@@ -14,6 +14,7 @@ references:
- package.json
- docs/workflow/verification.md
priority: medium
ordinal: 106500
---
## Description

View File

@@ -5,11 +5,12 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 22:58'
updated_date: '2026-03-17 23:00'
updated_date: '2026-03-18 05:28'
labels:
- bug
- stats
- ui
milestone: m-1
dependencies: []
references:
- >-
@@ -18,6 +19,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/query.ts
priority: medium
ordinal: 104500
---
## Description

View File

@@ -5,10 +5,11 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 23:19'
updated_date: '2026-03-17 23:29'
updated_date: '2026-03-18 05:28'
labels:
- stats
- ui
milestone: m-1
dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/stats/src/App.tsx
@@ -21,6 +22,7 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/library/MediaDetailView.tsx
priority: medium
ordinal: 103500
---
## Description

View File

@@ -5,10 +5,11 @@ status: Done
assignee:
- codex
created_date: '2026-03-17 23:42'
updated_date: '2026-03-17 23:57'
updated_date: '2026-03-18 05:28'
labels:
- stats
- ui
milestone: m-1
dependencies: []
references:
- >-
@@ -31,6 +32,7 @@ documentation:
- >-
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-17-episode-detail-session-accordion.md
priority: medium
ordinal: 102500
---
## Description

View File

@@ -1,14 +1,15 @@
---
id: TASK-187.1
title: Auto-expand targeted session when opening media detail
status: In Progress
status: Done
assignee:
- codex
created_date: '2026-03-18 01:32'
updated_date: '2026-03-18 01:36'
updated_date: '2026-03-18 05:28'
labels:
- stats
- ui
milestone: m-1
dependencies: []
references:
- stats/src/lib/stats-navigation.ts
@@ -19,6 +20,7 @@ references:
- stats/src/lib/stats-navigation.test.ts
parent_task_id: TASK-187
priority: medium
ordinal: 117500
---
## Description
@@ -29,11 +31,11 @@ When a navigation path opens episode/media detail with a known session ID, the m
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Media detail navigation state can carry an optional target session ID alongside the selected video.
- [ ] #2 Any navigation path that opens media detail with a known session ID causes that session row to auto-expand when the episode history loads.
- [ ] #3 Session-tab fallback for orphan sessions without a video still behaves as it does now.
- [ ] #4 Media detail auto-expansion clears or stabilizes its one-shot navigation state so normal manual expand/collapse behavior still works after landing.
- [ ] #5 Relevant navigation/component tests cover the targeted media-detail auto-expand behavior.
- [x] #1 Media detail navigation state can carry an optional target session ID alongside the selected video.
- [x] #2 Any navigation path that opens media detail with a known session ID causes that session row to auto-expand when the episode history loads.
- [x] #3 Session-tab fallback for orphan sessions without a video still behaves as it does now.
- [x] #4 Media detail auto-expansion clears or stabilizes its one-shot navigation state so normal manual expand/collapse behavior still works after landing.
- [x] #5 Relevant navigation/component tests cover the targeted media-detail auto-expand behavior.
<!-- AC:END -->
## Implementation Plan

View File

@@ -0,0 +1,56 @@
---
id: TASK-189
title: Replace stats word counts with Yomitan token counts
status: Done
assignee:
- codex
created_date: '2026-03-18 01:35'
updated_date: '2026-03-18 05:28'
labels:
- stats
- tokenizer
- bug
milestone: m-1
dependencies: []
references:
- src/core/services/immersion-tracker-service.ts
- src/core/services/immersion-tracker/reducer.ts
- src/core/services/immersion-tracker/storage.ts
- src/core/services/immersion-tracker/query.ts
- src/core/services/immersion-tracker/lifetime.ts
- stats/src/components
- stats/src/lib/yomitan-lookup.ts
priority: medium
ordinal: 100500
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace heuristic immersion stats word counting with Yomitan token counts. Session/media/anime stats should use the exact merged Yomitan token stream as the denominator and display metric, with no whitespace/CJK-character fallback and no active `wordsSeen` concept in the runtime, storage, API, or stats UI.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `recordSubtitleLine` derives session count deltas from Yomitan token arrays instead of `calculateTextMetrics`.
- [x] #2 Active immersion tracking/storage/query code no longer depends on `wordsSeen` / `totalWordsSeen` fields for stats behavior.
- [x] #3 Stats UI labels and lookup-rate copy refer to tokens instead of words where those counts are shown to users.
- [x] #4 Regression tests cover token-count sourcing, zero-count behavior when tokenization payload is absent, and updated stats copy.
- [x] #5 A changelog fragment documents the user-visible stats denominator change.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add failing tracker tests proving subtitle count metrics come from Yomitan token arrays and stay zero when tokenization is absent.
2. Add failing stats UI tests for token-based copy and token-count display helpers.
3. Remove `wordsSeen` from active tracker/session/query/type paths and use `tokensSeen` as the single stats count field.
4. Update stats UI labels and lookup-rate copy from words to tokens.
5. Run targeted verification, then add the changelog fragment and any needed docs update.
<!-- SECTION:PLAN:END -->
## Outcome
<!-- SECTION:OUTCOME:BEGIN -->
Completed. Stats subtitle counts now come directly from Yomitan merged-token counts, `wordsSeen` is removed from the active tracker/storage/query/UI path, token-facing copy is updated, and focused regression coverage plus `bun run typecheck` are green.
<!-- SECTION:OUTCOME:END -->

View File

@@ -0,0 +1,54 @@
---
id: TASK-190
title: Add hover popups for session chart events
status: Done
assignee:
- Codex
created_date: '2026-03-17 22:20'
updated_date: '2026-03-18 05:28'
labels:
- stats
- ui
- bug
milestone: m-1
dependencies: []
references:
- stats/src/components/sessions/SessionDetail.tsx
- stats/src/lib/session-events.ts
- stats/src/hooks/useSessions.ts
- stats/src/lib/api-client.ts
- docs/plans/2026-03-17-session-event-hover-popups-design.md
priority: medium
ordinal: 105500
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add hover/focus popups to session chart event markers so pauses, seeks, lookups, and card-mine events explain themselves inline. Card-mine events should lazy-load available Anki note info and present it in a richer popup with browse affordances.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Hovering or focusing a session-chart marker opens an event-specific popup.
- [x] #2 Pause, seek, and lookup popups show concise event copy derived from marker metadata.
- [x] #3 Card-mine popups lazily fetch and cache Anki note info by note id.
- [x] #4 Card-mine popups show a formatted fallback when note info is missing or still loading.
- [x] #5 Regression tests cover event payload shaping and popup rendering behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add failing tests for event metadata shaping and popup content selection.
2. Extend session-event shaping to parse payload JSON into typed marker metadata.
3. Add lazy note-info fetch/cache state for card-mine markers.
4. Render interactive marker overlay + custom popup in the session detail chart.
5. Run targeted stats/core verification and update this task with the result.
<!-- SECTION:PLAN:END -->
## Outcome
<!-- SECTION:OUTCOME:BEGIN -->
Completed. Session-chart event markers now open event-specific hover/focus popups, including lazy-loaded Anki note info for card-mine events with browse affordances. Verification passed via targeted stats tests, `bun run typecheck`, and the core verification lane in `.tmp/skill-verification/subminer-verify-20260317-222545-CQzyqK`.
<!-- SECTION:OUTCOME:END -->

View File

@@ -0,0 +1,62 @@
---
id: TASK-193
title: Fix session chart event popup position drift
status: Done
assignee:
- Codex
created_date: '2026-03-17 23:55'
updated_date: '2026-03-17 23:59'
labels:
- stats
- ui
- bug
milestone: m-1
dependencies: []
references:
- stats/src/components/sessions/SessionDetail.tsx
- stats/src/components/sessions/SessionEventOverlay.tsx
- stats/src/lib/session-events.ts
priority: medium
ordinal: 105600
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Fix the session timeline event popup trigger positions so hover markers stay aligned with the underlying chart event lines across the full visible time range.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Event popup triggers stay horizontally aligned with chart event lines from session start through session end.
- [x] #2 Alignment logic uses the rendered chart plot area rather than guessed container percentages.
- [x] #3 Regression coverage locks the marker-position projection math.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a failing regression test for marker-position projection with chart offsets.
2. Capture the rendered plot box from Recharts and pass it into the overlay.
3. Position overlay markers in plot-area pixels, rerun targeted stats verification, then record the result.
<!-- SECTION:PLAN:END -->
## Outcome
<!-- SECTION:OUTCOME:BEGIN -->
Completed. Session event hover markers now read the actual Recharts plot-area offset and width, then project marker X positions into plot-area pixels instead of full-container percentages. That keeps popup triggers aligned with the underlying reference lines across long session timelines.
Verification:
- `bun test stats/src/lib/session-events.test.ts stats/src/lib/session-detail.test.tsx stats/src/components/sessions/SessionEventPopover.test.tsx`
- `cd stats && bun run build`
- `bun x prettier --check 'stats/src/components/sessions/SessionDetail.tsx' 'stats/src/components/sessions/SessionEventOverlay.tsx' 'stats/src/lib/session-events.ts' 'stats/src/lib/session-events.test.ts' 'backlog/tasks/task-193 - Fix-session-chart-event-popup-position-drift.md'`
- `bun run typecheck:stats` still fails on pre-existing unrelated errors in `src/components/anime/AnilistSelector.tsx`, `src/components/library/LibraryTab.tsx`, `src/lib/reading-utils.test.ts`, `src/lib/reading-utils.ts`, `src/lib/vocabulary-tab.test.ts`, and `src/lib/yomitan-lookup.test.tsx`
<!-- SECTION:OUTCOME:END -->

View File

@@ -0,0 +1,64 @@
---
id: TASK-195
title: Keep final card-mine OSD result from being overwritten by progress spinner
status: Done
assignee:
- Codex
created_date: '2026-03-18 19:40'
updated_date: '2026-03-18 19:49'
labels:
- anki
- ui
- bug
milestone: m-1
dependencies: []
references:
- src/anki-integration/ui-feedback.ts
- src/anki-integration.ts
- src/anki-integration/card-creation.ts
priority: medium
ordinal: 105610
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
When a card mine finishes, the mpv OSD currently tries to show the final status text but the in-flight Anki progress spinner can immediately overwrite it on the next tick. Stop the spinner first, then show a single-line final result with a success/failure marker and the mined-word notification.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Successful mine/update OSD results render after the spinner is stopped and do not get overwritten by a later spinner tick.
- [x] #2 Failure results that replace the spinner show an `x` marker and stay visible on the same OSD line.
- [x] #3 Regression coverage locks the spinner teardown/result-notification ordering.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a focused failing regression test around the Anki UI-feedback spinner/result helper.
2. Add a helper that stops progress before emitting the final OSD result line with `✓`/`x`.
3. Route mine/update result notifications through that helper, then run targeted verification.
<!-- SECTION:PLAN:END -->
## Outcome
<!-- SECTION:OUTCOME:BEGIN -->
Added a dedicated Anki UI-feedback result helper that force-clears the in-flight spinner state before emitting the final OSD result line. Successful card-update notifications now render as `✓ Updated card: ...`, and sentence-card creation failures now render as `x Sentence card failed: ...` without a later spinner tick reclaiming the line.
Verification:
- `bun test src/anki-integration/ui-feedback.test.ts`
- `bun test src/anki-integration/ui-feedback.test.ts src/anki-integration/note-update-workflow.test.ts src/anki-integration.test.ts src/core/services/mining.test.ts src/main/runtime/mining-actions.test.ts`
- `bun x prettier --check src/anki-integration/ui-feedback.ts src/anki-integration/ui-feedback.test.ts src/anki-integration.ts src/anki-integration/card-creation.ts "backlog/tasks/task-195 - Keep-final-card-mine-OSD-result-from-being-overwritten-by-progress-spinner.md" changes/2026-03-18-mine-osd-spinner-result.md`
- `bun run changelog:lint`
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/anki-integration/ui-feedback.ts src/anki-integration/ui-feedback.test.ts src/anki-integration.ts src/anki-integration/card-creation.ts changes/2026-03-18-mine-osd-spinner-result.md`
- Verifier artifacts: `.tmp/skill-verification/subminer-verify-20260318-194614-uZMrAx/`
<!-- SECTION:OUTCOME:END -->

Some files were not shown because too many files have changed in this diff Show More