Compare commits

..

44 Commits

Author SHA1 Message Date
b061b265c2 chore(vendor): bump subminer-yomitan 2026-03-17 20:12:42 -07:00
f2b3af17d7 docs: update docs, add backlog tasks and change notes
Update configuration, immersion tracking, and mining workflow docs.
Add backlog tasks for upcoming work items and change notes for recent
features and fixes.
2026-03-17 20:12:42 -07:00
5698121996 chore: minor fixes and cleanup across services and renderer 2026-03-17 20:12:41 -07:00
f8e2ae4887 feat: overhaul stats dashboard with navigation, trends, and anime views
Add navigation state machine for tab/detail routing, anime overview
stats with Yomitan lookup rates, session word count accuracy fixes,
vocabulary tab hook order fix, simplified trends data fetching from
backend-aggregated endpoints, and improved session detail charts.
2026-03-17 20:12:41 -07:00
08a5401a7d feat: add background stats server daemon lifecycle
Implement `subminer stats -b` to start a background stats daemon and
`subminer stats -s` to stop it, with PID-based process lifecycle
management, single-instance lock bypass for daemon mode, and automatic
reuse of running daemon instances.
2026-03-17 20:12:41 -07:00
55ee12e87f feat: refactor immersion tracker queries and session word tracking
Add comprehensive query helpers for session deletion with word aggregate
refresh, known-words-per-session timeline, anime-level word summaries,
and trends dashboard aggregation. Track yomitanLookupCount in session
metrics and support bulk session operations.
2026-03-17 20:12:41 -07:00
a5a6426fe1 feat: add mark-as-watched keybinding and Yomitan lookup tracking
Add configurable keybinding to mark the current video as watched with
IPC plumbing between renderer and main process. Add event listener
infrastructure for tracking Yomitan dictionary lookups per session.
2026-03-17 20:12:41 -07:00
75f2c212c7 refactor: centralize watch threshold constant
Extract DEFAULT_MIN_WATCH_RATIO (0.85) into src/shared/watch-threshold.ts
to replace the hardcoded ANILIST_UPDATE_MIN_WATCH_RATIO in main.ts.
2026-03-17 20:05:08 -07:00
3dd337f518 update backlog tasks 2026-03-17 20:05:08 -07:00
94ec28b48c refactor: isolate character dictionary completion handling 2026-03-17 20:05:08 -07:00
078792e0b2 docs: refresh immersion and stats documentation 2026-03-17 20:05:08 -07:00
390ae1b2f2 feat: optimize stats dashboard data and components 2026-03-17 20:05:08 -07:00
11710f20db feat: stabilize startup sync and overlay/runtime paths 2026-03-17 20:05:08 -07:00
de574c04bd Add isolated typecheck test for get_frequency script
- add `scripts/get_frequency.test.ts` to verify `scripts/get_frequency.ts` typechecks alone
- remove duplicate `yomitanSession` property from runtime state/interface
2026-03-17 20:05:08 -07:00
a9e33618e7 chore: apply remaining workspace formatting and updates 2026-03-17 20:05:08 -07:00
77c35c770d chore: add stats lint/check wiring for CI 2026-03-17 20:05:08 -07:00
64e9821e7a chore(backlog): sync task metadata and archives 2026-03-17 20:05:08 -07:00
5c529802c6 fix(stats): restore cross-anime words table 2026-03-17 20:05:08 -07:00
8123a145c0 fix(plugin): add lowercase linux binary fallbacks 2026-03-17 20:05:07 -07:00
659118c20c docs: document stats page mining, word exclusions, and vocabulary UX improvements 2026-03-17 20:05:07 -07:00
929159bba5 test(renderer): verify excluded interjections remain visible as non-interactive text 2026-03-17 20:05:07 -07:00
a317019bb9 feat(tokenizer): exclude interjections and sound effects from subtitle annotations
- Filter out 感動詞 (interjection) POS1 tokens from annotation payloads
- Exclude common interjection terms (ああ, ええ, はあ, etc.)
- Exclude reduplicated kana SFX with optional trailing と
- shouldExcludeTokenFromSubtitleAnnotations checks both POS1 and term patterns
- filterSubtitleAnnotationTokens applied after annotation stage
2026-03-17 20:05:07 -07:00
5767667d51 feat(stats): add mine card from stats page with Yomitan bridge
- POST /api/stats/mine-card endpoint with mode=word|sentence|audio
- mode=word: creates full Yomitan card (definition/reading/pitch) via hidden search page bridge
- mode=sentence/audio: creates card directly with Lapis/Kiku flags
- Audio + image generated in parallel from source video via ffmpeg
- Respects all AnkiConnect config (AVIF, static, field mappings, metadata pattern)
- addYomitanNoteViaSearch calls window.__subminerAddNote exposed by Yomitan fork
- Syncs AnkiConnect URL to Yomitan before each word mine
2026-03-17 20:05:07 -07:00
a1f30fd482 feat(tracking): store secondary subtitle text and source path in occurrence data
- Add secondary_text column to imm_subtitle_lines with migration
- Pass currentSecondarySubText through recordSubtitleLine flow
- Include secondaryText and sourcePath in word/kanji occurrence queries
- Update all type interfaces (backend + frontend)
2026-03-17 20:05:07 -07:00
5a30446809 feat(stats): add click handler to bar charts for word detail navigation 2026-03-17 20:05:07 -07:00
6634255f43 feat(stats): fix truncated readings and improve word detail UX
- fullReading() reconstructs full word reading from headword + partial stored reading
- FrequencyRankTable always shows reading for every row
- Word highlighted in example sentences with underline style
- Bar chart clicks open word detail panel
2026-03-17 20:05:07 -07:00
a3ed8dcf3d feat(stats): add word exclusion list for vocabulary tab
- useExcludedWords hook manages excluded words in localStorage
- ExclusionManager modal for viewing/removing excluded words
- Exclusion button on WordDetailPanel header
- Filtered words affect stat cards, charts, frequency table, and word list
2026-03-17 20:05:07 -07:00
92c1557e46 refactor: split known words config from n-plus-one 2026-03-17 20:05:07 -07:00
04682a02cc feat: improve stats dashboard and annotation settings 2026-03-17 20:05:07 -07:00
650e95cdc3 Feature/renderer performance (#24) 2026-03-17 20:05:07 -07:00
46fbea902a Harden stats APIs and fix Electron Yomitan debug runtime
- Validate stats session IDs/limits and add AnkiConnect request timeouts
- Stabilize stats window/runtime lifecycle and tighten window security defaults
- Fix Electron CLI debug startup by unsetting `ELECTRON_RUN_AS_NODE` and wiring Yomitan session state
- Expand regression coverage for tracker queries/events ordering and session aggregates
- Update docs for stats dashboard usage and Yomitan lookup troubleshooting
2026-03-17 20:05:07 -07:00
93811ebfde fix(launcher): default stats cleanup to vocab mode 2026-03-17 20:05:07 -07:00
74544d79a7 docs: update stats dashboard docs, config, and releasing checklist
- Update dashboard tab descriptions to include Anime tab and richer session timelines
- Add autoOpenBrowser config option to stats section
- Add subminer stats cleanup command to changelog fragment
- Expand releasing checklist with doc verification, changelog lint, and build gates
2026-03-17 20:05:07 -07:00
536f0a1315 feat(stats): redesign session timeline and clean up vocabulary tab
- Replace cumulative line chart with activity-focused area chart showing per-interval new words
- Add total words as a blue line on a secondary right Y-axis
- Add pause shaded regions, seek markers, and card mined markers with numeric x-axis for reliable rendering
- Add clickable header logo with proper aspect ratio
- Remove unused "Hide particles & single kana" checkbox from vocabulary tab
2026-03-17 20:05:07 -07:00
ff2d9141bc feat(stats): add episodes completed and anime completed to tracking snapshot
- Query watched videos count and anime with all episodes watched
- Display in overview tracking snapshot
- Remove efficiency section from trends
2026-03-17 20:05:07 -07:00
249a7cada8 chore: remove implementation plan documents 2026-03-17 20:05:07 -07:00
9530445a95 feat: add AniList rate limiter and remaining backlog tasks 2026-03-17 20:05:07 -07:00
2d87dae6cc docs: update documentation site for stats dashboard and immersion tracking 2026-03-17 20:05:07 -07:00
0f44107beb feat(stats): build anime-centric stats dashboard frontend
5-tab React dashboard with Catppuccin Mocha theme:
- Overview: hero stats, streak calendar, watch time chart, recent sessions
- Anime: grid with cover art, episode list with completion %, detail view
- Trends: 15 charts across Activity, Efficiency, Anime, and Patterns
- Vocabulary: POS-filtered word/kanji lists with detail panels
- Sessions: expandable session history with event timeline

Features:
- Cross-tab navigation (anime <-> vocabulary)
- Global word detail panel overlay
- Expandable episode detail with Anki card links (Expression field)
- Per-anime multi-line trend charts
- Watch time by day-of-week and hour-of-day
- Collapsible sections with accessibility (aria-expanded)
- Card size selector for anime grid
- Cover art caching via AniList
- HTTP API client with file:// protocol fallback for Electron overlay
2026-03-17 20:05:07 -07:00
950263bd66 feat(stats): add launcher stats command and build integration
- Launcher stats subcommand with cleanup mode
- Stats frontend build integrated into Makefile
- CI workflow updated for stats package
- Config example updated with stats section
- mpv plugin menu entry for stats toggle
2026-03-17 20:05:07 -07:00
26fb5b4162 feat(stats): wire stats server, overlay, and CLI into main process
- Stats server auto-start on immersion tracker init
- Stats overlay toggle via keybinding and IPC
- Stats CLI command (subminer stats) with cleanup mode
- mpv plugin menu integration for stats toggle
- CLI args for --stats, --stats-cleanup, --stats-response-path
2026-03-17 20:04:40 -07:00
ffe5c6e1c6 feat(stats): add stats server, API endpoints, config, and Anki integration
- Hono HTTP server with 20+ REST endpoints for stats data
- Stats overlay BrowserWindow with toggle keybinding
- IPC channel definitions and preload bridge
- Stats config section (toggleKey, serverPort, autoStartServer, autoOpenBrowser)
- Config resolver for stats section
- AnkiConnect proxy endpoints (guiBrowse, notesInfo)
- Note ID passthrough in card mining callback chain
- Stats CLI command with autoOpenBrowser respect
2026-03-17 20:04:40 -07:00
fe8bb167c4 feat(immersion): add anime metadata, occurrence tracking, and schema upgrades
- Add imm_anime table with AniList integration
- Add imm_subtitle_lines, imm_word_line_occurrences, imm_kanji_line_occurrences
- Add POS fields (part_of_speech, pos1, pos2, pos3) to imm_words
- Add anime metadata parsing with guessit fallback
- Add video duration tracking and watched status
- Add episode, streak, trend, and word/kanji detail queries
- Deduplicate subtitle line recording within sessions
- Pass Anki note IDs through card mining callback chain
2026-03-17 20:01:23 -07:00
cc5d270b8e docs: add stats dashboard design docs, plans, and knowledge base
- Stats dashboard redesign design and implementation plans
- Episode detail and Anki card link design
- Internal knowledge base restructure
- Backlog tasks for testing, verification, and occurrence tracking
2026-03-17 20:01:23 -07:00
558 changed files with 4709 additions and 34168 deletions

View File

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

5
.gitignore vendored
View File

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

View File

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

View File

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

290
README.md
View File

@@ -1,171 +1,44 @@
<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 />
<img src="assets/SubMiner.png" width="160" alt="SubMiner logo">
# SubMiner
## Turn mpv into a sentence-mining workstation.
Look up words with Yomitan, export to Anki in one key, track your immersion — all without leaving mpv.
[![License: GPL v3](https://img.shields.io/badge/license-GPLv3-1a1a2e?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0)
[![Platform](https://img.shields.io/badge/platform-Linux%20·%20macOS%20·%20Windows-1a1a2e?style=flat-square)](https://github.com/ksyasuda/SubMiner)
[![Docs](https://img.shields.io/badge/docs-docs.subminer.moe-e6a817?style=flat-square)](https://docs.subminer.moe)
[![AUR](https://img.shields.io/aur/version/subminer-bin?style=flat-square&color=1a1a2e)](https://aur.archlinux.org/packages/subminer-bin)
[![SubMiner demo](./assets/minecard.webp)](./assets/minecard.mp4)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Linux](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-informational)]()
[![Docs](https://img.shields.io/badge/docs-docs.subminer.moe-blueviolet)](https://docs.subminer.moe)
</div>
## 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.
<br />
<div align="center">
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan dictionary popup over annotated subtitles in mpv">
[![SubMiner demo (Animated preview)](./assets/minecard.webp)](./assets/minecard.mp4)
</div>
<br>
<br />
### Instant Anki Mining
## What it does
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.
SubMiner is an Electron overlay that sits on top of mpv. It turns your video player into a full sentence-mining workstation:
<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>
- **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
<br>
### Reading Annotations
Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Known words fade back; new words stand out. Grammar-only tokens render as plain text so you focus on what matters.
<div align="center">
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets">
</div>
<br>
### Immersion Dashboard
Local stats dashboard — watch time, anime library, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking.
<div align="center">
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
</div>
<br>
### Integrations
<table>
<tr>
<td><b>YouTube</b></td>
<td>Auto-loaded yt-dlp subtitle tracks at startup with a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
</tr>
<tr>
<td><b>AniList</b></td>
<td>Automatic episode tracking and progress sync</td>
</tr>
<tr>
<td><b>Jellyfin</b></td>
<td>Browse and launch media from your Jellyfin server</td>
</tr>
<tr>
<td><b>Jimaku</b></td>
<td>Search and download Japanese subtitles</td>
</tr>
<tr>
<td><b>alass / ffsubsync</b></td>
<td>Automatic subtitle retiming — requires <code>alass</code> or <code>ffsubsync</code> on your <code>PATH</code> (optional; subtitle syncing is disabled without them)</td>
</tr>
<tr>
<td><b>WebSocket</b></td>
<td>Annotated subtitle feed for external clients (texthooker pages, custom tools)</td>
</tr>
</table>
<div align="center">
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page receiving annotated subtitle lines via WebSocket">
</div>
<br>
---
## Requirements
| | Required | Optional |
| -------------- | --------------------------------------- | -------------------------------------- |
| **Player** | [`mpv`](https://mpv.io) with IPC socket | — |
| **Processing** | `ffmpeg`, `mecab` + `mecab-ipadic` | `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) |
| **Media** | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` |
| **Selection** | — | `fzf` / `rofi` |
> [!NOTE]
> [`bun`](https://bun.sh) is required if building from source or using the CLI wrapper: `subminer`. Pre-built releases (AppImage, DMG, installer) do not require it.
**Platform-specific:**
| Linux | macOS | Windows |
| ----------------------------------- | ------------------------ | ------------- |
| `hyprctl` or `xdotool` + `xwininfo` | Accessibility permission | No extra deps |
<details>
<summary><b>Arch Linux</b></summary>
```bash
paru -S --needed mpv ffmpeg mecab-git mecab-ipadic
# Optional
paru -S --needed yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo
# Optional: subtitle sync (install at least one for subtitle syncing to work)
paru -S --needed alass python-ffsubsync
# X11 / XWAYLAND
paru -S --needed xdotool xorg-xwininfo
```
</details>
<details>
<summary><b>macOS</b></summary>
```bash
brew install mpv ffmpeg mecab mecab-ipadic
# Optional
brew install yt-dlp fzf rofi chafa ffmpegthumbnailer
# Optional: subtitle sync (install at least one for subtitle syncing to work)
brew install alass
pip install ffsubsync
```
Grant Accessibility permission to SubMiner in **System Settings > Privacy & Security > Accessibility**.
</details>
<details>
<summary><b>Windows</b></summary>
Install [`mpv`](https://mpv.io/installation/) and [`ffmpeg`](https://ffmpeg.org/download.html) and ensure both are on your `PATH`.
For MeCab, install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary.
</details>
---
## Quick Start
## Quick start
### 1. Install
<details>
<summary><b>Arch Linux (AUR)</b></summary>
**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:
```bash
paru -S subminer-bin
@@ -174,85 +47,84 @@ 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
```
</details>
<details>
<summary><b>Linux (AppImage)</b></summary>
**Linux (AppImage):**
```bash
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
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`.
</details>
**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>
<summary><b>macOS</b></summary>
**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`.
Download the latest DMG or ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
**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).
</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
### 2. Launch the app once
```bash
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
# Linux
SubMiner.AppImage
```
---
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
Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more: **[docs.subminer.moe](https://docs.subminer.moe)**
---
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/).
## Acknowledgments
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 |
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).
## License

View File

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

View File

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

View File

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

View File

@@ -1,64 +0,0 @@
---
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 -->

View File

@@ -1,8 +0,0 @@
---
id: m-1
title: "Stats Dashboard"
---
## Description
Milestone: Stats Dashboard

View File

@@ -4,7 +4,6 @@ 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
@@ -20,17 +19,20 @@ 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.

View File

@@ -4,7 +4,6 @@ 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
@@ -18,17 +17,20 @@ 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.

View File

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

View File

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

View File

@@ -1,11 +1,11 @@
---
id: TASK-133
title: Improve AniList character dictionary parity with upstream guide
status: To Do
status: In Progress
assignee:
- OpenCode
created_date: '2026-03-08 21:06'
updated_date: '2026-03-18 05:27'
updated_date: '2026-03-10 06:18'
labels:
- dictionary
- anilist
@@ -24,7 +24,6 @@ documentation:
- >-
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-08-anilist-character-dictionary-parity.md
priority: high
ordinal: 3000
---
## Description

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ title: Refresh current subtitle after character dictionary sync completes
status: Done
assignee: []
created_date: '2026-03-09 00:00'
updated_date: '2026-03-18 05:28'
updated_date: '2026-03-16 05:13'
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: 42500
ordinal: 41500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Show character dictionary auto-sync progress on OSD
status: Done
assignee: []
created_date: '2026-03-09 01:10'
updated_date: '2026-03-18 05:28'
updated_date: '2026-03-16 05:13'
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: 41500
ordinal: 40500
---
## Description

View File

@@ -2,10 +2,9 @@
id: TASK-143
title: Keep character dictionary auto-sync non-blocking during startup
status: Done
assignee:
- codex
assignee: []
created_date: '2026-03-09 01:45'
updated_date: '2026-03-23 03:22'
updated_date: '2026-03-16 05:13'
labels:
- dictionary
- startup
@@ -18,7 +17,7 @@ references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/current-media-tokenization-gate.ts
priority: high
ordinal: 144500
ordinal: 37500
---
## Description
@@ -34,20 +33,8 @@ 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 -->

View File

@@ -6,7 +6,7 @@ title: >-
status: Done
assignee: []
created_date: '2026-03-09 10:40'
updated_date: '2026-03-18 05:28'
updated_date: '2026-03-16 05:13'
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: 37500
ordinal: 36500
---
## Description

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,65 @@
---
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 -->

View File

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

View File

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

View File

@@ -1,80 +0,0 @@
---
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 -->

View File

@@ -1,12 +1,11 @@
---
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-18 05:31'
updated_date: '2026-03-14 18:31'
labels: []
milestone: m-1
dependencies: []
priority: high
ordinal: 9010
@@ -15,19 +14,25 @@ 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:
@@ -36,4 +41,5 @@ Verification:
- `bun test src/core/services/immersion-tracker-service.test.ts src/core/services/immersion-tracker/__tests__/query.test.ts src/core/services/immersion-tracker/storage-session.test.ts launcher/parse-args.test.ts launcher/commands/command-modules.test.ts src/main/runtime/stats-cli-command.test.ts src/main/runtime/mpv-main-event-main-deps.test.ts src/core/services/cli-command.test.ts`
- `bun run docs:test`
- `bun run docs:build`
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -5,24 +5,20 @@ status: Done
assignee:
- '@codex'
created_date: '2026-03-16 10:45'
updated_date: '2026-03-18 05:28'
updated_date: '2026-03-16 23:04'
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: 54500
ordinal: 53000
---
## Description

View File

@@ -0,0 +1,64 @@
---
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 -->

View File

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

View File

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

View File

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

View File

@@ -1,65 +0,0 @@
---
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 -->

View File

@@ -1,63 +0,0 @@
---
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 -->

View File

@@ -1,65 +0,0 @@
---
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 -->

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,11 +2,10 @@
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-18 05:28'
updated_date: '2026-03-17 16:19'
labels: []
dependencies: []
references:
@@ -16,7 +15,6 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/__tests__/stats-server.test.ts
- /Users/sudacode/projects/japanese/SubMiner/stats/src/hooks/useSessions.ts
ordinal: 109500
---
## Description

View File

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

View File

@@ -1,67 +0,0 @@
---
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 sessions 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 helpers `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 -->

View File

@@ -2,11 +2,10 @@
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-18 05:28'
updated_date: '2026-03-17 16:29'
labels: []
dependencies: []
references:
@@ -14,7 +13,6 @@ references:
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/vocabulary/VocabularyTab.tsx
- /Users/sudacode/projects/japanese/SubMiner/stats/src/App.tsx
- /Users/sudacode/projects/japanese/SubMiner/stats/src/lib/api-client.ts
ordinal: 108500
---
## Description

View File

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

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