mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-09 16:19:25 -07:00
Compare commits
21 Commits
b061b265c2
...
v0.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
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/*
|
||||
|
||||
|
||||
128
CHANGELOG.md
128
CHANGELOG.md
@@ -1,5 +1,133 @@
|
||||
# Changelog
|
||||
|
||||
## 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
|
||||
|
||||
285
README.md
285
README.md
@@ -1,44 +1,166 @@
|
||||
<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</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) |
|
||||
| **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
|
||||
# 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
|
||||
```
|
||||
|
||||
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 +169,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
|
||||
|
||||
|
||||
8
backlog/milestones/m-1 - stats-dashboard.md
Normal file
8
backlog/milestones/m-1 - stats-dashboard.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
id: m-1
|
||||
title: "Stats Dashboard"
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Milestone: Stats Dashboard
|
||||
@@ -4,6 +4,7 @@ title: Add Jellyfin remote-session subtitle streaming to texthooker
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-03-08 03:46'
|
||||
updated_date: '2026-03-18 05:27'
|
||||
labels:
|
||||
- jellyfin
|
||||
- texthooker
|
||||
@@ -19,20 +20,17 @@ references:
|
||||
documentation:
|
||||
- 'https://api.jellyfin.org/'
|
||||
priority: medium
|
||||
ordinal: 1000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Allow SubMiner to follow subtitles from a separate Jellyfin client session, such as a TV app, without requiring local mpv playback. The feature should fetch the active subtitle stream from Jellyfin, map the remote playback position to subtitle cues, and feed the existing subtitle tokenization plus annotated texthooker websocket pipeline so texthooker-only mode can be used while watching on another device.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 User can target a remote Jellyfin session and stream its current subtitle cue into SubMiner's existing subtitle-processing pipeline without launching local Jellyfin playback in mpv.
|
||||
- [ ] #2 Texthooker-only mode can display subtitle updates from the tracked remote Jellyfin session through the existing annotation websocket feed.
|
||||
- [ ] #3 Remote session changes are handled safely: item changes, subtitle-track changes, pause/seek/stop, and session disconnects clear or refresh subtitle state without crashing.
|
||||
|
||||
@@ -4,6 +4,7 @@ title: Add native AI API key secret storage
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-03-08 07:25'
|
||||
updated_date: '2026-03-18 05:27'
|
||||
labels:
|
||||
- ai
|
||||
- config
|
||||
@@ -17,20 +18,17 @@ references:
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/jellyfin-token-store.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
priority: medium
|
||||
ordinal: 2000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Store the shared AI provider API key using the app's native secret-storage pattern so users do not need to keep the OpenRouter key in config files or shell commands.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Users can configure the shared AI provider without storing the API key in config.jsonc.
|
||||
- [ ] #2 The app persists and reloads the shared AI API key using encrypted native secret storage when available.
|
||||
- [ ] #3 Behavior is defined for existing ai.apiKey and ai.apiKeyCommand configs, including compatibility during migration.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
id: TASK-133
|
||||
title: Improve AniList character dictionary parity with upstream guide
|
||||
status: In Progress
|
||||
status: To Do
|
||||
assignee:
|
||||
- OpenCode
|
||||
created_date: '2026-03-08 21:06'
|
||||
updated_date: '2026-03-10 06:18'
|
||||
updated_date: '2026-03-18 05:27'
|
||||
labels:
|
||||
- dictionary
|
||||
- anilist
|
||||
@@ -24,6 +24,7 @@ documentation:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-08-anilist-character-dictionary-parity.md
|
||||
priority: high
|
||||
ordinal: 3000
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
id: TASK-143
|
||||
title: Keep character dictionary auto-sync non-blocking during startup
|
||||
status: Done
|
||||
assignee: []
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-09 01:45'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- dictionary
|
||||
- startup
|
||||
@@ -17,7 +18,7 @@ references:
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/current-media-tokenization-gate.ts
|
||||
priority: high
|
||||
ordinal: 37500
|
||||
ordinal: 144500
|
||||
---
|
||||
|
||||
## Description
|
||||
@@ -33,8 +34,20 @@ Keep character dictionary auto-sync running in parallel during startup without d
|
||||
- [x] #3 Regression coverage verifies auto-sync builds before the gate and only mutates Yomitan after the gate resolves.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a regression test for startup autoplay release surviving delayed mpv readiness or late subtitle refresh after dictionary sync.
|
||||
2. Harden the autoplay-ready release path so paused startup keeps retrying until mpv is actually released or media changes, without resuming user-paused playback later.
|
||||
3. Keep the existing character-dictionary revisit fixes and paused-startup OSD fixes aligned with the autoplay change, then run targeted runtime tests and typecheck.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added a small current-media tokenization gate in main runtime. Media changes reset the gate, the first tokenization-ready event marks it ready, and auto-sync now waits on that gate only before Yomitan dictionary inspection/import/settings updates. Snapshot generation and merged ZIP build still run immediately in parallel.
|
||||
|
||||
2026-03-20: User reports startup remains paused after annotations/tokenization are visible and only resumes after character-dictionary generation/import finishes. Investigating autoplay-ready release regression vs dictionary sync completion refresh.
|
||||
|
||||
2026-03-20: Added startup autoplay retry-budget helper so paused startup retries cover the full plugin gate window instead of only ~2.8s. Verification: bun test src/main/runtime/startup-autoplay-release-policy.test.ts src/main/runtime/character-dictionary-auto-sync.test.ts src/main/runtime/startup-osd-sequencer.test.ts src/main/runtime/character-dictionary-auto-sync-completion.test.ts; bun run typecheck; bun run test:fast; bun run test:env; bun run build; bun run test:smoke:dist; runtime-compat verifier passed at .tmp/skill-verification/subminer-verify-20260320-022106-nM28Nk. Pending real installed-app/mpv validation.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
id: TASK-166
|
||||
title: Harden SubMiner change verification for authoritative agentic runtime checks
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-13 05:19'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
labels:
|
||||
- testing
|
||||
- agents
|
||||
- verification
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/.agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/.agents/skills/subminer-change-verification/SKILL.md
|
||||
documentation:
|
||||
- /home/sudacode/projects/japanese/SubMiner/testing-plan.md
|
||||
- /home/sudacode/projects/japanese/SubMiner/docs-site/development.md
|
||||
ordinal: 22500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Tighten the SubMiner change-verification classifier and verifier so the implementation matches the approved agentic verification plan: authoritative runtime verification must fail closed when unavailable, lane naming should use real-runtime semantics, session and artifact identities must be unique, and the verifier must be safer for parallel agent use.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 The verifier uses `real-runtime` terminology instead of `real-gui` for authoritative runtime verification
|
||||
- [x] #2 Requested authoritative runtime verification fails closed with a non-green outcome when it cannot run, and unknown lanes do not pass open
|
||||
- [x] #3 The verifier allocates a unique session identifier and artifact root that does not rely on second-level timestamp uniqueness alone
|
||||
- [x] #4 The verifier summary/report output includes explicit top-level status and session metadata needed for agent aggregation
|
||||
- [x] #5 The classifier and verifier better reflect runtime-escalation cases for launcher/plugin/socket/runtime-sensitive changes
|
||||
- [x] #6 Regression tests cover the new verifier/classifier behavior
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add regression tests for classifier/verifier behavior before changing the scripts.
|
||||
2. Harden `verify_subminer_change.sh` to use `real-runtime` terminology, fail closed for blocked/unknown authoritative verification, and emit unique session metadata in summaries.
|
||||
3. Update `classify_subminer_diff.sh` and the skill doc to use `real-runtime` escalation language and better flag launcher/plugin/runtime-sensitive paths.
|
||||
4. Run targeted regression tests plus a focused dry-run verifier check, then record outcomes and blockers in the task.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added `scripts/subminer-change-verification.test.ts` to regression-test classifier/verifier behavior around `real-runtime` naming, fail-closed authoritative verification, unknown lanes, and unique session metadata.
|
||||
|
||||
Reworked `verify_subminer_change.sh` to normalize `real-gui` to `real-runtime`, emit unique session IDs and richer summary metadata, block authoritative runtime verification when unavailable, and fail closed for unknown lanes.
|
||||
|
||||
Updated `classify_subminer_diff.sh` to emit `real-runtime-candidate` for launcher/plugin/runtime-sensitive paths, and updated the active skill doc wording to match the new lane terminology.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Hardened the SubMiner change-verification tooling to match the approved agentic verification plan. The verifier now uses `real-runtime` terminology for authoritative runtime verification, preserves compatibility with the deprecated `real-gui` alias, fails closed for unknown lanes, and returns a blocked non-green outcome when requested authoritative runtime verification cannot run. It now allocates a unique session ID and artifact root by default, writes richer session metadata and top-level status into `summary.json`/`summary.txt` plus `reports/summary.*`, and records path selection mode, blockers, and session-local env roots for agent aggregation. The classifier now emits `real-runtime-candidate` for launcher/plugin/runtime-sensitive paths, and the active skill doc uses the same terminology. Verification ran via `bun test scripts/subminer-change-verification.test.ts`, direct dry-run smoke checks for blocked `real-runtime` and failed unknown-lane execution, and a targeted classifier invocation for launcher/plugin paths.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -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:
|
||||
- >-
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: TASK-169
|
||||
title: Cut minor release v0.7.0 for stats and runtime polish
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-19 17:20'
|
||||
updated_date: '2026-03-19 17:31'
|
||||
labels:
|
||||
- release
|
||||
- docs
|
||||
- minor
|
||||
dependencies:
|
||||
- TASK-168
|
||||
references:
|
||||
- package.json
|
||||
- README.md
|
||||
- docs/RELEASING.md
|
||||
- docs-site/changelog.md
|
||||
- CHANGELOG.md
|
||||
- release/release-notes.md
|
||||
priority: high
|
||||
ordinal: 108000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Prepare the next release cut as `v0.7.0`, keeping 0-ver semantics by rolling the accumulated stats/dashboard, launcher, overlay, and stability work into the next minor line instead of a `1.0.0` release.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Repository version metadata is updated to `0.7.0`.
|
||||
- [x] #2 Root release-facing docs are refreshed for the `0.7.0` release cut.
|
||||
- [x] #3 `CHANGELOG.md` and `release/release-notes.md` contain the committed `v0.7.0` section and consumed fragments are removed.
|
||||
- [x] #4 Public changelog/docs surfaces reflect the new release.
|
||||
- [x] #5 Release-prep verification is recorded.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Bump `package.json` to `0.7.0`.
|
||||
2. Refresh release-facing docs: root `README.md`, release guide versioning note, and public docs changelog summary.
|
||||
3. Run `bun run changelog:build --version 0.7.0` to commit release artifacts and consume pending fragments.
|
||||
4. Run release-prep verification (`changelog`, typecheck, tests, docs build if docs-site changed).
|
||||
5. Update this task with notes, verification, and final summary.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Bumped `package.json` from `0.6.5` to `0.7.0` and refreshed the root release-facing copy in `README.md` so the release prep explicitly calls out the new stats/dashboard line plus the background stats daemon commands. Updated `docs/RELEASING.md` with the repo's 0-ver versioning policy and an explicit `--date` reminder after the changelog generator initially stamped `2026-03-20` from UTC instead of the intended local release date `2026-03-19`.
|
||||
|
||||
Ran `bun run changelog:build --version 0.7.0`, which generated `CHANGELOG.md` and `release/release-notes.md` and removed the queued `changes/*.md` fragments for the accumulated stats, launcher, overlay, JLPT, and stability work. Added a curated `v0.7.0` summary to `docs-site/changelog.md` so the public docs changelog stays aligned with the committed root changelog while remaining user-facing.
|
||||
|
||||
Verification:
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh`
|
||||
- `bun run changelog:lint`
|
||||
- `bun run changelog:check --version 0.7.0`
|
||||
- `bun run verify:config-example`
|
||||
- `bun run typecheck`
|
||||
- `bun run test:fast`
|
||||
- `bun run test:env`
|
||||
- `bun run build`
|
||||
- `bun run docs:test`
|
||||
- `bun run docs:build`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Prepared minor release `v0.7.0` as the next 0-ver major line. Version metadata, root changelog, generated release notes, README release copy, release-guide policy, and the public docs changelog are now aligned for the release cut.
|
||||
|
||||
Docs update required: yes. Completed in `README.md`, `docs/RELEASING.md`, and `docs-site/changelog.md`.
|
||||
Changelog fragment required: no new fragment for this task. Existing pending release fragments were consumed into the committed `v0.7.0` changelog section and `release/release-notes.md`.
|
||||
|
||||
Release-prep verification passed across changelog validation, config-example verification, typecheck, fast/env tests, full build, and docs-site test/build.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -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
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
id: TASK-172
|
||||
title: Wire immersion occurrence drilldown into stats API and vocabulary drawer
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-14 12:05'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
labels:
|
||||
- immersion
|
||||
- stats
|
||||
- ui
|
||||
dependencies:
|
||||
- TASK-171
|
||||
ordinal: 18500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Expose the new immersion word/kanji occurrence queries through the stats server and add a right-side Vocabulary drawer that shows recent occurrence rows with paging when a word or kanji is clicked.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Stats server exposes word and kanji occurrence endpoints with bounded recent-first paging.
|
||||
- [x] #2 Stats client/types support loading occurrence pages for a selected word or kanji.
|
||||
- [x] #3 Vocabulary tab opens a right drawer for the selected word/kanji, shows recent occurrences, and supports loading more.
|
||||
- [x] #4 Focused regression coverage exists for the server endpoint contract, and the stats UI still typechecks/builds.
|
||||
- [x] #5 Verification covers the cheapest sufficient backend and stats-UI lanes.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-14: Design approved in-thread. Chosen UX: click a word chip or kanji glyph to open a right-side drawer with recent-first occurrences, initial cap 50, plus “Load more”.
|
||||
2026-03-14: Implemented `/api/stats/vocabulary/occurrences` and `/api/stats/kanji/occurrences` with `limit` + `offset` paging. The drawer uses direct stats HTTP client calls and keeps existing aggregate vocabulary data flow intact.
|
||||
2026-03-14: Verification commands run:
|
||||
- `bun test src/core/services/__tests__/stats-server.test.ts`
|
||||
- `bun run typecheck`
|
||||
- `cd stats && bun run build`
|
||||
- `bun run docs:test`
|
||||
- `bun run docs:build`
|
||||
- `cd stats && bunx tsc --noEmit`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/core/services/stats-server.ts src/core/services/__tests__/stats-server.test.ts`
|
||||
2026-03-14: Verification results:
|
||||
- stats server endpoint tests: passed
|
||||
- root typecheck: passed
|
||||
- stats UI production build: passed
|
||||
- docs-site test/build: passed
|
||||
- `cd stats && bunx tsc --noEmit`: passed after removing stale `hasCoverArt` prop usage in the library stats UI
|
||||
- verifier result: passed (`typecheck`, `test:fast`)
|
||||
- verifier artifacts: `.tmp/skill-verification/subminer-verify-20260314-120900-J0VvB0/`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Wired occurrence drilldown into the stats server and Vocabulary tab. Words and kanji now open a right-side drawer that loads recent occurrence rows 50 at a time and supports “Load more”.
|
||||
|
||||
Added bounded recent-first occurrence endpoints to the stats HTTP API, extended the stats client/type surface, and made word chips plus kanji glyphs selectable with active-state styling.
|
||||
|
||||
Updated the immersion-tracking docs to mention vocabulary occurrence drilldown. Verified with focused stats-server tests, root typecheck, stats UI production build, docs-site test/build, the repo verifier core lane, and a direct `stats` package typecheck after removing the stale `MediaHeader` prop mismatch.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: TASK-177.1
|
||||
title: Fix overview lookup rate metric
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-19 17:46'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- stats
|
||||
- immersion-tracking
|
||||
- yomitan
|
||||
dependencies: []
|
||||
references:
|
||||
- stats/src/components/overview/OverviewTab.tsx
|
||||
- stats/src/lib/dashboard-data.ts
|
||||
- stats/src/lib/yomitan-lookup.ts
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
- src/core/services/stats-server.ts
|
||||
parent_task_id: TASK-177
|
||||
priority: medium
|
||||
ordinal: 132500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Update the stats homepage Tracking Snapshot so Lookup Rate reflects lifetime intentional Yomitan lookups normalized by total tokens seen, matching the newer stats semantics already used in session, media, and anime views.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Overview data exposes the lifetime totals needed to compute global Yomitan lookups per 100 tokens on the homepage
|
||||
- [x] #2 The homepage Tracking Snapshot Lookup Rate card shows Yomitan lookup rate as `X / 100 tokens` with tooltip/copy aligned to that meaning
|
||||
- [x] #3 Automated tests cover the lifetime totals plumbing and homepage summary/rendering change
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Extend overview lifetime hints/query plumbing to include total tokens seen and total intentional Yomitan lookups from finished sessions.
|
||||
2. Add/adjust focused tests first for query hints, stats overview API typing/mocks, and overview summary formatting so the homepage metric fails under old semantics.
|
||||
3. Update the overview summary/card to derive Lookup Rate from lifetime Yomitan lookups per 100 tokens and align tooltip/copy with that meaning.
|
||||
4. Run focused verification on the touched query, stats-server, and stats UI tests; record results and blockers in the task notes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Extended overview lifetime hints to include total tokens seen and total intentional Yomitan lookups from finished sessions so the homepage can compute a true global lookup rate.
|
||||
|
||||
Extracted the homepage Tracking Snapshot into a dedicated presentational component to keep OverviewTab smaller and make the Lookup Rate card copy directly testable.
|
||||
|
||||
Focused verification passed for query hints, IPC/stats overview plumbing, stats server overview response, dashboard summary logic, and homepage snapshot rendering.
|
||||
|
||||
SubMiner verifier core lane artifact: .tmp/skill-verification/subminer-verify-20260319-105320-7FDlwh. `bun run typecheck` passed there; `bun run test:fast` failed for a pre-existing/unrelated environment issue in scripts/update-aur-package.test.ts because scripts/update-aur-package.sh reported `mapfile: command not found`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Homepage Lookup Rate now uses lifetime intentional Yomitan lookups normalized by lifetime tokens seen, matching the existing session/media/anime semantics instead of the old known-word hit-rate metric. I extended overview query hints and API typings with total token and Yomitan lookup totals, updated the overview summary builder to reuse the shared per-100-token formatter, and replaced the inline Tracking Snapshot block with a dedicated component that renders `X / 100 tokens` plus Yomitan-specific tooltip copy.
|
||||
|
||||
Tests added/updated: query hints coverage for the new lifetime totals, stats server and IPC overview fixtures, overview summary assertions, and a dedicated Tracking Snapshot render test for the homepage card text. Focused `bun test` runs passed for those touched areas. Repo-native verifier `--lane core` also passed `bun run typecheck`; its `bun run test:fast` step still fails for the unrelated existing `scripts/update-aur-package.sh: line 71: mapfile: command not found` environment issue.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: TASK-177.2
|
||||
title: Count homepage new words by headword
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-19 19:38'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- stats
|
||||
- immersion-tracking
|
||||
- vocabulary
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
- stats/src/components/overview/TrackingSnapshot.tsx
|
||||
- stats/src/lib/dashboard-data.ts
|
||||
parent_task_id: TASK-177
|
||||
priority: medium
|
||||
ordinal: 130500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Align the homepage New Words metric with the Known Words semantics by counting distinct headwords first seen in the selected window, so inflected or alternate forms of the same word do not inflate the summary.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Homepage new-word counts use distinct headwords by earliest first-seen timestamp instead of counting separate word-form rows
|
||||
- [x] #2 Homepage tooltip/copy reflects the headword-based semantics
|
||||
- [x] #3 Automated tests cover the headword de-duplication behavior and affected overview copy
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Change the new-word aggregate query to group `imm_words` by headword, compute each headword's earliest `first_seen`, and count headwords whose first sighting falls within today/week windows.
|
||||
2. Add failing tests first for the aggregate path so multiple rows sharing a headword only contribute once.
|
||||
3. Update homepage tooltip/copy to say unique headwords first seen today/week.
|
||||
4. Run focused query and stats overview tests, then record verification and any blockers.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Updated the new-word aggregate to count distinct headwords by each headword's earliest `first_seen` timestamp, so multiple inflected/form rows for the same headword contribute only once.
|
||||
|
||||
Adjusted homepage tooltip copy to say unique headwords first seen today/week, keeping the visible card labels unchanged.
|
||||
|
||||
Focused verification passed for the query aggregate and homepage snapshot tests.
|
||||
|
||||
SubMiner verifier core lane artifact: .tmp/skill-verification/subminer-verify-20260319-123942-4intgW. `bun run typecheck` passed there; `bun run test:fast` still fails for the unrelated environment issue in scripts/update-aur-package.test.ts (`mapfile: command not found`).
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Homepage New Words now uses headword-level semantics instead of counting separate `(headword, word, reading)` rows. The aggregate query groups `imm_words` by headword, uses each headword's earliest `first_seen`, and counts headwords first seen today or this week so alternate forms do not inflate the summary. The homepage tooltip copy now explicitly says the metric is based on unique headwords.
|
||||
|
||||
Added focused regression coverage for the de-duplication rule in `getQueryHints` and for the updated homepage tooltip text. Targeted `bun test` runs passed for the touched query and stats UI files. Repo verifier `--lane core` again passed `bun run typecheck`; `bun run test:fast` remains blocked by the unrelated existing `scripts/update-aur-package.sh: line 71: mapfile: command not found` failure.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: TASK-177.3
|
||||
title: Fix attached stats command flow and browser config
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-19 20:15'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- launcher
|
||||
- stats
|
||||
- cli
|
||||
dependencies: []
|
||||
references:
|
||||
- launcher/commands/stats-command.ts
|
||||
- launcher/commands/command-modules.test.ts
|
||||
- launcher/main.test.ts
|
||||
- src/main/runtime/stats-cli-command.ts
|
||||
- src/main/runtime/stats-cli-command.test.ts
|
||||
parent_task_id: TASK-177
|
||||
priority: medium
|
||||
ordinal: 129500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Make `subminer stats` stay attached to the foreground app process instead of routing through daemon startup, while keeping background/stop behavior on the daemon path. Ensure browser opening for stats respects only `stats.autoOpenBrowser` in the normal stats flow.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Default `subminer stats` forwards through the attached foreground stats command path instead of the daemon-start path
|
||||
- [x] #2 `subminer stats --background` and `subminer stats --stop` continue using the daemon control path
|
||||
- [x] #3 Normal stats launches do not open a browser when `stats.autoOpenBrowser` is false, and automated tests cover the launcher/runtime regressions
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add failing launcher tests first so default `stats` expects `--stats` forwarding while `--background` and `--stop` continue to expect daemon control flags.
|
||||
2. Add/adjust runtime stats command tests to prove `stats.autoOpenBrowser=false` suppresses browser opening on the normal attached stats path.
|
||||
3. Patch launcher forwarding logic in `launcher/commands/stats-command.ts` to choose foreground vs daemon flags correctly without changing cleanup handling.
|
||||
4. Run targeted launcher and stats runtime tests, then record verification results and blockers.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Confirmed root cause: launcher default `stats` flow always forwarded `--stats-daemon-start` plus `--stats-daemon-open-browser`, which detached the terminal process and bypassed `stats.autoOpenBrowser` because browser opening happened in daemon control instead of the normal stats CLI handler.
|
||||
|
||||
Updated launcher forwarding so plain `subminer stats` now uses the attached `--stats` path, while explicit `--background` and `--stop` continue using daemon control flags.
|
||||
|
||||
Added launcher regression coverage for the attached/default path and preserved background/stop expectations; added runtime coverage proving `stats.autoOpenBrowser=false` suppresses browser opening on the normal stats path.
|
||||
|
||||
Verifier passed for `launcher-plugin` and `runtime-compat` lanes. Artifact: .tmp/skill-verification/subminer-verify-20260319-131703-ZaAaUV.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed `subminer stats` so the default command now forwards to the normal attached `--stats` app path instead of the daemon-start path. That keeps the foreground process attached to the terminal as expected, while `subminer stats --background` and `subminer stats --stop` still use daemon control. Because the normal stats CLI path already respects `config.stats.autoOpenBrowser`, this also fixes the unwanted browser-open behavior that previously bypassed config via `--stats-daemon-open-browser`.
|
||||
|
||||
Added launcher command and launcher integration regressions for the new forwarding behavior, plus a runtime stats CLI regression that asserts `stats.autoOpenBrowser=false` suppresses browser opening. Verification passed with targeted launcher tests, targeted runtime stats tests, and the SubMiner verifier `launcher-plugin` + `runtime-compat` lanes.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
id: TASK-182.2
|
||||
title: Improve session detail known-word chart scaling
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-19 20:31'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- stats
|
||||
- ui
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionDetail.tsx
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/stats/src/lib/session-detail.test.tsx
|
||||
parent_task_id: TASK-182
|
||||
ordinal: 128500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Adjust the expanded session-detail known-word percentage chart so the vertical range reflects the session's actual percent range instead of always spanning 0-100. Keep the chart easier to read while preserving the percent-based tooltip/legend behavior already used in the stats UI.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Expanded session detail scales the known/unknown percent chart to the session's observed percent range instead of hard-coding a 0-100 top bound
|
||||
- [x] #2 The chart keeps a small headroom above the highest observed known-word percent so the line remains visually readable near the top edge
|
||||
- [x] #3 Automated frontend coverage locks the new percent-domain behavior and preserves existing session-detail rendering
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a focused frontend regression test for the session-detail ratio chart domain calculation, covering a session whose known-word percentage stays in a narrow band below 100% and expecting a dynamic top bound with headroom.
|
||||
2. Update `stats/src/components/sessions/SessionDetail.tsx` to compute a dynamic percent-axis domain and matching ticks for the ratio chart, keeping the lower bound at 0%, adding modest padding above the highest known percentage, rounding to clean tick steps, and capping at 100%.
|
||||
3. Apply the computed percent-axis bounds consistently to the right-side Y axis and the session chart pause overlays so the visual framing stays aligned.
|
||||
4. Run targeted frontend tests and the SubMiner verification helper on the touched files, then record results and any blockers in the task.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented dynamic known-percentage axis scaling in `stats/src/components/sessions/SessionDetail.tsx`: the ratio chart now keeps a 0% floor, uses the highest observed known percentage plus 5 points of headroom for the top bound, rounds that bound up to clean 10-point ticks, caps at 100%, and enables `allowDataOverflow` so the stacked area chart actually honors the tighter domain.
|
||||
|
||||
Added frontend regression coverage in `stats/src/lib/session-detail.test.tsx` for the axis-max helper, covering both a narrow-band session and near-100% cap behavior.
|
||||
|
||||
Added user-visible changelog fragment `changes/2026-03-19-session-detail-chart-scaling.md`.
|
||||
|
||||
Verification: `bun test stats/src/lib/session-detail.test.tsx` passed; `bun run typecheck` passed; `bun run changelog:lint` passed; `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core stats/src/components/sessions/SessionDetail.tsx stats/src/lib/session-detail.test.tsx` ran and passed `typecheck` but failed `bun run test:fast` on a pre-existing unrelated issue in `scripts/update-aur-package.test.ts` / `scripts/update-aur-package.sh` (`mapfile: command not found`). Artifacts: `.tmp/skill-verification/subminer-verify-20260319-134440-JRHAUJ`.
|
||||
|
||||
Docs decision: no internal docs update required; the behavior change is localized UI presentation with no API/workflow change. Changelog decision: yes, required and completed because the fix is user-visible.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Improved expanded session-detail chart readability by replacing the fixed 0-100 known-word percentage axis with a dynamic top bound based on the session’s highest observed known percentage plus modest headroom, rounded to clean ticks and capped at 100%. The ratio chart now also enables `allowDataOverflow` so Recharts preserves the tighter percent domain even though the stacked known/unknown areas sum to 100%.
|
||||
|
||||
Added frontend regression coverage for the new axis-max behavior and a changelog fragment for the user-visible stats fix.
|
||||
|
||||
Verification: `bun test stats/src/lib/session-detail.test.tsx`, `bun run typecheck`, and `bun run changelog:lint` passed. The SubMiner verification helper’s `core` lane also passed `typecheck`, but `bun run test:fast` remains red on a pre-existing unrelated bash-compat failure in `scripts/update-aur-package.test.ts` / `scripts/update-aur-package.sh` (`mapfile: command not found`).
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -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
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
---
|
||||
id: TASK-188
|
||||
title: Refactor stats chart data pipeline to use backend-aggregated series
|
||||
status: In Progress
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-18 00:29'
|
||||
updated_date: '2026-03-18 00:55'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- stats
|
||||
- performance
|
||||
- refactor
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
@@ -21,6 +22,7 @@ references:
|
||||
- stats/src/types/stats.ts
|
||||
- stats/src/lib/dashboard-data.ts
|
||||
priority: medium
|
||||
ordinal: 138500
|
||||
---
|
||||
|
||||
## Description
|
||||
@@ -31,12 +33,12 @@ Reduce long-term dashboard performance debt by moving chart aggregation out of t
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 Stats API exposes chart-oriented aggregated trend data needed by the trends dashboard without requiring raw session lists for those charts.
|
||||
- [ ] #2 The trends dashboard consumes the new aggregated API responses and no longer rebuilds its main chart datasets from raw sessions in the render path.
|
||||
- [ ] #3 Time-range and grouping behavior remain correct for recent and all-time views, with explicit handling that keeps older history performant.
|
||||
- [ ] #4 Existing overview and anime detail charts continue to behave correctly, or are migrated to the shared aggregation path where it reduces debt.
|
||||
- [ ] #5 Tests cover backend aggregation/query behavior and frontend consumption of the new response shapes.
|
||||
- [ ] #6 Internal docs are updated to describe the new stats chart data flow and scaling rationale.
|
||||
- [x] #1 Stats API exposes chart-oriented aggregated trend data needed by the trends dashboard without requiring raw session lists for those charts.
|
||||
- [x] #2 The trends dashboard consumes the new aggregated API responses and no longer rebuilds its main chart datasets from raw sessions in the render path.
|
||||
- [x] #3 Time-range and grouping behavior remain correct for recent and all-time views, with explicit handling that keeps older history performant.
|
||||
- [x] #4 Existing overview and anime detail charts continue to behave correctly, or are migrated to the shared aggregation path where it reduces debt.
|
||||
- [x] #5 Tests cover backend aggregation/query behavior and frontend consumption of the new response shapes.
|
||||
- [x] #6 Internal docs are updated to describe the new stats chart data flow and scaling rationale.
|
||||
<!-- 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,68 @@
|
||||
---
|
||||
id: TASK-191
|
||||
title: 'Assess PR #19 CodeRabbit review follow-ups'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-17 23:15'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- pr-review
|
||||
- stats
|
||||
- immersion-tracker
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/immersion-tracker-service.ts
|
||||
- src/core/services/immersion-tracker-service.test.ts
|
||||
priority: medium
|
||||
ordinal: 139500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Validate the open CodeRabbit review comments on PR #19 against the current branch, implement only the confirmed fixes, and record which bot suggestions are stale or technically incomplete.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Each open CodeRabbit PR #19 comment is validated against the current branch behavior
|
||||
- [x] #2 Confirmed issues are fixed with regression coverage where it fits
|
||||
- [x] #3 Non-actionable or partially-wrong bot guidance is documented explicitly
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Inspect the open CodeRabbit review threads on PR #19 and restate each finding in codebase terms.
|
||||
2. Add failing regression tests for any verified bugs before changing production code.
|
||||
3. Patch the smallest safe service-layer behavior, rerun focused verification, and record which suggestions were accepted versus rejected.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Validated the two open CodeRabbit inline findings on PR #19 against the current branch. Both reported real bugs in `ImmersionTrackerService`, but the first suggestion's exact remediation was incomplete for this codebase.
|
||||
|
||||
`reassignAnimeAnilist` did overwrite `imm_anime.description` with `NULL` when callers omitted `description`. Fixed with a presence-aware SQL update that preserves the existing description when the field is omitted while still allowing explicit `description: null` to clear the stored value. Rejected the bot's `COALESCE(?, description)` prompt because that would silently remove the explicit-clear behavior the API already supports.
|
||||
|
||||
`ensureCoverArt` could return `true` after a fetcher reported success even when no cover-art row/blob was stored, because `undefined !== null` evaluated truthy through optional chaining. Fixed by loading the row into a local variable and requiring a non-null blob.
|
||||
|
||||
Added regression coverage in `src/core/services/immersion-tracker-service.test.ts` for omitted-description preservation, explicit-null clearing, and the no-row `ensureCoverArt` false-positive case.
|
||||
|
||||
Verification passed:
|
||||
- `bun test src/core/services/immersion-tracker-service.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/core/services/immersion-tracker-service.ts src/core/services/immersion-tracker-service.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/core/services/immersion-tracker-service.ts src/core/services/immersion-tracker-service.test.ts`
|
||||
|
||||
Verifier artifact directory: `.tmp/skill-verification/subminer-verify-20260317-231743-wHFNnN`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the open PR #19 CodeRabbit comments and fixed the two confirmed service-layer regressions. `reassignAnimeAnilist` now preserves an existing anime description when callers omit the `description` field but still clears it on explicit `null`, and `ensureCoverArt` no longer reports success when no cover-art row/blob exists after a fetch attempt.
|
||||
|
||||
Both comments were actionable, but one bot-proposed fix was not correct as written for this branch: replacing the description update with `COALESCE(?, description)` would have broken intentional description clearing. Added regression tests for the accepted behaviors and verified the change with the full touched service test file plus the SubMiner `core` verification lane.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: TASK-192
|
||||
title: Fix stale anime cover art after AniList reassignment
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-20 00:12'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- stats
|
||||
- immersion-tracker
|
||||
- anilist
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/immersion-tracker-service.ts
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
- src/core/services/immersion-tracker-service.test.ts
|
||||
priority: medium
|
||||
ordinal: 127500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Fix the stats anime-detail cover image path so reassigning an anime to a different AniList entry replaces the stored cover art bytes instead of keeping the previous image blob under updated metadata.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Reassigning an anime to a different AniList entry stores the new cover art bytes for that anime's videos
|
||||
- [x] #2 Shared blob deduplication still works when multiple videos in the anime use the same new cover image
|
||||
- [x] #3 Focused regression coverage proves stale cover blobs are replaced on reassignment
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing regression test that reassigns an anime twice with different downloaded cover bytes and asserts the resolved cover updates.
|
||||
2. Update cover-art upsert logic so new blob bytes generate a new shared hash instead of reusing an existing hash for the row.
|
||||
3. Run the focused immersion tracker service test file and record the result.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-20: Created during live debugging of a user-reported stale anime profile picture after changing the AniList entry from the stats UI.
|
||||
2026-03-20: Root cause was in `upsertCoverArt(...)`. When a row already had `cover_blob_hash`, a later AniList reassignment with a freshly downloaded cover reused the existing hash instead of hashing the new bytes, so the blob store kept serving the old image while metadata changed.
|
||||
2026-03-20: Added a regression in `src/core/services/immersion-tracker-service.test.ts` that reassigns the same anime twice with different fetched image bytes and asserts the resolved anime cover changes to the second blob while both videos still deduplicate to one shared hash.
|
||||
2026-03-20: Fixed `src/core/services/immersion-tracker/query.ts` so incoming cover blob bytes compute a fresh hash before falling back to an existing row hash. Existing hashes are now reused only when no new bytes were fetched.
|
||||
2026-03-20: Verification commands run:
|
||||
- `bun test src/core/services/immersion-tracker-service.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/core/services/immersion-tracker/query.ts src/core/services/immersion-tracker-service.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/core/services/immersion-tracker/query.ts src/core/services/immersion-tracker-service.test.ts`
|
||||
2026-03-20: Verification results:
|
||||
- focused service test: passed
|
||||
- verifier lane selection: `core`
|
||||
- verifier result: passed (`bun run typecheck`, `bun run test:fast`)
|
||||
- verifier artifacts: `.tmp/skill-verification/subminer-verify-20260320-001433-IZLFqs/`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed stale anime cover art after AniList reassignment by correcting cover-blob hash replacement in the immersion tracker storage layer. Reassignments now store the new fetched image bytes instead of reusing the previous blob hash from the row, while still deduplicating the updated image across videos in the same anime.
|
||||
|
||||
Added focused regression coverage that reproduces the exact failure mode: same anime reassigned twice with different cover downloads, with the second image expected to replace the first. Verified with the touched service test file plus the SubMiner `core` verification lane.
|
||||
<!-- SECTION:FINAL_SUMMARY: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,35 @@
|
||||
---
|
||||
id: TASK-194
|
||||
title: App-owned YouTube subtitle picker flow
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-18 07:52'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/youtube/orchestrator.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/youtube/manual-subs.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer.ts
|
||||
documentation:
|
||||
- /home/sudacode/projects/japanese/SubMiner/youtube.md
|
||||
priority: medium
|
||||
ordinal: 137500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Replace the YouTube subtitle-generation-first flow with an app-owned picker flow that boots mpv paused, opens an overlay track picker, downloads selected subtitles into external subtitle files, and preserves generation as an explicit mode. Keep the existing SubMiner tokenization and annotation pipeline as the downstream consumer of downloaded subtitle files.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Launcher and app expose YouTube subtitle acquisition modes `download` and `generate`, with `download` as the default.
|
||||
- [x] #2 YouTube playback boots mpv paused and presents an overlay selection UI for primary and secondary subtitle choices.
|
||||
- [x] #3 Selected YouTube subtitle tracks are downloaded to external subtitle files and loaded into mpv before playback resumes.
|
||||
- [x] #4 `generate` mode preserves the existing subtitle generation path as an explicit opt-in behavior.
|
||||
- [x] #5 Downloaded YouTube subtitle files integrate with the existing SubMiner subtitle/tokenization/annotation pipeline without regressing current overlay behavior.
|
||||
- [x] #6 Tests cover mode selection, subtitle-track enumeration/selection flow, and the paused bootstrap plus app handoff path.
|
||||
- [x] #7 User-facing config and launcher docs are updated to describe the new modes and default behavior.
|
||||
<!-- AC: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 -->
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
id: TASK-196
|
||||
title: Fix subtitle prefetch cache-key mismatch and active-cue window
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-18 16:05'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-processing-controller.ts
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-prefetch.ts
|
||||
priority: high
|
||||
ordinal: 136500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Investigate and fix file-backed subtitle annotation latency where prefetch should warm upcoming lines but live playback still tokenizes each subtitle line. Likely causes: cache-key mismatch between parsed cue text and mpv `sub-text`, and priority-window selection skipping the currently active cue during mid-line starts/seeks.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Prefetched subtitle entries are reused when live subtitle text differs only by normalization details such as ASS `\N`, newline collapsing, or surrounding whitespace.
|
||||
- [x] #2 Priority-window selection includes the currently active cue when playback starts or seeks into the middle of a cue.
|
||||
- [x] #3 Regression tests cover the cache-hit normalization path and active-cue priority-window behavior.
|
||||
- [x] #4 Verification covers the touched prefetch/controller lane.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add failing regression tests in `subtitle-processing-controller.test.ts` and `subtitle-prefetch.test.ts`.
|
||||
2. Normalize cache keys in the subtitle processing controller so prefetch/live paths share keys.
|
||||
3. Adjust prefetch priority-window selection to include the active cue.
|
||||
4. Run targeted tests, then SubMiner verification lane for touched files.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Outcome
|
||||
|
||||
<!-- SECTION:OUTCOME:BEGIN -->
|
||||
Normalized subtitle cache keys inside the processing controller so prefetched ASS/VTT/live subtitle text variants reuse the same cache entry, and changed priority-window selection to include the currently active cue based on cue end time. Added regression coverage for both paths and verified the change with the `core` lane.
|
||||
<!-- SECTION:OUTCOME:END -->
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: TASK-197
|
||||
title: Eliminate per-line plain subtitle flash on prefetch cache hit
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-18 16:28'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-196
|
||||
references:
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-processing-controller.ts
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/mpv-main-event-actions.ts
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/mpv-main-event-main-deps.ts
|
||||
priority: high
|
||||
ordinal: 135500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Remove the remaining small per-line subtitle annotation delay after prefetch warmup by avoiding the unconditional plain-subtitle broadcast on mpv subtitle-change events when a cached annotated payload already exists.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 On a subtitle cache hit, the mpv subtitle-change path can emit annotated subtitle payload synchronously instead of first broadcasting `tokens: null`.
|
||||
- [x] #2 Cache-miss behavior still preserves immediate plain-text subtitle display while async tokenization runs.
|
||||
- [x] #3 Regression tests cover the controller cache-consume path and the mpv subtitle-change handler cache-hit branch.
|
||||
- [x] #4 Verification covers the touched core/runtime lane.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add failing tests for controller cache consumption and mpv subtitle-change immediate annotated emission.
|
||||
2. Add a controller method that consumes cached subtitle payload synchronously while updating internal latest/emitted state.
|
||||
3. Wire the mpv subtitle-change handler to use the immediate cached payload when present, falling back to the existing plain-text path on misses.
|
||||
4. Run focused tests and the cheapest sufficient verification lane.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Outcome
|
||||
|
||||
<!-- SECTION:OUTCOME:BEGIN -->
|
||||
Added `consumeCachedSubtitle` to the subtitle processing controller so cache hits can be claimed synchronously without reprocessing, then wired the mpv subtitle-change handler to emit cached annotated payloads immediately while preserving the existing plain-text fallback for misses. Verified with focused unit tests plus the `runtime-compat` lane.
|
||||
<!-- SECTION:OUTCOME:END -->
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
id: TASK-199
|
||||
title: Forward launcher log level into mpv plugin script opts
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-18 21:16'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-198
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/aniskip-metadata.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/mpv.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/main.test.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/aniskip-metadata.test.ts
|
||||
priority: medium
|
||||
ordinal: 134500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Make `subminer --log-level=debug ...` reach the mpv plugin auto-start path by forwarding the launcher log level into `--script-opts`, so plugin-started overlay and texthooker subprocesses inherit debug logging.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Launcher mpv playback includes `subminer-log_level=<level>` in `--script-opts` when a non-info CLI log level is used.
|
||||
- [x] #2 Detached idle mpv launch uses the same script-opt forwarding.
|
||||
- [x] #3 Regression tests cover launcher script-opt forwarding.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing launcher regression test that captures mpv argv and expects `subminer-log_level=debug` inside `--script-opts`.
|
||||
2. Extend the shared script-opt builder to accept launcher log level and emit `subminer-log_level` for non-info runs.
|
||||
3. Reuse that builder in both normal mpv playback and detached idle mpv launch.
|
||||
4. Run focused launcher tests and launcher-plugin verification.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Outcome
|
||||
|
||||
<!-- SECTION:OUTCOME:BEGIN -->
|
||||
Forwarded launcher log level into mpv plugin script opts via the shared builder and reused that builder for idle mpv launch. `subminer --log-level=debug ...` now gives the plugin `opts.log_level=debug`, so auto-started overlay and texthooker subprocesses include `--log-level debug` and the tokenizer timing logs can actually appear in the app log.
|
||||
<!-- SECTION:OUTCOME:END -->
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: TASK-200
|
||||
title: 'Address latest PR #19 CodeRabbit follow-ups'
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-19 07:18'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- pr-review
|
||||
- anki-integration
|
||||
- launcher
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- launcher/mpv.test.ts
|
||||
- src/anki-integration.ts
|
||||
- src/anki-integration/card-creation.ts
|
||||
- src/anki-integration/runtime.ts
|
||||
- src/anki-integration/known-word-cache.ts
|
||||
priority: medium
|
||||
ordinal: 133500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Validate the latest 2026-03-19 CodeRabbit review round on PR #19, implement only the confirmed fixes, and verify the touched launcher and Anki integration paths.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Each latest-round PR #19 CodeRabbit inline comment is validated against the current branch and classified as actionable or not warranted
|
||||
- [x] #2 Confirmed correctness issues in launcher and Anki integration code are fixed with focused regression coverage where practical
|
||||
- [x] #3 Targeted verification runs for the touched areas and the task notes record what changed versus what was rejected
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Validate the five inline comments from the 2026-03-19 CodeRabbit PR #19 review against current launcher and Anki integration code.
|
||||
2. Add or extend focused tests for any confirmed launcher env-sandbox, notification-state, AVIF lead-in propagation, or known-word-cache lifecycle/scope regressions.
|
||||
3. Apply the smallest safe fixes in `launcher/mpv.test.ts`, `src/anki-integration.ts`, `src/anki-integration/card-creation.ts`, `src/anki-integration/runtime.ts`, and `src/anki-integration/known-word-cache.ts` as needed.
|
||||
4. Run targeted unit tests plus the SubMiner verification helper on the touched files, then record which comments were accepted or rejected in task notes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Validated the five latest inline comments from CodeRabbit review `3973222927` on PR #19.
|
||||
|
||||
Accepted fixes:
|
||||
- Hardened the three `findAppBinary` launcher tests against host leakage by sandboxing `SUBMINER_APPIMAGE_PATH` / `SUBMINER_BINARY_PATH` and stubbing executable checks so `/opt` and PATH resolution are deterministic.
|
||||
- `showNotification()` now marks OSD/both updates as failed when `errorSuffix` is present instead of always rendering a success marker.
|
||||
- `applyRuntimeConfigPatch()` now avoids starting or stopping known-word cache lifecycle work while the runtime is stopped, while still clearing cached state when highlighting is disabled.
|
||||
- Extracted shared known-word cache lifecycle helpers and switched the persisted cache identity to the same lifecycle config used by runtime restart detection, so changes to `fields.word`, per-deck field mappings, or refresh interval invalidate stale cache state correctly.
|
||||
|
||||
Rejected fix:
|
||||
- The `createSentenceCard()` AVIF lead-in comment was technically incomplete for this branch. There is no current caller that computes an `animatedLeadInSeconds` input for sentence-card creation, and the existing lead-in resolver depends on note media fields that do not exist before the new card's media is generated.
|
||||
|
||||
Regression coverage added:
|
||||
- `src/anki-integration.test.ts` partial-failure OSD result marker.
|
||||
- `src/anki-integration/runtime.test.ts` stopped-runtime known-word lifecycle guards.
|
||||
- `src/anki-integration/known-word-cache.test.ts` cache invalidation when `fields.word` or per-deck field mappings change.
|
||||
|
||||
Verification:
|
||||
- `bun test src/anki-integration/runtime.test.ts`
|
||||
- `bun test src/anki-integration/known-word-cache.test.ts`
|
||||
- `bun test src/anki-integration.test.ts --test-name-pattern 'marks partial update notifications as failures in OSD mode'`
|
||||
- `bun test launcher/mpv.test.ts --test-name-pattern 'findAppBinary resolves ~/.local/bin/SubMiner.AppImage when it exists|findAppBinary resolves /opt/SubMiner/SubMiner.AppImage when ~/.local/bin candidate does not exist|findAppBinary finds subminer on PATH when AppImage candidates do not exist'`
|
||||
- `bun test src/anki-integration.test.ts src/anki-integration/runtime.test.ts src/anki-integration/known-word-cache.test.ts launcher/mpv.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh launcher/mpv.test.ts src/anki-integration.ts src/anki-integration/runtime.ts src/anki-integration/known-word-cache.ts src/anki-integration/runtime.test.ts src/anki-integration/known-word-cache.test.ts src/anki-integration.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane launcher-plugin --lane core launcher/mpv.test.ts src/anki-integration.ts src/anki-integration/runtime.ts src/anki-integration/known-word-cache.ts src/anki-integration/runtime.test.ts src/anki-integration/known-word-cache.test.ts src/anki-integration.test.ts`
|
||||
|
||||
Verifier result:
|
||||
- `launcher-plugin` lane passed (`test:launcher:smoke:src`, `test:plugin:src`).
|
||||
- `core/typecheck` passed.
|
||||
- `core/test-fast` failed for an unrelated existing environment issue in `scripts/update-aur-package.test.ts`: `scripts/update-aur-package.sh: line 71: mapfile: command not found` under the local macOS Bash environment.
|
||||
- Verifier artifacts: `.tmp/skill-verification/subminer-verify-20260319-002617-UgpKUy`
|
||||
|
||||
Classification: actionable and fixed -> `launcher/mpv.test.ts` env leakage hardening, `src/anki-integration.ts` partial-failure OSD marker, `src/anki-integration/runtime.ts` started-guard for known-word lifecycle calls, `src/anki-integration/known-word-cache.ts` cache identity alignment with runtime lifecycle config.
|
||||
|
||||
Classification: not warranted as written -> `src/anki-integration/card-creation.ts` lead-in threading comment. No current `createSentenceCard()` caller computes or owns an `animatedLeadInSeconds` value, and the existing lead-in helper derives from preexisting note media fields, so blindly adding an optional parameter would not fix a real branch behavior bug.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed four confirmed PR #19 latest-round CodeRabbit issues locally: deterministic launcher `findAppBinary` tests, correct partial-failure OSD result markers, started-state guards around known-word cache lifecycle restarts, and shared known-word cache identity logic so field-mapping changes invalidate stale cache state. Added focused regression coverage for each confirmed behavior.
|
||||
|
||||
One comment was intentionally not applied: the `createSentenceCard()` AVIF lead-in suggestion does not match the current branch architecture because no caller computes that value today and the existing resolver requires preexisting note media fields. Verification is green for all touched targeted tests plus the launcher-plugin/core typecheck lanes; the only remaining red is an unrelated existing `test:fast` failure in `scripts/update-aur-package.test.ts` caused by `mapfile` being unavailable in the local Bash environment.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
id: TASK-201
|
||||
title: Suppress repeated macOS overlay loading OSD during fullscreen tracker flaps
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-19 18:47'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- macos
|
||||
- overlay
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-visibility.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/main/overlay-visibility-runtime.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main/state.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-visibility.test.ts
|
||||
priority: high
|
||||
ordinal: 131500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Reduce macOS fullscreen annoyance where the visible overlay briefly loses tracking and re-shows the `Overlay loading...` OSD even though the overlay runtime is already initialized and no new instance is launching. Keep the first startup/loading feedback, but suppress repeat loading notifications caused by subsequent tracker churn during fullscreen enter/leave or focus flaps.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 The first macOS visible-overlay load still shows the existing `Overlay loading...` OSD when tracker data is not yet ready.
|
||||
- [x] #2 Repeated macOS tracker flaps after the overlay has already recovered do not immediately re-show `Overlay loading...` on every loss/recovery cycle.
|
||||
- [x] #3 Focused regression tests cover the repeated tracker-loss/recovery path and preserve the initial-load notification behavior.
|
||||
- [x] #4 The change does not alter overlay runtime bootstrap or single-instance behavior; only notification suppression behavior changes.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add focused failing regressions in `src/core/services/overlay-visibility.test.ts` that preserve the first macOS `Overlay loading...` OSD and suppress an immediate second OSD after tracker recovery/loss churn.
|
||||
2. Extend the overlay-visibility state/runtime plumbing with a small macOS loading-OSD suppression state so tracker flap retries can be rate-limited without touching overlay bootstrap or single-instance logic.
|
||||
3. Reset the suppression when the user explicitly hides the visible overlay so intentional hide/show retries can still surface first-load feedback.
|
||||
4. Run focused verification for the touched overlay visibility/runtime tests and update the task with results.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added optional loading-OSD suppression hooks to `src/core/services/overlay-visibility.ts` so macOS can rate-limit repeated `Overlay loading...` notifications without changing overlay bootstrap behavior.
|
||||
|
||||
Implemented service-local suppression state in `src/main/overlay-visibility-runtime.ts` with a 30s cooldown and explicit reset when the visible overlay is manually hidden, so fullscreen tracker flaps stay quiet but intentional hide/show retries can still show loading feedback.
|
||||
|
||||
Added focused regressions in `src/core/services/overlay-visibility.test.ts` for `loss -> recover -> immediate loss` suppression and for manual hide resetting suppression.
|
||||
|
||||
Verification: `bun test src/core/services/overlay-visibility.test.ts`; `bun test src/main/runtime/overlay-visibility-runtime-main-deps.test.ts src/main/runtime/overlay-visibility-runtime.test.ts`; `bun run typecheck`; `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane runtime-compat src/core/services/overlay-visibility.ts src/main/overlay-visibility-runtime.ts src/core/services/overlay-visibility.test.ts` -> passed. Real-runtime lane skipped: change is notification suppression logic and cheap/runtime-compat coverage was sufficient for this scoped behavior change; no live mpv/macOS fullscreen session was run in this turn.
|
||||
|
||||
Docs update required: no. Changelog fragment required: yes; added `changes/2026-03-19-overlay-loading-osd-fullscreen-flaps.md`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Reduced repeated macOS `Overlay loading...` popups caused by fullscreen tracker flap churn without touching overlay bootstrap or single-instance behavior. `src/core/services/overlay-visibility.ts` now accepts optional suppression hooks around the loading OSD path, and `src/main/overlay-visibility-runtime.ts` uses service-local state to rate-limit that OSD for 30 seconds while resetting the suppression when the visible overlay is explicitly hidden. Added focused regressions in `src/core/services/overlay-visibility.test.ts` to preserve the first-load notification, suppress immediate repeat notifications after tracker recovery/loss churn, and keep manual hide/show retries able to surface the loading OSD again. Added changelog fragment `changes/2026-03-19-overlay-loading-osd-fullscreen-flaps.md`. Verification passed with targeted overlay tests, typecheck, and the `runtime-compat` verifier lane; live macOS/mpv fullscreen runtime validation was not run in this turn.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
id: TASK-202
|
||||
title: Use ended session media position for anime episode progress
|
||||
status: Done
|
||||
assignee:
|
||||
- Codex
|
||||
created_date: '2026-03-19 14:55'
|
||||
updated_date: '2026-03-19 17:36'
|
||||
labels:
|
||||
- stats
|
||||
- ui
|
||||
- bug
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- stats/src/components/anime/EpisodeList.tsx
|
||||
- stats/src/types/stats.ts
|
||||
- src/core/services/immersion-tracker/session.ts
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
- src/core/services/immersion-tracker/storage.ts
|
||||
priority: medium
|
||||
ordinal: 105720
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The anime episode list currently computes the `Progress` column from cumulative `totalActiveMs / durationMs`, which can exceed the intended watch-position meaning after rewatches or repeated sessions. Persist the playback position at the time a session ends and drive episode progress from that stored stop position instead.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Session finalization persists the playback position reached when the session ended.
|
||||
- [x] #2 Anime episode queries expose the most recent ended-session media position for each episode.
|
||||
- [x] #3 Episode-list progress renders from ended media position instead of cumulative active watch time.
|
||||
- [x] #4 Regression coverage locks storage/query/UI behavior for the new progress source.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Add failing regression coverage for persisted ended media position and episode progress rendering.
|
||||
2. Add `ended_media_ms` to the immersion-session schema and persist `lastMediaMs` when ending a session.
|
||||
3. Thread the new field through episode queries/types and render episode progress from `endedMediaMs / durationMs`.
|
||||
4. Run targeted verification plus typecheck, then record the outcome.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Outcome
|
||||
|
||||
<!-- SECTION:OUTCOME:BEGIN -->
|
||||
|
||||
Added nullable `ended_media_ms` storage to immersion sessions, persisted `lastMediaMs` when sessions finalize, and exposed the most recent ended-session media position through anime episode queries/types. The anime episode list now renders `Progress` from `endedMediaMs / durationMs` instead of cumulative active watch time, so rewatches no longer inflate the displayed percentage.
|
||||
|
||||
Verification:
|
||||
|
||||
- `bun test src/core/services/immersion-tracker/storage-session.test.ts`
|
||||
- `bun test src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
- `bun test stats/src/lib/yomitan-lookup.test.tsx stats/src/lib/stats-ui-navigation.test.tsx`
|
||||
- `bun run typecheck`
|
||||
- `bun run changelog:lint`
|
||||
- `bun x prettier --check 'src/core/services/immersion-tracker/types.ts' 'src/core/services/immersion-tracker/storage.ts' 'src/core/services/immersion-tracker/session.ts' 'src/core/services/immersion-tracker/query.ts' 'src/core/services/immersion-tracker/storage-session.test.ts' 'src/core/services/immersion-tracker/__tests__/query.test.ts' 'stats/src/types/stats.ts' 'stats/src/components/anime/EpisodeList.tsx' 'stats/src/lib/yomitan-lookup.test.tsx' 'stats/src/lib/stats-ui-navigation.test.tsx' 'backlog/tasks/task-202 - Use-ended-session-media-position-for-anime-episode-progress.md' 'changes/2026-03-19-stats-ended-media-progress.md'`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core 'src/core/services/immersion-tracker/types.ts' 'src/core/services/immersion-tracker/storage.ts' 'src/core/services/immersion-tracker/session.ts' 'src/core/services/immersion-tracker/query.ts' 'src/core/services/immersion-tracker/storage-session.test.ts' 'src/core/services/immersion-tracker/__tests__/query.test.ts' 'stats/src/types/stats.ts' 'stats/src/components/anime/EpisodeList.tsx' 'stats/src/lib/yomitan-lookup.test.tsx' 'stats/src/lib/stats-ui-navigation.test.tsx' 'backlog/tasks/task-202 - Use-ended-session-media-position-for-anime-episode-progress.md' 'changes/2026-03-19-stats-ended-media-progress.md'`
|
||||
- Verifier artifacts: `.tmp/skill-verification/subminer-verify-20260319-173511-AV7kUg/`
|
||||
|
||||
<!-- SECTION:OUTCOME:END -->
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: TASK-203
|
||||
title: Restore known and JLPT annotation for reading-mismatch subtitle tokens
|
||||
status: Done
|
||||
assignee:
|
||||
- Codex
|
||||
created_date: '2026-03-19 18:25'
|
||||
updated_date: '2026-03-19 18:25'
|
||||
labels:
|
||||
- subtitle
|
||||
- bug
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/tokenizer/annotation-stage.ts
|
||||
- src/core/services/tokenizer/annotation-stage.test.ts
|
||||
priority: medium
|
||||
ordinal: 105721
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Some subtitle tokens lose both known-word coloring and JLPT underline even though the popup resolves a valid dictionary term. Repro example: `大体` in `大体 僕だって困ってたんですよ!` can be known via kana-only Anki data (`だいたい`) while JLPT lookup should still resolve from the kanji surface/headword.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Subtitle annotation can mark a token known via its reading when the configured headword/surface lookup misses.
|
||||
- [x] #2 JLPT eligibility no longer drops valid kanji terms just because their reading contains repeated kana patterns.
|
||||
- [x] #3 Regression coverage locks the combined known + JLPT case for `大体`.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Outcome
|
||||
|
||||
<!-- SECTION:OUTCOME:BEGIN -->
|
||||
|
||||
Known-word annotation now falls back to the token reading after the configured headword/surface lookup misses, so kana-only known-card entries still light up matching subtitle tokens. JLPT eligibility now ignores repeated-kana noise checks on the reading when a real surface/headword is present, which preserves JLPT tagging for words like `大体`.
|
||||
|
||||
Verification:
|
||||
|
||||
- `bun test src/core/services/tokenizer/annotation-stage.test.ts`
|
||||
|
||||
<!-- SECTION:OUTCOME:END -->
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: TASK-204
|
||||
title: Make known-word cache incremental and avoid full rebuilds
|
||||
status: Done
|
||||
assignee:
|
||||
- Codex
|
||||
created_date: '2026-03-19 19:05'
|
||||
updated_date: '2026-03-19 19:12'
|
||||
labels:
|
||||
- anki
|
||||
- cache
|
||||
- performance
|
||||
dependencies: []
|
||||
references:
|
||||
- src/anki-integration/known-word-cache.ts
|
||||
- src/anki-integration.ts
|
||||
- src/config/resolve/anki-connect.ts
|
||||
- src/config/definitions/defaults-integrations.ts
|
||||
priority: high
|
||||
ordinal: 105722
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Replace the known-word cache rebuild behavior with incremental synchronization. Startup should load existing cache state without immediately pulling all tracked Anki notes. Config-timed sync should reconcile adds, deletes, and in-place field edits against cached per-note state. Mined cards should optionally append their extracted words immediately after mining, enabled by default. Full rebuild should remain available only through explicit doctor tooling.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Known-word cache startup no longer performs an automatic full rebuild.
|
||||
- [x] #2 Config-timed sync incrementally reconciles note additions, deletions, and edited word fields for the tracked known-word deck scope.
|
||||
- [x] #3 Newly mined cards update the known-word cache immediately when the new config flag is enabled, and skip that fast path when disabled.
|
||||
- [x] #4 Persisted cache state remains usable by stats endpoints that read the `words` set from disk.
|
||||
- [x] #5 Regression tests cover startup behavior, incremental sync diffs, and the new config flag.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Outcome
|
||||
|
||||
<!-- SECTION:OUTCOME:BEGIN -->
|
||||
|
||||
Known-word cache startup now loads persisted state and schedules sync based on refresh timing instead of wiping and rebuilding immediately. Persisted cache state now includes per-note word snapshots so timed refreshes can remove deleted notes, update edited notes, and keep the global `words` set stable for stats consumers. Added `ankiConnect.knownWords.addMinedWordsImmediately`, default `true`, so newly mined cards can update the cache immediately without waiting for the next timed sync.
|
||||
|
||||
Verification:
|
||||
|
||||
- `bun test src/anki-integration/known-word-cache.test.ts`
|
||||
- `bun test src/config/resolve/anki-connect.test.ts src/config/config.test.ts`
|
||||
- `bun test src/anki-integration.test.ts src/anki-integration/runtime.test.ts src/core/services/__tests__/stats-server.test.ts`
|
||||
- `bun run test:config:src`
|
||||
- `bun run typecheck`
|
||||
- `bun run test:fast`
|
||||
- `bun run test:env`
|
||||
- `bun run build`
|
||||
- `bun run test:smoke:dist`
|
||||
|
||||
<!-- SECTION:OUTCOME:END -->
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: TASK-204.1
|
||||
title: Restore stale-only startup known-word cache refresh
|
||||
status: Done
|
||||
assignee:
|
||||
- '@Codex'
|
||||
created_date: '2026-03-20 02:52'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- anki
|
||||
- cache
|
||||
- bug
|
||||
dependencies: []
|
||||
references:
|
||||
- src/anki-integration/known-word-cache.ts
|
||||
- src/anki-integration/known-word-cache.test.ts
|
||||
- docs/plans/2026-03-19-known-word-cache-incremental-sync-design.md
|
||||
parent_task_id: TASK-204
|
||||
priority: high
|
||||
ordinal: 124500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Follow up on the incremental known-word cache change so startup still performs a refresh when the persisted cache is older than the configured refresh interval, while leaving fresh persisted state untouched.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Startup refreshes known words immediately when persisted cache state is stale for the configured interval.
|
||||
- [x] #2 Startup skips the immediate refresh when persisted cache state is still fresh.
|
||||
- [x] #3 Regression tests cover both stale and fresh startup paths.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add focused known-word cache lifecycle tests that distinguish fresh startup state from stale startup state and verify the stale path currently fails.
|
||||
2. Update startup scheduling in src/anki-integration/known-word-cache.ts so persisted cache still loads immediately, but startup only triggers an immediate refresh when the cache is stale for the configured interval or the cache scope/config changed.
|
||||
3. Run focused known-word cache tests and targeted SubMiner verification for the touched cache/runtime lane, then update the task with results.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Verified current lifecycle behavior: fresh persisted known-word cache already skips immediate startup refresh when the cache scope/config matches; stale persisted cache already refreshes immediately. Added regression coverage for both startup paths plus a proxy integration test showing addNote responses return without waiting for background enrichment.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Added regression coverage for known-word cache startup behavior and proxy response timing. The cache tests now lock in the intended lifecycle: fresh persisted state stays load-only on startup, while stale persisted state refreshes immediately. Added a proxy integration test proving addNote responses return without waiting for background enrichment. Verification: targeted Bun tests passed (`bun test src/anki-connect.test.ts src/anki-integration/anki-connect-proxy.test.ts src/anki-integration/known-word-cache.test.ts src/anki-integration/note-update-workflow.test.ts src/anki-integration/runtime.test.ts`) and direct `bun run test:fast` passed. The `subminer-change-verification` helper repeatedly reported `bun run test:fast` as failed in its isolated lane despite the direct command passing, so that helper lane remains a flaky/blocking verification artifact rather than a reproduced code failure.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: TASK-205
|
||||
title: 'Address PR #19 Claude frontend review follow-ups'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-20 02:41'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels: []
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- stats/src/components/vocabulary/VocabularyTab.tsx
|
||||
- stats/src/hooks/useSessions.ts
|
||||
- stats/src/hooks/useTrends.ts
|
||||
priority: medium
|
||||
ordinal: 126500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Assess Claude's latest PR #19 review, apply any valid frontend fixes from that review batch, and verify the stats dashboard behavior stays unchanged aside from the targeted performance and error-handling improvements.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 VocabularyTab avoids recomputing expensive known-word and summary aggregates on unrelated rerenders while preserving current displayed values.
|
||||
- [x] #2 useSessions and useSessionDetail normalize rejected values into stable string errors without throwing from the catch handler.
|
||||
- [x] #3 Targeted tests cover the addressed review items and pass locally.
|
||||
- [x] #4 Any user-facing docs remain accurate after the changes.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add focused tests that fail on the current branch for the two valid Claude findings: render-time aggregate recomputation in VocabularyTab and unsafe non-Error rejection handling in useSessions/useSessionDetail.
|
||||
2. Update VocabularyTab to memoize the expensive summary and known-word aggregate calculations off the existing filteredWords/kanji/knownWords inputs without changing rendered values.
|
||||
3. Normalize hook error handling to convert unknown rejection values into stable strings, matching the existing useTrends pattern.
|
||||
4. Run the targeted stats/frontend test lane, verify no docs changes are needed, and record results in task notes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Validated Claude's latest PR #19 review comment from 2026-03-20 and narrowed it to two valid frontend follow-ups: memoized VocabularyTab aggregates and non-Error-safe session hook error handling.
|
||||
|
||||
Added focused regression tests in stats/src/lib/vocabulary-tab.test.ts and stats/src/hooks/useSessions.test.ts before patching the implementation.
|
||||
|
||||
Verification: `cd stats && bun test src/lib/vocabulary-tab.test.ts src/hooks/useSessions.test.ts` passed; `bun run format:check:stats` passed.
|
||||
|
||||
Project-native verifier (`.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core ...`) passed root `bun run typecheck` and failed at `bun run test:fast` due an unrelated existing failure in `scripts/update-aur-package.test.ts` (`mapfile: command not found`). Artifact: `.tmp/skill-verification/subminer-verify-20260319-194525-vxVD9V`.
|
||||
|
||||
No user-facing docs changes were needed because the fixes only affect render-time memoization and error normalization.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed Claude's latest PR #19 review and applied the two valid follow-ups. `stats/src/components/vocabulary/VocabularyTab.tsx` now memoizes `buildVocabularySummary(filteredWords, kanji)` and the known-word count so unrelated rerenders do not rescan the filtered vocabulary list. `stats/src/hooks/useSessions.ts` now exports a small `toErrorMessage` helper and uses it in both `useSessions` and `useSessionDetail`, preventing `.catch()` handlers from throwing when a promise rejects with a non-`Error` value.
|
||||
|
||||
Added targeted regressions in `stats/src/lib/vocabulary-tab.test.ts` and `stats/src/hooks/useSessions.test.ts` to lock in the memoization shape and error normalization behavior. Verification passed for `cd stats && bun test src/lib/vocabulary-tab.test.ts src/hooks/useSessions.test.ts` and `bun run format:check:stats`. The repo-native verification wrapper for the classified `core` lane also passed root `bun run typecheck`, but `bun run test:fast` is currently blocked by an unrelated existing failure in `scripts/update-aur-package.test.ts` (`mapfile: command not found`); artifacts are recorded under `.tmp/skill-verification/subminer-verify-20260319-194525-vxVD9V`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: TASK-206
|
||||
title: 'Assess latest PR #19 CodeRabbit review comments'
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-20 02:51'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- pr-review
|
||||
- launcher
|
||||
- anki-integration
|
||||
- docs
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- launcher/commands/command-modules.test.ts
|
||||
- launcher/commands/stats-command.ts
|
||||
- launcher/config/cli-parser-builder.ts
|
||||
- launcher/mpv.ts
|
||||
- README.md
|
||||
- src/anki-integration.ts
|
||||
- src/anki-integration/known-word-cache.ts
|
||||
priority: medium
|
||||
ordinal: 125500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Validate the latest 2026-03-20 CodeRabbit review round on PR #19 against the current branch, implement only the confirmed fixes, and record which bot suggestions are stale, incorrect, or incomplete.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Each latest-round 2026-03-20 CodeRabbit inline comment on PR #19 is validated against current branch behavior and classified as actionable or not warranted
|
||||
- [x] #2 Confirmed correctness issues in launcher, Anki integration, and docs are fixed with focused regression coverage where practical
|
||||
- [x] #3 Targeted verification runs for the touched areas succeed or remaining unrelated failures are documented in task notes
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Pull the 2026-03-20 CodeRabbit review threads from PR #19 and validate each comment against the current branch, separating real issues from stale or incomplete bot guidance.
|
||||
2. For each confirmed behavior bug, add or extend a focused failing test before changing production code; keep docs-only fixes scoped to the exact markdownlint/install issue.
|
||||
3. Patch the smallest safe fixes in launcher, README, and Anki integration code, taking care not to overwrite unrelated local edits.
|
||||
4. Run targeted tests and relevant SubMiner verification lanes for touched files, then record accepted versus rejected review comments in task notes and summary.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Validated the 2026-03-20 CodeRabbit PR #19 round as eight actionable items: one launcher test-name mismatch, three launcher behavior/test fixes, two README markdown/install fixes, one dead-code cleanup in Anki integration, and one real known-word cache deck-scoping bug.
|
||||
|
||||
Known-word cache review comment was correct in substance but needed a branch-specific fix: preserve deck->field scoping by querying per deck and carrying the allowed field list per note, rather than changing `notesInfo` shape.
|
||||
|
||||
Verification passed for targeted tests plus verifier docs/launcher-plugin lanes. Core verifier failed on unrelated pre-existing typecheck worktree state in `src/anki-integration/anki-connect-proxy.test.ts` (`TS2349` at line 395, `releaseProcessing?.()`), which is outside this task's touched files.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the latest 2026-03-20 CodeRabbit review round on PR #19 and applied all eight confirmed action items. Launcher behavior now surfaces non-zero stats-process exits after the startup handshake, rejects cleanup-only stats flags unless `cleanup` is selected, preserves empty quoted `mpv` args, and has updated regression coverage for each case. The known-word cache now preserves deck-specific field mappings during refresh by querying configured decks separately and extracting only the fields assigned to each deck; the unused `getPreferredWordValue` wrapper in `src/anki-integration.ts` was removed.
|
||||
|
||||
Documentation/test hygiene fixes also landed: the README platform badge no longer has an empty link target, Linux AppImage install instructions create `~/.local/bin` before downloads, the stats-command timing test was renamed to match actual behavior, and `launcher/picker.test.ts` now restores `XDG_DATA_HOME` safely while forcing Linux-path expectations explicitly so the file passes on macOS hosts.
|
||||
|
||||
Verification run:
|
||||
- `bun test launcher/commands/command-modules.test.ts`
|
||||
- `bun test launcher/parse-args.test.ts`
|
||||
- `bun test launcher/mpv.test.ts`
|
||||
- `bun test launcher/picker.test.ts`
|
||||
- `bun test src/anki-integration/known-word-cache.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh README.md launcher/commands/command-modules.test.ts launcher/commands/stats-command.ts launcher/config/cli-parser-builder.ts launcher/mpv.test.ts launcher/mpv.ts launcher/parse-args.test.ts launcher/picker.test.ts src/anki-integration.ts src/anki-integration/known-word-cache.test.ts src/anki-integration/known-word-cache.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane docs --lane launcher-plugin --lane core README.md launcher/commands/command-modules.test.ts launcher/commands/stats-command.ts launcher/config/cli-parser-builder.ts launcher/mpv.test.ts launcher/mpv.ts launcher/parse-args.test.ts launcher/picker.test.ts src/anki-integration.ts src/anki-integration/known-word-cache.test.ts src/anki-integration/known-word-cache.ts`
|
||||
|
||||
Verifier results:
|
||||
- `docs` lane passed (`docs:test`, `docs:build`)
|
||||
- `launcher-plugin` lane passed (`test:launcher:smoke:src`, `test:plugin:src`)
|
||||
- `core/typecheck` failed on unrelated existing worktree changes in `src/anki-integration/anki-connect-proxy.test.ts(395,5)`: `TS2349 This expression is not callable. Type 'never' has no call signatures.`
|
||||
- Verifier artifacts: `.tmp/skill-verification/subminer-verify-20260319-195752-RNLVgE`
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: TASK-207
|
||||
title: 'Verify PR #19 follow-up typecheck blocker is cleared'
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-20 03:03'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- pr-review
|
||||
- anki-integration
|
||||
- verification
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- src/anki-integration/anki-connect-proxy.test.ts
|
||||
priority: medium
|
||||
ordinal: 123500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Confirm the previously unrelated `anki-connect-proxy.test.ts` typecheck failure no longer blocks verification for the PR #19 CodeRabbit follow-up work, and only patch it if the failure still reproduces.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Reproduce or clear the `src/anki-integration/anki-connect-proxy.test.ts` typecheck blocker with current workspace state
|
||||
- [x] #2 If the blocker still exists, apply the smallest safe fix and verify it
|
||||
- [x] #3 Document the verification result and any remaining unrelated blockers
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Re-run `bun run typecheck` and a focused proxy test against the current workspace to confirm whether the previous `anki-connect-proxy.test.ts` failure still reproduces.
|
||||
2. If the failure reproduces, use the typecheck failure itself as the red test, patch the smallest type-safe fix in the test, and rerun focused verification.
|
||||
3. Re-run the relevant verifier lane(s), then record whether the blocker is cleared or if any unrelated failures remain.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Re-ran `bun run typecheck` against the current workspace and the prior `src/anki-integration/anki-connect-proxy.test.ts` blocker no longer reproduces.
|
||||
|
||||
Focused verification passed for `bun test src/anki-integration/anki-connect-proxy.test.ts`. Core verifier now passes `typecheck` and reaches `test:fast`.
|
||||
|
||||
Current remaining unrelated verifier failure is unchanged local environment behavior in `scripts/update-aur-package.test.ts`: `scripts/update-aur-package.sh: line 71: mapfile: command not found` under macOS Bash. Artifact: `.tmp/skill-verification/subminer-verify-20260319-200320-vy2YHa`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Verified the previously reported PR #19 follow-up typecheck blocker is cleared in the current workspace. `bun run typecheck` now passes, and the focused proxy regression file `src/anki-integration/anki-connect-proxy.test.ts` also passes, including the background-enrichment response timing test.
|
||||
|
||||
Re-running the SubMiner core verifier confirms the blocker moved forward: `core/typecheck` passes, and the remaining `core/test-fast` failure is unrelated to the proxy test. The only red is the existing macOS Bash compatibility issue in `scripts/update-aur-package.test.ts`, where `scripts/update-aur-package.sh` uses `mapfile` and exits with `line 71: mapfile: command not found`.
|
||||
|
||||
Verification run:
|
||||
- `bun run typecheck`
|
||||
- `bun test src/anki-integration/anki-connect-proxy.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/anki-integration/anki-connect-proxy.test.ts`
|
||||
|
||||
Verifier result:
|
||||
- `core/typecheck` passed
|
||||
- `core/test-fast` failed only in `scripts/update-aur-package.test.ts` because local macOS Bash lacks `mapfile`
|
||||
- Artifact: `.tmp/skill-verification/subminer-verify-20260319-200320-vy2YHa`
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
id: TASK-208
|
||||
title: 'Assess newest PR #19 CodeRabbit round after 1227706'
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-20 03:37'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- pr-review
|
||||
- launcher
|
||||
- anki-integration
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- launcher/commands/stats-command.ts
|
||||
- launcher/mpv.ts
|
||||
- src/anki-integration.ts
|
||||
priority: medium
|
||||
ordinal: 122500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Validate the newest 2026-03-20 03:23 CodeRabbit review round on PR #19 after commit `1227706`, implement only the confirmed fixes, and record any bot suggestions that are stale or technically incomplete.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Each newest-round CodeRabbit inline comment posted after commit `1227706` is validated against current branch behavior and classified as actionable or not warranted
|
||||
- [x] #2 Confirmed issues are fixed with focused regression coverage where practical
|
||||
- [x] #3 Targeted verification runs for the touched areas succeed or remaining unrelated failures are documented
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Pull the three newest CodeRabbit inline threads posted after commit `1227706` and restate each finding against the current branch code.
|
||||
2. For each confirmed behavior bug, add or extend a focused failing test before changing production code; reject any stale or incorrect bot suggestion with notes.
|
||||
3. Patch the smallest safe fixes in `launcher/commands/stats-command.ts`, `launcher/mpv.ts`, and/or `src/anki-integration.ts` as warranted, without disturbing unrelated local edits.
|
||||
4. Run targeted tests and the cheapest sufficient verifier lanes, then record accepted versus rejected comments in task notes and summary.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Validated the newest 2026-03-20 03:23 CodeRabbit round as three comments: two actionable launcher issues and one non-warranted Anki suggestion.
|
||||
|
||||
Accepted fixes: cancel the pending stats response poll when the attached app exits non-zero before startup response, and surface `spawnSync()` launch/stop errors in launcher mpv helpers instead of treating `result.status ?? 0` / ignored status as success.
|
||||
|
||||
Rejected fix: the `src/anki-integration.ts` / card-creation suggestion would double count locally mined cards. Local sentence mining already records stats in `src/main/runtime/anki-actions.ts` when `mineSentenceCardCore` returns `true`; adding a second callback in card creation would increment tracker counts twice for the same card.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the newest CodeRabbit PR #19 round after commit `1227706` and fixed the two confirmed launcher regressions. `runStatsCommand()` now gives the startup response waiter an abort signal and cancels the polling loop immediately when the attached app exits non-zero before startup response, covering both the normal stats startup race and the cleanup/startup race. `launchTexthookerOnly()` now fails non-zero when `spawnSync()` reports an execution error, and `stopOverlay()` logs a warning when the stop command cannot be spawned or exits non-zero instead of silently treating that path as success.
|
||||
|
||||
One bot comment was intentionally rejected: recording mined-card stats inside the direct card-creation path would double count locally mined cards, because the successful local mining flow already records cards in `src/main/runtime/anki-actions.ts` after `mineSentenceCardCore()` returns `true`.
|
||||
|
||||
Verification run:
|
||||
- `bun test launcher/commands/command-modules.test.ts`
|
||||
- `bun test launcher/mpv.test.ts`
|
||||
- `bun run typecheck`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh launcher/commands/stats-command.ts launcher/commands/command-modules.test.ts launcher/mpv.ts launcher/mpv.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane launcher-plugin launcher/commands/stats-command.ts launcher/commands/command-modules.test.ts launcher/mpv.ts launcher/mpv.test.ts`
|
||||
|
||||
Verifier result:
|
||||
- `launcher-plugin` lane passed (`test:launcher:smoke:src`, `test:plugin:src`)
|
||||
- `typecheck` passed
|
||||
- Verifier artifacts: `.tmp/skill-verification/subminer-verify-20260319-204639-dzUj16`
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: TASK-209
|
||||
title: Exclude grammar-tail そうだ from subtitle annotations
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-20 04:06'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- tokenizer
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer/annotation-stage.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer/annotation-stage.test.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer.test.ts
|
||||
priority: high
|
||||
ordinal: 120500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Sentence-final grammar-tail `そうだ` tokens can still receive subtitle annotation styling, including frequency highlighting, when Yomitan returns a standalone `そうだ` token and MeCab enriches it as an auxiliary-stem/coupla pattern (`名詞|助動詞`, `助動詞語幹`). Keep the subtitle text visible, but treat this grammar tail like other grammar-only endings so it renders without annotation metadata.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Sentence-final grammar-tail `そうだ` tokens enriched as auxiliary-stem/copula patterns do not receive frequency highlighting or other subtitle annotation metadata.
|
||||
- [x] #2 The preceding lexical token in cases like `与えるそうだ` keeps its existing annotation behavior.
|
||||
- [x] #3 Regression tests cover the annotation-stage exclusion and end-to-end subtitle tokenization for the `そうだ` grammar-tail case.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add focused regression coverage for the reported `与えるそうだ` case at both annotation-stage and tokenizeSubtitle levels.
|
||||
2. Reproduce failure by modeling the MeCab-enriched grammar-tail shape (`名詞|助動詞`, `特殊`, `助動詞語幹`) that currently keeps frequency metadata.
|
||||
3. Update subtitle-annotation exclusion logic to recognize auxiliary-stem/copula grammar tails via POS metadata plus normalized tail text, not a raw sentence-specific string match.
|
||||
4. Re-run targeted tokenizer and annotation-stage tests, then record the verification commands and outcome in the task notes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Investigated reported `与えるそうだ` case. MeCab tags `そう` as `名詞,特殊,助動詞語幹` and `だ` as `助動詞`; after overlap enrichment the Yomitan token becomes `pos1=名詞|助動詞`, `pos2=特殊`, `pos3=助動詞語幹`, which currently escapes subtitle-annotation exclusion and can keep a frequency rank.
|
||||
|
||||
Implemented a POS-shape subtitle-annotation exclusion for MeCab-enriched auxiliary-stem grammar tails. The new predicate keys off merged tokens whose POS tags stay within `名詞/助動詞/助詞` and whose POS3 includes `助動詞語幹`, which clears annotation metadata for `そうだ`-style tails without hard-coding the full subtitle text.
|
||||
|
||||
Verification: `bun test src/core/services/tokenizer/annotation-stage.test.ts`, `bun test src/core/services/tokenizer.test.ts --test-name-pattern 'explanatory ending|interjection|single-kana merged tokens from frequency highlighting|auxiliary-stem そうだ grammar tails|composite function/content token from frequency highlighting|keeps frequency for content-led merged token with trailing colloquial suffixes'`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Added regression coverage for `与えるそうだ` and updated subtitle annotation exclusion logic to drop annotation metadata for MeCab-enriched auxiliary-stem grammar tails. The fix is POS-driven rather than sentence-specific, so `そうだ`-style grammar endings stay visible/hoverable as plain text while neighboring lexical tokens keep their existing frequency/JLPT behavior.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: TASK-210
|
||||
title: Show latest session position in anime episode progress
|
||||
status: Done
|
||||
assignee:
|
||||
- '@Codex'
|
||||
created_date: '2026-03-20 04:09'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- stats
|
||||
- bug
|
||||
- ui
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- stats/src/components/anime/EpisodeList.tsx
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
- src/core/services/immersion-tracker/session.ts
|
||||
- src/core/services/immersion-tracker-service.ts
|
||||
ordinal: 121500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Anime episode rows in stats can show watch time and lookups from the latest session while the Progress column stays blank because it only reads `ended_media_ms` from ended sessions. Update the progress source so a just-watched episode reflects the latest known session stop position without falling back to cumulative watch time.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Anime episode progress uses the latest known session position for the episode, including the most recent active session when available.
|
||||
- [x] #2 Ended-session progress remains correct and does not regress to cumulative watch time.
|
||||
- [x] #3 Regression coverage locks query and/or UI behavior for active-session and ended-session episode progress.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add failing regression coverage for anime episode progress when the latest session is still active but has a known playback position.
|
||||
2. Persist the latest playback position on the active `imm_sessions` row during playback so stats queries can read it before session finalization.
|
||||
3. Update anime episode queries to use the newest known session position for progress while preserving ended-session behavior.
|
||||
4. Run targeted verification for immersion tracker, stats query, and cheap repo checks; record results and task outcome.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Root cause: stale active-session recovery rebuilt session state with `lastMediaMs = null`, so `finalizeSessionRecord` overwrote persisted progress checkpoints with `ended_media_ms = NULL` during startup reconciliation.
|
||||
|
||||
Implemented telemetry-flush checkpointing to persist `lastMediaMs` onto the active `imm_sessions` row, preserved that checkpoint through stale-session reconciliation, and updated anime episode progress queries to read the latest known non-null session position across active or ended sessions.
|
||||
|
||||
Verification: targeted regressions passed (`bun test src/core/services/immersion-tracker-service.test.ts --test-name-pattern 'flushTelemetry checkpoints latest playback position on the active session row|startup finalizes stale active sessions and applies lifetime summaries'`, `bun test src/core/services/immersion-tracker/__tests__/query.test.ts --test-name-pattern 'getAnimeEpisodes prefers the latest session media position when the latest session is still active|getAnimeEpisodes returns latest ended media position and aggregate metrics'`), broader tracker/query suite passed (`bun test src/core/services/immersion-tracker-service.test.ts src/core/services/immersion-tracker/__tests__/query.test.ts`), `bun run typecheck` passed via verifier, `bun run changelog:lint` passed.
|
||||
|
||||
Verification blocker: `.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core ...` reported `bun run test:fast` failure from pre-existing `scripts/update-aur-package.test.ts` (`mapfile: command not found` under bash), unrelated to this change set.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Persist anime episode progress checkpoints before session finalization so stats can survive crashes/restarts and still show the latest known watch position. Telemetry flushes now checkpoint `lastMediaMs` onto the active `imm_sessions` row, stale-session recovery preserves that checkpoint when finalizing recovered sessions, and `getAnimeEpisodes` now reads the newest non-null session position whether it came from an active or ended session.
|
||||
|
||||
Added regressions for active-session checkpoint persistence, stale-session recovery preserving `ended_media_ms`, and episode queries preferring the latest known session position. Verification passed for the targeted and broader immersion tracker/query suites, plus `bun run typecheck` and `bun run changelog:lint`. The verifier's `bun run test:fast` step still fails on the pre-existing `scripts/update-aur-package.test.ts` bash `mapfile` issue, which is outside this task's scope.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: TASK-211
|
||||
title: >-
|
||||
Recover anime episode progress from subtitle timing when checkpoints are
|
||||
missing
|
||||
status: Done
|
||||
assignee:
|
||||
- '@Codex'
|
||||
created_date: '2026-03-20 10:15'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- stats
|
||||
- bug
|
||||
milestone: m-1
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/immersion-tracker/query.ts
|
||||
- src/core/services/immersion-tracker/__tests__/query.test.ts
|
||||
ordinal: 119500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Anime episode progress can still show `0%` for older sessions that have watch-time and subtitle timing but no persisted `ended_media_ms` checkpoint. Recover progress from the latest retained subtitle/event segment end so already-recorded sessions render a useful progress percentage.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `getAnimeEpisodes` returns the latest known session position even when `ended_media_ms` is null but subtitle/event timing exists.
|
||||
- [x] #2 Existing ended-session metrics and aggregation totals do not regress.
|
||||
- [x] #3 Regression coverage locks the fallback behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added a query-side fallback for anime episode progress: when the newest session for a video has no persisted `ended_media_ms`, `getAnimeEpisodes` now uses the latest retained subtitle-line or session-event `segment_end_ms` from that same session. This recovers useful progress for already-recorded sessions that have timing data but predate or missed checkpoint persistence.
|
||||
|
||||
Verification: `bun test src/core/services/immersion-tracker/__tests__/query.test.ts` passed. `bun run typecheck` passed.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: TASK-212
|
||||
title: Fix mac texthooker helper startup blocking mpv launch
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-20 08:27'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- macos
|
||||
- startup
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/startup.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/plugin/subminer/process.lua
|
||||
priority: high
|
||||
ordinal: 140500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`subminer` mpv auto-start on mac can stall before the video is usable because the helper process launched with `--texthooker` still runs heavy app-ready startup. Recent logs show the helper loading the Yomitan Chromium extension, emitting `Permission 'contextMenus' is unknown` warnings, then hitting Chromium runtime errors before SubMiner signals readiness back to the mpv plugin. The texthooker helper should take the minimal startup path needed to serve texthooker traffic without loading overlay/window-only startup work that can crash or delay readiness.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Launching SubMiner with `--texthooker` avoids heavy app-ready startup work that is not required for texthooker helper mode.
|
||||
- [x] #2 A regression test covers texthooker helper startup so it fails if Yomitan extension loading is reintroduced on that path.
|
||||
- [x] #3 The change preserves existing startup behavior for non-texthooker app launches.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Follow-up: user confirmed the root issue is the plugin auto-start ordering. Adjust mpv plugin sequencing so `--start` launches before any separate `--texthooker` helper, then verify plugin regressions still pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed the mac mpv startup hang caused by the `--texthooker` helper taking the full app-ready path. `runAppReadyRuntime` now fast-paths texthooker-only mode through minimal startup (`reloadConfig` plus CLI handling) so it no longer loads Yomitan or first-run setup work before serving texthooker traffic. Added regression coverage in `src/core/services/app-ready.test.ts`, then verified with `bun test src/core/services/app-ready.test.ts src/core/services/startup.test.ts`, `bun test src/cli/args.test.ts src/main/early-single-instance.test.ts src/main/runtime/stats-cli-command.test.ts`, and `bun run typecheck`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: TASK-213
|
||||
title: Show character dictionary progress during paused startup waits
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-20 08:59'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- ux
|
||||
- dictionary
|
||||
- startup
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/startup-osd-sequencer.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync-notifications.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
priority: medium
|
||||
ordinal: 141500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
During startup on mpv auto-start, character dictionary regeneration/update can be active while playback remains paused. The current startup OSD sequencer buffers dictionary progress behind annotation-loading OSD, which leaves the user with no visible dictionary-specific progress while the pause is active. Adjust the startup OSD sequencing so dictionary progress can surface once tokenization is ready during the paused startup window, without regressing later ready/failure handling.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 When tokenization is ready during startup, later character dictionary progress updates are shown on OSD even if annotation-loading state is still active.
|
||||
- [ ] #2 Startup OSD completion/failure behavior for character dictionary sync remains coherent after the new progress ordering.
|
||||
- [ ] #3 Regression coverage exercises the paused startup sequencing for dictionary progress.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-20: Confirmed issue is broader than OSD-only. Paused-startup OSD fixes remain relevant, but current user report also points at a regression in non-blocking startup playback release (tracked in TASK-143).
|
||||
|
||||
2026-03-20: OSD sequencing fix remains in local patch alongside TASK-143 regression fix. Covered by startup-osd-sequencer tests; pending installed-app/mpv validation before task finalization.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
id: TASK-214
|
||||
title: Jump subtitle sidebar directly to resume position on first resolved cue
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-21 11:15'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- ux
|
||||
- overlay
|
||||
- subtitles
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/subtitle-sidebar.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/subtitle-sidebar.test.ts
|
||||
priority: medium
|
||||
ordinal: 142500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
When playback starts from a resumed timestamp while the subtitle sidebar is open, the sidebar currently smooth-scrolls from the top of the cue list to the resumed cue. Change the first resolved active-cue positioning to jump immediately to the resume location while preserving smooth auto-follow for later playback-driven cue advances.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 The first active cue resolved after open/resume uses an instant jump instead of smooth-scrolling through the list.
|
||||
- [x] #2 Normal subtitle-sidebar auto-follow remains smooth after the first active cue has been positioned.
|
||||
- [x] #3 Regression coverage distinguishes the initial jump behavior from later smooth auto-follow updates.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-21: Fixed by treating the first auto-scroll from `previousActiveCueIndex < 0` as `behavior: 'auto'` in the subtitle sidebar scroll helper. Added renderer regression coverage for initial jump plus later smooth follow.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: TASK-215
|
||||
title: Add startup auto-open option for subtitle sidebar
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-21 11:35'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- feature
|
||||
- ux
|
||||
- overlay
|
||||
- subtitles
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/types.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-subtitle.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/subtitle-sidebar.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/renderer/renderer.ts
|
||||
priority: medium
|
||||
ordinal: 143500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Add a subtitle sidebar config option that auto-opens the sidebar once during overlay startup. The option should default to `false`, only apply when the sidebar feature is enabled, and should not force the sidebar back open later in the same session after manual close or later visibility changes.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `subtitleSidebar.autoOpen` is available in config with default `false`.
|
||||
- [x] #2 When enabled, overlay startup opens the subtitle sidebar once after initial sidebar config/snapshot load.
|
||||
- [x] #3 Regression coverage covers config resolution and startup-only auto-open behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-21: Added `subtitleSidebar.autoOpen` to types/defaults/config registry and resolver. Renderer bootstrap now calls a startup-only subtitle sidebar helper after the initial snapshot refresh. Modal regression coverage verifies startup auto-open requires both `enabled` and `autoOpen`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: TASK-216
|
||||
title: 'Address PR #28 CodeRabbit follow-ups on subtitle sidebar'
|
||||
status: Completed
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-21 00:00'
|
||||
updated_date: '2026-03-21 00:00'
|
||||
labels:
|
||||
- pr-review
|
||||
- subtitle-sidebar
|
||||
- renderer
|
||||
dependencies: []
|
||||
references:
|
||||
- src/main/runtime/subtitle-prefetch-init.ts
|
||||
- src/main/runtime/subtitle-prefetch-init.test.ts
|
||||
- src/renderer/handlers/mouse.ts
|
||||
- src/renderer/handlers/mouse.test.ts
|
||||
- src/renderer/modals/subtitle-sidebar.ts
|
||||
- src/renderer/modals/subtitle-sidebar.test.ts
|
||||
- src/renderer/style.css
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Validate the CodeRabbit follow-ups on PR #28 for the subtitle sidebar workstream, implement the confirmed fixes, and verify the touched runtime and renderer paths.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Review comments that described real regressions are fixed in code
|
||||
- [x] #2 Focused regression coverage exists for the fixed behaviors
|
||||
- [x] #3 Targeted typecheck and runtime-compat verification pass
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Completed follow-up fixes for PR #28:
|
||||
- Cleared parsed subtitle cues on subtitle prefetch init failure so stale snapshot cache entries do not survive a failed refresh.
|
||||
- Treated primary and secondary subtitle containers as one hover region so moving between them does not resume playback mid-transition.
|
||||
- Kept the subtitle sidebar closed when disabled, serialized snapshot polling with timeouts, made cue rows keyboard-activatable, resolved stale cue selection fallback, and resumed hover-paused playback when the modal closes.
|
||||
|
||||
Regression coverage added:
|
||||
- `src/main/runtime/subtitle-prefetch-init.test.ts`
|
||||
- `src/renderer/handlers/mouse.test.ts`
|
||||
- `src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
|
||||
Verification:
|
||||
- `bun test src/main/runtime/subtitle-prefetch-init.test.ts`
|
||||
- `bun test src/renderer/handlers/mouse.test.ts`
|
||||
- `bun test src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
- `bun run typecheck`
|
||||
- `bun run test:runtime:compat`
|
||||
|
||||
2026-03-21: Reopened to assess a newer CodeRabbit review pass on PR #28 and address any remaining valid action items before push/reply.
|
||||
|
||||
2026-03-21: Addressed the latest CodeRabbit follow-up pass in commit d70c6448 after rebasing onto the updated remote branch tip.
|
||||
|
||||
2026-03-21: Reopened for the latest CodeRabbit round on commit d70c6448; current actionable item is the invalid ctx.state.isOverSubtitleSidebar assignment in subtitle-sidebar.ts.
|
||||
|
||||
2026-03-22: Addressed the live hover-state and startup mouse-ignore follow-ups from the latest CodeRabbit pass. `handleMouseLeave()` now clears `isOverSubtitle` and drops `secondary-sub-hover-active` when leaving the secondary subtitle container toward the primary container, and renderer startup now calls `syncOverlayMouseIgnoreState(ctx)` instead of forcing `setIgnoreMouseEvents(true, { forward: true })`. The sidebar IPC hover catch and CSS spacing comments were already satisfied in the current tree.
|
||||
|
||||
2026-03-22: Regenerated `bun.lock` from a clean install so the `electron-builder-squirrel-windows` override now resolves at `26.8.2` in the lockfile alongside `app-builder-lib@26.8.2`.
|
||||
|
||||
2026-03-21: Finished the remaining cleanup pass from the latest review. `subtitleSidebar.layout` now uses enum validation, `SubtitleCue` is re-exported from `src/types.ts` as the single public type path, and the subtitle sidebar resize listener now has unload cleanup wired through the renderer.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Implemented the confirmed PR #28 CodeRabbit follow-ups for subtitle sidebar behavior and added regression coverage plus verification for the touched renderer and runtime paths.
|
||||
|
||||
Handled the latest CodeRabbit review pass for PR #28: accepted zero sidebar opacity, closed/inerted the sidebar when refresh sees config disabled, moved poll rescheduling out of finally, caught hover pause IPC failures, and fixed the stylelint spacing issue.
|
||||
|
||||
Verification: bun test src/config/resolve/subtitle-sidebar.test.ts; bun test src/renderer/modals/subtitle-sidebar.test.ts; bun test src/renderer/handlers/mouse.test.ts; bun run typecheck; bun run test:fast; bun run test:env; bun run build; SubMiner verifier lanes config + runtime-compat (including test:runtime:compat and test:smoke:dist).
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
id: TASK-217
|
||||
title: Fix embedded overlay passthrough sync between subtitle and sidebar
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-21 23:16'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- overlay
|
||||
- macos
|
||||
dependencies: []
|
||||
references:
|
||||
- src/renderer/handlers/mouse.ts
|
||||
- src/renderer/modals/subtitle-sidebar.ts
|
||||
- src/renderer/renderer.ts
|
||||
documentation:
|
||||
- docs/workflow/verification.md
|
||||
priority: high
|
||||
ordinal: 118500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
On macOS, when both the subtitle overlay and embedded subtitle sidebar are visible, mouse passthrough to mpv can remain stale until the user hovers the sidebar. After closing the sidebar, passthrough can likewise remain stale until the user hovers the subtitle again. Fix the overlay input-state synchronization so passthrough reflects the current hover/open state immediately instead of relying on the last hover target.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When the embedded subtitle sidebar is open and the pointer is not over subtitle or sidebar content, the overlay returns to mouse passthrough immediately without requiring a sidebar hover cycle.
|
||||
- [x] #2 When transitioning between subtitle hover and sidebar hover states on macOS embedded sidebar mode, mouse ignore state stays in sync with the currently interactive region.
|
||||
- [x] #3 Closing the embedded subtitle sidebar restores the correct passthrough state based on remaining subtitle hover/modal state without requiring an additional hover.
|
||||
- [x] #4 Regression tests cover the passthrough synchronization behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a shared renderer-side passthrough sync helper that derives whether the overlay should ignore mouse events from subtitle hover, embedded sidebar visibility/hover, popup visibility, and modal state.
|
||||
2. Replace direct embedded-sidebar passthrough toggles in subtitle hover/sidebar handlers with calls to the shared sync helper so state is recomputed on every transition.
|
||||
3. Add regression tests for macOS embedded sidebar mode covering sidebar-open idle passthrough, subtitle-to-sidebar transitions, and sidebar-close restore behavior.
|
||||
4. Run targeted renderer tests for mouse/sidebar passthrough coverage, then summarize any residual risk.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added shared renderer overlay mouse-ignore recompute so subtitle hover, embedded sidebar hover/open/close, and popup idle transitions all derive passthrough from current state instead of last hover target.
|
||||
|
||||
Added regression coverage for embedded sidebar idle passthrough on subtitle leave and for sidebar-close recompute behavior.
|
||||
|
||||
Verification: `bun run typecheck` passed; `bun test src/renderer/handlers/mouse.test.ts` passed; `bun test src/renderer/modals/subtitle-sidebar.test.ts` passed; core verification wrapper artifact at `.tmp/skill-verification/subminer-verify-20260321-162743-XhSBxw` hit an unrelated `bun run test:fast` failure in `scripts/update-aur-package.test.ts` because macOS system bash lacks `mapfile`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed stale embedded-sidebar passthrough sync on macOS by introducing a shared renderer mouse-ignore recompute path and tracking sidebar-hover state separately from subtitle hover. Subtitle hover leave, sidebar hover enter/leave, sidebar open, and sidebar close now all recompute passthrough from the current overlay state instead of waiting for a later hover event to repair it. Added regression tests covering subtitle-leave passthrough while the embedded sidebar is open but idle, plus sidebar-close restore behavior based on remaining subtitle hover state.
|
||||
|
||||
Tests run:
|
||||
- `bun run typecheck`
|
||||
- `bun test src/renderer/handlers/mouse.test.ts`
|
||||
- `bun test src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/renderer/state.ts src/renderer/overlay-mouse-ignore.ts src/renderer/handlers/mouse.ts src/renderer/handlers/mouse.test.ts src/renderer/modals/subtitle-sidebar.ts src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/renderer/state.ts src/renderer/overlay-mouse-ignore.ts src/renderer/handlers/mouse.ts src/renderer/handlers/mouse.test.ts src/renderer/modals/subtitle-sidebar.ts src/renderer/modals/subtitle-sidebar.test.ts` (typecheck passed; `test:fast` blocked by unrelated `scripts/update-aur-package.test.ts` failure on macOS Bash 3.2 lacking `mapfile`)
|
||||
|
||||
Risk: the classifier flagged this as a real-runtime candidate, so actual Electron/mpv macOS pointer behavior was not exercised in a live runtime during this turn.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: TASK-218
|
||||
title: Delete zero-session media from stats library and trends
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-22 16:20'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- stats
|
||||
- immersion-tracker
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/query.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/lifetime.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/maintenance.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/__tests__/query.test.ts
|
||||
priority: medium
|
||||
ordinal: 153500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Deleting the last retained session for a video still left stale lifetime media rows and trend rollups behind, so the stats dashboard could continue showing ghost entries in Library and Trends after all sessions were gone.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Deleting the final session for a video removes that media from Library queries and detail reads
|
||||
- [x] #2 Deleting the final session for a video removes stale daily/monthly trend rollups for that media
|
||||
- [x] #3 Regression coverage proves zero-session media disappears from affected stats surfaces after deletion
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing regression around deleting the only retained session for a video while preexisting lifetime and rollup rows exist.
|
||||
2. Patch the deletion path to rebuild lifetime and rollup state from retained sessions inside the same transaction.
|
||||
3. Run focused immersion-tracker tests plus the repo-native verifier core lane and record results.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added a query regression that seeds a finished session plus stale lifetime media/anime rows and daily/monthly rollups, deletes that only session, and asserts Library, Anime detail, and Trends all drop the media immediately.
|
||||
|
||||
Refactored lifetime rebuild logic so it can run inside an existing delete transaction, then reused that helper from `deleteSession`, `deleteSessions`, and `deleteVideo`.
|
||||
|
||||
Added a rollup rebuild helper that clears existing daily/monthly rollups and reconstructs them from retained telemetry inside the current transaction so deleted sessions cannot leave ghost trend points behind.
|
||||
|
||||
Verification passed:
|
||||
- `bun test src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
- `bun test src/core/services/immersion-tracker-service.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/core/services/immersion-tracker/query.ts src/core/services/immersion-tracker/lifetime.ts src/core/services/immersion-tracker/maintenance.ts src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/core/services/immersion-tracker/query.ts src/core/services/immersion-tracker/lifetime.ts src/core/services/immersion-tracker/maintenance.ts src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
|
||||
Verifier artifact dir: `.tmp/skill-verification/subminer-verify-20260322-210718-n6sGL8`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Delete paths now rebuild lifetime summaries and trend rollups after removing sessions, so when the last session for a video disappears the stats database also drops that media from Library, related detail reads, and chart data. Added a regression proving a video with only stale lifetime/rollup rows vanishes after its final session is deleted, and verified the change with focused immersion-tracker tests plus the SubMiner core verification lane.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: TASK-219
|
||||
title: Restore streamed video progress in anime episodes
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-22 21:25'
|
||||
updated_date: '2026-03-24 06:44'
|
||||
labels:
|
||||
- stats
|
||||
- immersion-tracker
|
||||
- youtube
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/query.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker-service.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/__tests__/query.test.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker-service.test.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Episode progress for streamed media can stay at `0%` because some remote sessions persist `ended_media_ms = 0` even when subtitle timing and watch activity clearly advanced, and the anime episode query currently treats `0` as a valid progress checkpoint.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Anime episode progress ignores zero-valued session checkpoints and falls back to subtitle/event timing
|
||||
- [x] #2 New streamed sessions persist meaningful progress even when playback-position updates are missing or sparse
|
||||
- [x] #3 Regression tests cover the zero-checkpoint remote-session case
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Restored anime episode progress handling for streamed sessions by ignoring zero-valued `ended_media_ms` checkpoints and falling back to subtitle/event timing, with regression coverage for the remote-session zero-checkpoint case.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
id: TASK-220
|
||||
title: Restore YouTube overlay mpv keybindings after picker routing
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-22 00:00'
|
||||
updated_date: '2026-03-22 23:49'
|
||||
labels:
|
||||
- bug
|
||||
- overlay
|
||||
- youtube
|
||||
- keyboard
|
||||
dependencies: []
|
||||
references:
|
||||
- src/renderer/handlers/keyboard.ts
|
||||
- src/renderer/modals/youtube-track-picker.ts
|
||||
- src/renderer/handlers/keyboard.test.ts
|
||||
- src/renderer/modals/youtube-track-picker.test.ts
|
||||
documentation:
|
||||
- docs/workflow/verification.md
|
||||
priority: high
|
||||
ordinal: 118800
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Regression: after adding the YouTube subtitle picker modal path, visible-overlay keydown handling can stop before reaching the shared mpv keybinding dispatch path. Result: default overlay mpv bindings like `Space` pause/play and `q` quit stop working while the overlay owns focus during YouTube playback.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Unhandled keys while the YouTube track picker state is active still fall through to the shared overlay mpv keybinding dispatcher.
|
||||
- [x] #2 The YouTube picker continues to consume `Enter` and `Escape` for its own actions.
|
||||
- [x] #3 Renderer regression tests cover both the picker modal key contract and the shared keyboard dispatch fallback.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing renderer keyboard regression test covering YouTube picker state plus shared mpv keybinding fallback.
|
||||
2. Update the global keyboard handler to return early only when the YouTube picker actually handles the key event.
|
||||
3. Update the picker modal handler to return false for unhandled keys while preserving `Enter`/`Escape`.
|
||||
4. Run the cheap renderer verification lane and record results.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Fixed the regression by making the global renderer keyboard handler stop early for the YouTube picker only when the picker actually consumes the key. The picker modal now returns `false` for unrelated keys, so shared overlay mpv bindings like `Space` and `KeyQ` still dispatch while the visible overlay has focus.
|
||||
|
||||
Added regression coverage in the keyboard handler suite for mpv keybinding fallback during YouTube picker state, plus a picker-modal contract test that keeps `Escape` handled but leaves unrelated keys unclaimed.
|
||||
|
||||
Verification:
|
||||
- `bun test src/renderer/handlers/keyboard.test.ts src/renderer/modals/youtube-track-picker.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/renderer/handlers/keyboard.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/youtube-track-picker.ts src/renderer/modals/youtube-track-picker.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/renderer/handlers/keyboard.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/youtube-track-picker.ts src/renderer/modals/youtube-track-picker.test.ts`
|
||||
- verifier artifact: `.tmp/skill-verification/subminer-verify-20260322-234831-b2m6nJ`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Restored YouTube-session overlay mpv keybindings by removing an unconditional early return added to the renderer keyboard path for the YouTube subtitle picker modal. Unhandled keys now fall through to the shared mpv keybinding dispatcher, while handled picker keys (`Enter`, `Escape`) still stay local to the picker. Added renderer regression tests for both the keyboard fallback path and the picker modal key-consumption contract.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
id: TASK-221
|
||||
title: 'Assess and address PR #31 latest CodeRabbit review'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-23 07:53'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
priority: medium
|
||||
ordinal: 152500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the latest CodeRabbit review on PR #31, evaluate each actionable comment against the current branch, implement valid fixes, verify the changes, and prepare PR thread updates.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Inspect latest CodeRabbit review on PR #31 and separate valid action items from non-blocking suggestions.
|
||||
2. Add regression coverage for any real bugs before changing production code.
|
||||
3. Implement the minimal fixes for confirmed issues in runtime, renderer modal flow, and test fixtures.
|
||||
4. Run targeted tests plus repo-native verification lanes.
|
||||
5. Update PR threads with fix status and rationale for any comments not actioned yet.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented and pushed commit 207151db to PR #31.
|
||||
|
||||
Replied in-thread to the CodeRabbit comments for YouTube host matching, duplicate picker submissions, and missing MediaDetailView test fixture videoId fields.
|
||||
|
||||
Follow-up scope added: update release-facing docs/changelog for the YouTube subtitle picker work and run a release-readiness gate before handoff.
|
||||
|
||||
Added release-facing docs/changelog updates in commit b7e0026d and pushed them to PR #31.
|
||||
|
||||
Ran the release-readiness gate: changelog:lint, changelog:pr-check, verify:config-example, typecheck, test:fast, test:env, build, test:smoke:dist, docs:test, docs:build.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the latest CodeRabbit review on PR #31 and applied the confirmed fixes. Tightened `isYoutubeMediaPath()` to match only exact YouTube hosts or subdomains with a regression test for `notyoutube.com`, added an in-flight guard plus temporary control disabling to the YouTube track picker with a duplicate-submit regression test, replaced the picker empty-state `innerHTML` fallback with explicit DOM construction, and added the missing `videoId` fields to the `MediaDetailView` test fixtures. Verified with targeted Bun tests and the `runtime-compat` verification lane (`build`, `test:runtime:compat`, `test:smoke:dist`).
|
||||
|
||||
Updated `README.md`, `docs-site/usage.md`, and `changes/2026-03-23-immersion-youtube.md` so the PR is release-facing and user-visible surfaces describe the YouTube subtitle picker flow plus its latest hardening.
|
||||
|
||||
Release-readiness checks passed locally: `bun run changelog:lint`, `bun run changelog:pr-check`, `bun run verify:config-example`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, `bun run test:smoke:dist`, `bun run docs:test`, and `bun run docs:build`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
id: TASK-222
|
||||
title: Fix YouTube overlay keybindings in subtitle path
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-23 08:32'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main/runtime
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/core/services
|
||||
priority: high
|
||||
ordinal: 151500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Users watching video through the YouTube subtitle path cannot use some overlay keyboard controls such as quit and pause/play. Restore expected overlay keybinding behavior for that playback path without regressing other overlay input handling.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Overlay quit and pause/play keybindings work while using the YouTube subtitle path.
|
||||
- [x] #2 Existing overlay keybinding behavior for non-YouTube playback remains unchanged.
|
||||
- [x] #3 Regression coverage exercises the YouTube subtitle path keyboard handling.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a regression test around YouTube track-picker close to verify it requests main-process main-window focus restoration before returning overlay focus locally.
|
||||
2. Update the YouTube track-picker close flow to call `window.electronAPI.focusMainWindow()` alongside the existing `window.focus()` and `overlay.focus()` restoration.
|
||||
3. Run targeted tests for the picker/keyboard paths to verify YouTube playback regains overlay keybindings without regressing existing overlay behavior.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Investigated overlay input path. Renderer already maps Space/KeyQ to mpv commands, but YouTube track-picker close only restores DOM focus (`window.focus` + `overlay.focus`) and does not invoke main-process window focus recovery, unlike the keyboard-mode focus reclaim path. Suspected root cause: overlay BrowserWindow focus is not restored after the YouTube picker closes, so playback keybindings stop reaching renderer keydown handlers.
|
||||
|
||||
User approved implementation plan on 2026-03-23. Proceeding with TDD: add failing regression first, then minimal fix, then targeted verification.
|
||||
|
||||
Implemented fix in the YouTube track-picker close path: request main-process `focusMainWindow()` before restoring renderer window/overlay focus so overlay keydown handlers regain input after YouTube subtitle selection.
|
||||
|
||||
Verification: `bun test src/renderer/modals/youtube-track-picker.test.ts` and `bun test src/renderer/handlers/keyboard.test.ts` both pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Restored overlay keyboard focus after closing the YouTube subtitle picker by invoking the main-process `focusMainWindow()` recovery path before local window/overlay focus restoration. Added regression coverage to the YouTube picker modal test and verified existing keyboard handler coverage for YouTube picker passthrough keys (`Space`, `KeyQ`) remains green.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: TASK-223
|
||||
title: Fix YouTube overlay Anki initialization regression
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-23 08:41'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
- youtube
|
||||
- anki
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-runtime-init.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/cli-command-runtime-handler.ts
|
||||
documentation:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/docs/workflow/verification.md
|
||||
priority: high
|
||||
ordinal: 154500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Restore Anki-backed lookup and known-word behavior during YouTube playback. Recent startup changes appear to let the YouTube flow initialize the overlay before runtime prerequisites exist, leaving the Anki integration unavailable for popup Mine actions and known-word highlighting.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 YouTube playback initializes Anki integration once overlay startup prerequisites are available so lookup can offer card-add actions again
|
||||
- [x] #2 Known-word / N+1 state is available during YouTube playback when the user has Anki-backed known-word highlighting enabled
|
||||
- [x] #3 Regression coverage fails before the fix and passes after it for the YouTube startup path
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a regression test covering the YouTube playback command path and assert overlay startup prerequisites are established before the flow runs.
|
||||
2. Reuse the overlay startup prerequisite bootstrap for the YouTube playback path so Anki integration sees subtitle tracker, mpv client, and runtime options manager before initialization.
|
||||
3. Verify with focused runtime/CLI tests, then run the cheapest sufficient verification lane for the touched files.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Identified regression path in CLI command runtime: YouTube playback commands could reach overlay initialization without first materializing overlay startup prerequisites, leaving Anki integration unavailable during the initial startup attempt.
|
||||
|
||||
Added a regression test at src/main/runtime/cli-command-runtime-handler.test.ts covering youtubePlay command dispatch outside texthooker-only mode.
|
||||
|
||||
Verified with bun test src/main/runtime/cli-command-runtime-handler.test.ts src/main/runtime/cli-command-prechecks.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts and bun test src/core/services/cli-command.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts src/main/runtime/cli-command-runtime-handler.test.ts.
|
||||
|
||||
Removed the YouTube-only ensureOverlayRuntimeReady path from src/main.ts after confirming regular app startup already loads Yomitan and the shared CLI overlay pre-dispatch bootstrap now covers overlay prerequisites.
|
||||
|
||||
Moved overlay bootstrap into the generic initial-args startup path for any initial command that needs overlay runtime, so overlay prerequisites and overlay initialization happen before CLI dispatch instead of inside a YouTube-only or last-moment command path.
|
||||
|
||||
Additional verification passed: bun test src/main/runtime/initial-args-runtime-handler.test.ts src/main/runtime/initial-args-handler.test.ts src/main/runtime/initial-args-main-deps.test.ts, bun run typecheck, bun run test:runtime:compat
|
||||
|
||||
Follow-up regression: subtitle picker was still auto-submitting the default selection during YouTube startup. Investigating renderer-side immediate Enter key bleed-through on picker open.
|
||||
|
||||
Root cause for remaining picker regression: the YouTube track picker accepted Enter immediately on open, so the launch keypress could auto-submit the default track selection before the modal was visible to the user.
|
||||
|
||||
Added renderer regression coverage in src/renderer/modals/youtube-track-picker.test.ts proving immediate Enter after open is ignored and a later Enter still submits normally.
|
||||
|
||||
Implemented a 200ms open-key guard in src/renderer/modals/youtube-track-picker.ts for Enter-based submission only; Escape/click behavior unchanged.
|
||||
|
||||
New follow-up regression report: YouTube subtitle picker can open before the mpv playback window is ready, leaving the picker behind the overlay after geometry snaps into place. Investigating picker-open gating and modal-targeting timing.
|
||||
|
||||
Identified likely cause of picker-behind-overlay regression: YouTube picker open logic mixed overlay targets. First attempt preferred the visible main overlay, timeout retry switched to the dedicated modal window, allowing a late first open to cover the modal.
|
||||
|
||||
Extracted picker-open policy into src/main/runtime/youtube-picker-open.ts and changed YouTube picker startup to always target the dedicated modal window, including retries. This keeps the picker on a single window path and lets overlay-runtime hide/click-through the main overlay while the modal is active.
|
||||
|
||||
Added regression tests in src/main/runtime/youtube-picker-open.test.ts covering dedicated modal first-open, dedicated-modal retry, and failure when no modal target is available.
|
||||
|
||||
User reports overlay flow still feels wrong: YouTube path appears to preload subtitles before mandatory selection and may open the picker before mpv window readiness. Re-evaluating flow design against regular video startup before further implementation.
|
||||
|
||||
New follow-up regression report: duplicate overlay windows appear during YouTube playback and only one window shows subtitles. Investigating main-overlay versus dedicated modal-window handoff/cleanup.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed the YouTube Anki initialization regression by making CLI commands that require overlay runtime bootstrap overlay startup prerequisites before command dispatch when not in texthooker-only mode. This ensures the YouTube playback flow has the mpv client, runtime options manager, and subtitle timing tracker ready before overlay/Anki initialization runs, restoring Mine actions and known-word-backed behavior.
|
||||
|
||||
Added a regression test covering youtubePlay command dispatch in src/main/runtime/cli-command-runtime-handler.test.ts.
|
||||
|
||||
Verification:
|
||||
- bun test src/main/runtime/cli-command-runtime-handler.test.ts src/main/runtime/cli-command-prechecks.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts
|
||||
- bun test src/core/services/cli-command.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts src/main/runtime/cli-command-runtime-handler.test.ts
|
||||
|
||||
Updated the fix to avoid a YouTube-specific startup path: removed the dedicated ensureOverlayRuntimeReady helper from src/main.ts and relied on the shared CLI overlay prerequisite bootstrap instead.
|
||||
|
||||
Additional verification: bun run typecheck
|
||||
|
||||
Follow-up adjustment: initial overlay-runtime commands now bootstrap overlay prerequisites and initialize overlay during the shared initial-args startup path, rather than waiting for command dispatch. This keeps YouTube on the regular startup path while preserving earlier overlay availability.
|
||||
|
||||
Additional verification: bun test src/main/runtime/initial-args-runtime-handler.test.ts src/main/runtime/initial-args-handler.test.ts src/main/runtime/initial-args-main-deps.test.ts; bun run test:runtime:compat
|
||||
|
||||
Follow-up fix: the YouTube subtitle picker now ignores immediate Enter key bleed-through right after opening, preventing the startup keypress from auto-submitting the default track selection before the modal is visible.
|
||||
|
||||
Added renderer regression coverage for immediate Enter suppression and verified with bun test src/renderer/modals/youtube-track-picker.test.ts plus the runtime-compat verification lane for the touched files.
|
||||
|
||||
Follow-up fix: YouTube subtitle picker startup now uses a dedicated modal-window path consistently instead of mixing main-overlay first-open with modal-window retry. That prevents late overlay opens from covering the interactive picker while mpv/window tracking settles.
|
||||
|
||||
Verified with bun test src/main/runtime/youtube-picker-open.test.ts, bun test src/renderer/modals/youtube-track-picker.test.ts, and the runtime-compat verification lane for src/main.ts plus the touched picker files.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
id: TASK-224
|
||||
title: >-
|
||||
Auto-load default YouTube subtitles at playback start and make picker
|
||||
manual-only
|
||||
status: Done
|
||||
assignee:
|
||||
- Codex
|
||||
created_date: '2026-03-23 18:51'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- youtube
|
||||
- mpv
|
||||
- overlay
|
||||
- keybindings
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main/runtime/youtube-flow.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/youtube-track-picker.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/config/definitions/shared.ts
|
||||
priority: high
|
||||
ordinal: 150500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Replace the mandatory YouTube subtitle picker startup flow with automatic default-track loading. On YouTube playback start, attempt to load the default primary subtitle and best-effort secondary subtitle without prompting. Gate playback only on primary subtitle load/tokenization readiness. If primary subtitle probing/download/loading fails, resume playback and report the failure through the configured notification/output path. Keep the YouTube subtitle picker as a regular overlay modal opened by a new default keybinding during active YouTube playback.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Opening a YouTube URL auto-selects and attempts to load the default primary subtitle without opening the picker modal.
|
||||
- [x] #2 Opening a YouTube URL also attempts to load the default secondary subtitle when available, but playback never waits on secondary success.
|
||||
- [x] #3 Playback remains gated only until the primary subtitle is loaded and tokenization is ready; primary failure resumes playback immediately.
|
||||
- [x] #4 Primary auto-load failures report through the existing configured notification/output path and keep playback running.
|
||||
- [x] #5 The YouTube subtitle picker can be opened manually during active YouTube playback via a new default keybinding.
|
||||
- [x] #6 Regression tests cover startup auto-load success, primary failure fallback, and the manual picker keybinding flow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add failing tests for YouTube startup auto-load success, primary failure fallback, and manual picker keybinding flow.
|
||||
2. Refactor the YouTube runtime to auto-select default tracks on startup, gate playback only on primary subtitle/tokenization readiness, and route failures through the configured notification/output path.
|
||||
3. Add a new default keybinding and command path to open the YouTube picker manually during active YouTube playback.
|
||||
4. Run targeted tests, then SubMiner verification lanes for launcher/runtime changes; update docs/changelog if required by the final behavior change.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Verification blocker outside this change: `bun run test:fast` still fails at `scripts/update-aur-package.test.ts` on macOS because `scripts/update-aur-package.sh` uses `mapfile`, which is unavailable in the system Bash 3.x environment used here.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Reworked app-owned YouTube playback to auto-load the default primary subtitle plus a best-effort secondary subtitle at startup instead of forcing the picker modal first. Playback now waits only on primary subtitle load/tokenization readiness, routes startup primary-failure messaging through the configured notification output path, and keeps the YouTube subtitle picker available on demand via a new default `Ctrl+Shift+J` keybinding during active YouTube playback. Updated the runtime/IPC/config plumbing, user-facing help/docs, and added regression coverage for startup auto-load, primary-failure fallback, and manual picker invocation.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: TASK-225
|
||||
title: Fix frozen primary YouTube subtitle display after auto-load startup
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-23 20:07'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
- youtube
|
||||
- subtitles
|
||||
dependencies:
|
||||
- TASK-224
|
||||
priority: high
|
||||
ordinal: 149500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After the new YouTube auto-load startup flow, the primary subtitle overlay can stay stuck on an older line while the subtitle sidebar continues advancing. Investigate startup suppression / subtitle refresh timing and restore live primary overlay updates after auto-loaded subtitles are injected.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When YouTube auto-load succeeds, the visible primary subtitle continues advancing after playback resumes.
|
||||
- [x] #2 Startup suppression does not leave the primary subtitle display stuck on a stale line.
|
||||
- [x] #3 A regression test covers the startup path that previously froze the visible primary subtitle while sidebar timing continued advancing.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Root cause: applyStartupState seeded youtubePlaybackFlowPending from initialArgs.youtubePlay, and runYoutubePlaybackFlowMain restored that preexisting true value after startup auto-load. Result: primary subtitle events stayed suppressed for startup-launched YouTube playback while sidebar timing still advanced.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Stopped pre-seeding youtubePlaybackFlowPending from startup CLI args so only the actual YouTube playback bootstrap window suppresses subtitle events. Added a regression test covering startup YouTube args and re-ran targeted YouTube/runtime subtitle tests plus typecheck.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
id: TASK-226
|
||||
title: Restore subtitle sidebar cues for auto-loaded YouTube subtitles
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-23 20:21'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
- youtube
|
||||
- subtitle-sidebar
|
||||
dependencies:
|
||||
- TASK-224
|
||||
- TASK-225
|
||||
priority: high
|
||||
ordinal: 148500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After fixing startup subtitle event suppression, the primary subtitle overlay updates for auto-loaded YouTube playback but the subtitle sidebar reports no parsed subtitle cues available. Investigate parsed subtitle source registration / refresh for auto-loaded YouTube subtitle files and restore sidebar cue population.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When YouTube auto-load succeeds, the subtitle sidebar receives parsed cues for the active primary subtitle source.
|
||||
- [x] #2 Auto-loaded YouTube subtitle source changes refresh the sidebar snapshot without requiring manual picker interaction.
|
||||
- [x] #3 A regression test covers the startup auto-load path where live primary subtitles render but sidebar cues remain empty.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Root cause: successful YouTube auto-load refreshed visible primary subtitle state, but did not explicitly initialize parsed subtitle cues from the resolved downloaded primary subtitle file. Sidebar cue population depended on later mpv source rediscovery, which could leave snapshots empty.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Added a regression test for YouTube auto-load sidebar cue refresh and wired the YouTube subtitle flow to explicitly refresh parsed subtitle cues from the resolved primary subtitle path after a successful load. Verified with targeted YouTube/sidebar/runtime tests plus typecheck.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: TASK-227
|
||||
title: 'Assess and address PR #31 latest CodeRabbit review round'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-24 03:53'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
priority: medium
|
||||
ordinal: 147500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the latest CodeRabbit review round on PR #31, verify each actionable comment against the current branch, implement only the valid fixes, add regression coverage where appropriate, and prepare thread replies for resolved or declined items.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Latest CodeRabbit comments on PR #31 are triaged into valid fixes vs non-actioned suggestions with rationale.
|
||||
- [x] #2 Confirmed issues are fixed with regression coverage where appropriate.
|
||||
- [x] #3 Relevant verification passes for the touched areas.
|
||||
- [x] #4 PR reply notes are ready for each addressed or declined latest-review comment.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Verify the five latest CodeRabbit inline comments against the current branch and separate valid bugs from non-actioned suggestions.
|
||||
2. Add failing regression coverage for confirmed issues in launcher playback tests, CLI YouTube flow error handling, and renderer YouTube picker disabled-state behavior.
|
||||
3. Implement the minimal production fixes for the confirmed issues, plus remove the duplicate overlay Anki initialization if still redundant.
|
||||
4. Inspect the YouTube primary-subtitle failure timer wiring to decide whether a code change is warranted in this round or whether a technical reply declining the comment is more correct.
|
||||
5. Run targeted Bun tests for the touched files and prepare concise PR thread replies for each latest-review comment.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Triaged the latest PR #31 CodeRabbit round: five inline comments were current action items; implemented all five. Strengthened the launcher playback test fixture so YouTube pause coverage no longer piggybacks on generic overlay auto-pause settings.
|
||||
|
||||
Added regression tests for CLI YouTube flow rejection handling, no-track picker disabled-state restoration, and app-owned YouTube notification suppression while subtitle acquisition is still in flight.
|
||||
|
||||
Implemented `runAsyncWithOsd(...)` handling for `args.youtubePlay`, kept no-track picker controls disabled after failed continue attempts, added `setAppOwnedFlowInFlight(...)` to the YouTube primary-subtitle notification runtime with main-process wiring around `runYoutubePlaybackFlowMain(...)`, and removed the duplicate `initializeOverlayAnkiIntegrationCore(...)` call from `initializeOverlayRuntime()`.
|
||||
|
||||
Verification passed: `bun test launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts src/renderer/modals/youtube-track-picker.test.ts src/main/runtime/youtube-primary-subtitle-notification.test.ts` and `bun run typecheck`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the latest CodeRabbit review round on PR #31 and implemented all five current inline action items. Strengthened the launcher playback regression test so app-owned YouTube pause behavior is asserted independently from generic overlay auto-pause settings, wrapped the CLI `youtubePlay` branch in the existing `runAsyncWithOsd(...)` path so probe/download/startup failures surface in logs and OSD, kept the no-track YouTube picker controls disabled after rejected continue attempts, suppressed the generic primary-subtitle failure timer while the app-owned YouTube flow is still probing/downloading and restarted it only after the flow settles, and removed the duplicate overlay Anki initialization from `initializeOverlayRuntime()`.
|
||||
|
||||
Verification passed with `bun test launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts src/renderer/modals/youtube-track-picker.test.ts src/main/runtime/youtube-primary-subtitle-notification.test.ts` and `bun run typecheck`.
|
||||
|
||||
Prepared thread-reply notes for the five latest inline comments; did not post them because GitHub replies are an external side effect.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: TASK-228
|
||||
title: 'Assess and address PR #31 subsequent CodeRabbit review round'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-24 04:10'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
- 'commit cdb12827 fix: address PR #31 latest review follow-ups'
|
||||
priority: medium
|
||||
ordinal: 146500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the subsequent CodeRabbit review round on PR #31 after commit cdb12827, verify each newly reported issue against the current branch, implement the valid fixes with regression coverage where appropriate, and prepare/update PR thread replies.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 New CodeRabbit comments after cdb12827 are triaged into valid fixes vs declined suggestions with rationale.
|
||||
- [x] #2 Confirmed issues are fixed with regression coverage where appropriate.
|
||||
- [x] #3 Relevant verification passes for the touched areas.
|
||||
- [x] #4 PR threads are updated for the addressed comments.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Verify the new CodeRabbit comments after cdb12827 and separate valid bugs from refactor-only suggestions.
|
||||
2. Add failing regression coverage for the valid runtime issues: `track.selected` fallback in the YouTube primary-subtitle notifier and consistent no-track handling in the picker.
|
||||
3. Inspect existing test seams for the `main.ts` flow-entry guards; if lightweight coverage exists, add it before patching. Otherwise apply the minimal `main.ts` fixes and rely on typecheck plus targeted regression tests around the affected runtime helpers.
|
||||
4. Implement the confirmed fixes: picker re-entry guard, broader `inFlight` cleanup, `track.selected` fallback, and a single canonical `hasTracks` check.
|
||||
5. Run targeted tests/typecheck and update the new PR threads with landed fix refs.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Triaged the post-cdb12827 CodeRabbit round. Implemented the 4 concrete follow-ups: manual picker re-entry guard, broader `setAppOwnedFlowInFlight(...)` cleanup, `track.selected` fallback in the YouTube primary-subtitle notifier, and a single canonical `payloadHasTracks(...)` helper in the picker. Also took the adjacent `replaceChildren()` cleanup while touching the same picker paths.
|
||||
|
||||
Verification passed: `bun test src/main/runtime/youtube-primary-subtitle-notification.test.ts src/renderer/modals/youtube-track-picker.test.ts launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts` and `bun run typecheck`.
|
||||
|
||||
Updated the new CodeRabbit inline threads with landed fix refs and left a top-level PR comment noting the large refactor suggestions are intentionally out of scope for this bugfix round.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the subsequent CodeRabbit review round on PR #31 after cdb12827 and applied the valid follow-ups in commit 5f6f93cd. Added a guard in `openYoutubeTrackPickerFromPlayback()` so the manual picker cannot re-enter while another YouTube flow session is active, widened the app-owned in-flight suppression to cover synchronous Windows mpv bootstrap and connect failures, taught the primary-subtitle notifier to honor `track.selected` before `sid` arrives, and unified the picker’s subtitle-availability logic behind `payloadHasTracks(...)` while swapping node clearing to `replaceChildren()`.
|
||||
|
||||
Verification passed with `bun test src/main/runtime/youtube-primary-subtitle-notification.test.ts src/renderer/modals/youtube-track-picker.test.ts launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts` and `bun run typecheck`.
|
||||
|
||||
Updated the latest inline CodeRabbit threads plus a top-level PR comment summarizing the round and explicitly deferred the large refactor suggestions as non-blocking maintainability nits.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
id: TASK-229
|
||||
title: 'Address PR #31 final CodeRabbit picker test follow-up'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-24 04:27'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
- >-
|
||||
CodeRabbit comment on src/renderer/modals/youtube-track-picker.test.ts
|
||||
global restoration / harness duplication
|
||||
priority: medium
|
||||
ordinal: 145500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Fix the remaining CodeRabbit comment on the YouTube picker test file by restoring absent globals correctly and reducing repeated test harness setup so global stubbing is consistent and isolated.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Picker tests restore `window`, `document`, and `CustomEvent` without leaving undefined-valued globals behind.
|
||||
- [x] #2 Repeated picker test setup is consolidated enough to remove the current review complaint.
|
||||
- [x] #3 Relevant picker tests pass and PR thread is updated.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing regression around global restoration semantics in the YouTube picker test harness.
|
||||
2. Extract shared DOM/environment helpers and restore logic using delete when globals were originally absent.
|
||||
3. Re-run focused tests and typecheck, then commit/push and reply on the PR thread.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Latest CodeRabbit comment targets youtube-track-picker.test.ts harness cleanup and correct restoration of global properties.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Addressed the last PR #31 CodeRabbit comment by refactoring the YouTube picker test harness to use shared DOM/env helpers, restoring absent globals via delete semantics, adding a regression for cleanup behavior, and pushing commit 039e2f56 with focused picker tests plus typecheck passing.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user