From 0ea17461235f50774bb115ca8928f866a12378ae Mon Sep 17 00:00:00 2001 From: sudacode Date: Thu, 19 Mar 2026 21:42:53 -0700 Subject: [PATCH] =?UTF-8?q?feat(stats):=20add=20media-detail=20navigation?= =?UTF-8?q?=20from=20Sessions=20rows;=20fix(tokenizer):=20exclude=20?= =?UTF-8?q?=E3=81=9D=E3=81=86=E3=81=A0=20auxiliary-stem=20from=20annotatio?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added hover-revealed ↗ button on SessionRow that navigates to the anime media-detail view for the session's videoId - Added `sessions` origin type to MediaDetailOrigin and openSessionsMediaDetail() / closeMediaDetail() handling so the back button returns correctly to the Sessions tab ("Back to Sessions") - Wired onNavigateToMediaDetail down through SessionsTab → SessionRow - Excluded tokens with MeCab POS3 = 助動詞語幹 (e.g. そうだ grammar tails) from subtitle annotation metadata so frequency, JLPT, and N+1 styling no longer apply to grammar-tail tokens - Added annotation-stage unit test and end-to-end tokenizeSubtitle test for the そうだ exclusion path - Updated docs-site changelog, immersion-tracking, and subtitle-annotations pages to reflect both changes Co-Authored-By: Claude Sonnet 4.6 --- docs-site/changelog.md | 2 ++ docs-site/immersion-tracking.md | 2 +- docs-site/subtitle-annotations.md | 2 +- stats/src/App.tsx | 15 ++++++++- stats/src/components/sessions/SessionRow.tsx | 16 ++++++++++ stats/src/components/sessions/SessionsTab.tsx | 8 ++++- stats/src/lib/stats-navigation.ts | 32 ++++++++++++++++++- 7 files changed, 72 insertions(+), 5 deletions(-) diff --git a/docs-site/changelog.md b/docs-site/changelog.md index 9cc40bc..9c8348d 100644 --- a/docs-site/changelog.md +++ b/docs-site/changelog.md @@ -6,6 +6,8 @@ - Improved stats accuracy and scale handling with Yomitan token counts, full session timelines, known-word timeline fixes, cross-media vocabulary fixes, and clearer session charts. - Improved overlay/runtime stability with quieter macOS fullscreen recovery, reduced repeated loading OSD popups, and better frequency/noise handling for subtitle annotations. - Added launcher mpv-args passthrough plus Linux plugin wrapper-name fallback for packaged installs. +- Added a hover-revealed ↗ button on Sessions tab rows to navigate directly to the anime media-detail view, with correct "Back to Sessions" back-navigation. +- Excluded auxiliary-stem `そうだ` grammar tails (MeCab POS3 `助動詞語幹`) from subtitle annotation metadata so frequency, JLPT, and N+1 styling no longer bleed onto grammar-tail tokens. ## v0.6.5 (2026-03-15) - Seeded the AUR checkout with the repo `.SRCINFO` template before rewriting metadata so tagged releases do not depend on prior AUR state. diff --git a/docs-site/immersion-tracking.md b/docs-site/immersion-tracking.md index 076c396..0614dd2 100644 --- a/docs-site/immersion-tracking.md +++ b/docs-site/immersion-tracking.md @@ -52,7 +52,7 @@ Watch time, sessions, words seen, and per-anime progress/pattern charts with con #### Sessions -Expandable session history with new-word activity, cumulative totals, and pause/seek/card markers. +Expandable session history with new-word activity, cumulative totals, and pause/seek/card markers. Each session row exposes a hover-revealed ↗ button that navigates to the anime media-detail view for that session; pressing the back button there returns to the Sessions tab. ![Stats Sessions](/screenshots/stats-sessions.png) diff --git a/docs-site/subtitle-annotations.md b/docs-site/subtitle-annotations.md index 6dea3b8..686908c 100644 --- a/docs-site/subtitle-annotations.md +++ b/docs-site/subtitle-annotations.md @@ -4,7 +4,7 @@ SubMiner annotates subtitle tokens in real time as they appear in the overlay. F All four are opt-in and configured under `subtitleStyle`, `ankiConnect.knownWords`, and `ankiConnect.nPlusOne` in your config. They apply independently — you can enable any combination. -Before any of those layers render, SubMiner strips annotation metadata from tokens that are usually just subtitle glue or annotation noise. Standalone particles, auxiliaries, adnominals, common explanatory endings like `んです` / `のだ`, merged trailing quote-particle forms like `...って`, repeated kana interjections, and similar non-lexical helper tokens remain hoverable in the subtitle text, but they render as plain tokens without known-word, N+1, frequency, JLPT, or name-match annotation styling. +Before any of those layers render, SubMiner strips annotation metadata from tokens that are usually just subtitle glue or annotation noise. Standalone particles, auxiliaries, adnominals, common explanatory endings like `んです` / `のだ`, merged trailing quote-particle forms like `...って`, auxiliary-stem grammar tails like `そうだ` (MeCab POS3 `助動詞語幹`), repeated kana interjections, and similar non-lexical helper tokens remain hoverable in the subtitle text, but they render as plain tokens without known-word, N+1, frequency, JLPT, or name-match annotation styling. ## N+1 Word Highlighting diff --git a/stats/src/App.tsx b/stats/src/App.tsx index 9d93a57..2fa9bc4 100644 --- a/stats/src/App.tsx +++ b/stats/src/App.tsx @@ -10,6 +10,7 @@ import { navigateToSession as navigateToSessionState, openAnimeEpisodeDetail, openOverviewMediaDetail, + openSessionsMediaDetail, switchTab, } from './lib/stats-navigation'; @@ -110,6 +111,10 @@ export function App() { [], ); + const navigateToSessionsMediaDetail = useCallback((videoId: number) => { + setViewState((prev) => openSessionsMediaDetail(prev, videoId)); + }, []); + const openWordDetail = useCallback((wordId: number) => { setGlobalWordId(wordId); }, []); @@ -155,7 +160,14 @@ export function App() { } onBack={() => setViewState((prev) => closeMediaDetail(prev))} backLabel={ - mediaDetail.origin.type === 'overview' ? 'Back to Overview' : 'Back to Library' + mediaDetail.origin.type === 'overview' + ? 'Back to Overview' + : mediaDetail.origin.type === 'sessions' + ? 'Back to Sessions' + : 'Back to Library' + } + onNavigateToAnime={ + mediaDetail.origin.type === 'anime' ? undefined : navigateToAnime } /> @@ -242,6 +254,7 @@ export function App() { onClearInitialSession={() => setViewState((prev) => ({ ...prev, focusedSessionId: null })) } + onNavigateToMediaDetail={navigateToSessionsMediaDetail} /> diff --git a/stats/src/components/sessions/SessionRow.tsx b/stats/src/components/sessions/SessionRow.tsx index dc30825..1d4d503 100644 --- a/stats/src/components/sessions/SessionRow.tsx +++ b/stats/src/components/sessions/SessionRow.tsx @@ -11,6 +11,7 @@ interface SessionRowProps { onToggle: () => void; onDelete: () => void; deleteDisabled?: boolean; + onNavigateToMediaDetail?: (videoId: number) => void; } function CoverThumbnail({ @@ -56,6 +57,7 @@ export function SessionRow({ onToggle, onDelete, deleteDisabled = false, + onNavigateToMediaDetail, }: SessionRowProps) { const displayWordCount = getSessionDisplayWordCount(session); const knownWordsSeen = session.knownWordsSeen; @@ -109,6 +111,20 @@ export function SessionRow({ {'\u25B8'} + {onNavigateToMediaDetail != null && session.videoId != null ? ( + + ) : null}