mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
Compare commits
25 Commits
b061b265c2
...
v0.9.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
4c95b57885
|
|||
|
242402b253
|
|||
|
61d15f9431
|
|||
|
508864bcbb
|
|||
|
23c54bb01e
|
|||
|
ec667c64e8
|
|||
|
39b2ccad8e
|
|||
|
23815945bf
|
|||
|
9dca83acd9
|
|||
|
55300e2d8c
|
|||
|
28afd15134
|
|||
|
58304757aa
|
|||
|
c95518e94a
|
|||
| 5ee4617607 | |||
|
842008b089
|
|||
|
6f56a0bcf6
|
|||
| 5feed360ca | |||
| c17f0a4080 | |||
| 0317c7f011 | |||
|
13797b5005
|
|||
|
b24d9d7487
|
|||
| 3a01cffc6b | |||
|
eddf6f0456
|
|||
|
f6c024d61e
|
|||
| 6749ff843c |
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@@ -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
5
.gitignore
vendored
@@ -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/*
|
||||
|
||||
|
||||
137
CHANGELOG.md
137
CHANGELOG.md
@@ -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
|
||||
|
||||
290
README.md
290
README.md
@@ -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 />
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[]()
|
||||
[](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.
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://github.com/ksyasuda/SubMiner)
|
||||
[](https://docs.subminer.moe)
|
||||
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||
|
||||
[](./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">
|
||||
|
||||
[](./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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -11,6 +11,7 @@ labels:
|
||||
- stats
|
||||
- database
|
||||
- anilist
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
@@ -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 -->
|
||||
@@ -10,6 +10,7 @@ labels:
|
||||
- immersion
|
||||
- stats
|
||||
- database
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
@@ -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
|
||||
@@ -8,6 +8,7 @@ updated_date: '2026-03-16 05:13'
|
||||
labels:
|
||||
- stats
|
||||
- ui
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
priority: low
|
||||
ordinal: 17500
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 -->
|
||||
@@ -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 -->
|
||||
@@ -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 -->
|
||||
@@ -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
Reference in New Issue
Block a user