mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-09 04:19:27 -07:00
feat(stats): use generic title wording for mixed-source library
The stats dashboard now supports both anime series and YouTube videos
in the same library, so the anime-only copy no longer fits. Rename
user-visible labels ("Active Anime", "Search anime…", "Anime — Per
Day", "Episodes per Anime", "Words In Multiple Anime", etc.) to use
"Title"/"Library" wording that covers either source.
Data-model names (animeId, animeCount, useAnimeLibrary) stay as-is;
this pass only touches strings the user actually reads.
This commit is contained in:
@@ -93,7 +93,7 @@ export function AnimeTab({
|
|||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search anime..."
|
placeholder="Search library..."
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
className="flex-1 bg-ctp-surface0 border border-ctp-surface1 rounded-lg px-3 py-2 text-sm text-ctp-text placeholder:text-ctp-overlay2 focus:outline-none focus:border-ctp-blue"
|
className="flex-1 bg-ctp-surface0 border border-ctp-surface1 rounded-lg px-3 py-2 text-sm text-ctp-text placeholder:text-ctp-overlay2 focus:outline-none focus:border-ctp-blue"
|
||||||
@@ -125,12 +125,12 @@ export function AnimeTab({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-ctp-overlay2 shrink-0">
|
<div className="text-xs text-ctp-overlay2 shrink-0">
|
||||||
{filtered.length} anime · {formatDuration(totalMs)}
|
{filtered.length} titles · {formatDuration(totalMs)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{filtered.length === 0 ? (
|
{filtered.length === 0 ? (
|
||||||
<div className="text-sm text-ctp-overlay2 p-4">No anime found</div>
|
<div className="text-sm text-ctp-overlay2 p-4">No titles found</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={`grid ${GRID_CLASSES[cardSize]} gap-4`}>
|
<div className={`grid ${GRID_CLASSES[cardSize]} gap-4`}>
|
||||||
{filtered.map((item) => (
|
{filtered.map((item) => (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function HeroStats({ summary, sessions }: HeroStatsProps) {
|
|||||||
/>
|
/>
|
||||||
<StatCard label="Current Streak" value={`${summary.streakDays}d`} color="text-ctp-peach" />
|
<StatCard label="Current Streak" value={`${summary.streakDays}d`} color="text-ctp-peach" />
|
||||||
<StatCard
|
<StatCard
|
||||||
label="Active Anime"
|
label="Active Titles"
|
||||||
value={formatNumber(summary.activeAnimeCount)}
|
value={formatNumber(summary.activeAnimeCount)}
|
||||||
color="text-ctp-mauve"
|
color="text-ctp-mauve"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export function TrackingSnapshot({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip text="Total unique episodes (videos) watched across all anime">
|
<Tooltip text="Total unique videos watched across all titles in your library">
|
||||||
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
||||||
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Episodes</div>
|
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Episodes</div>
|
||||||
<div className="mt-1 text-xl font-semibold font-mono tabular-nums text-ctp-blue">
|
<div className="mt-1 text-xl font-semibold font-mono tabular-nums text-ctp-blue">
|
||||||
@@ -79,9 +79,9 @@ export function TrackingSnapshot({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip text="Number of anime series fully completed">
|
<Tooltip text="Number of titles fully completed">
|
||||||
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
||||||
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Anime</div>
|
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Titles</div>
|
||||||
<div className="mt-1 text-xl font-semibold font-mono tabular-nums text-ctp-sapphire">
|
<div className="mt-1 text-xl font-semibold font-mono tabular-nums text-ctp-sapphire">
|
||||||
{formatNumber(summary.totalAnimeCompleted)}
|
{formatNumber(summary.totalAnimeCompleted)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export function SessionRow({
|
|||||||
}}
|
}}
|
||||||
aria-label={`View overview for ${session.canonicalTitle ?? 'Unknown Media'}`}
|
aria-label={`View overview for ${session.canonicalTitle ?? 'Unknown Media'}`}
|
||||||
className="absolute right-10 top-1/2 -translate-y-1/2 w-5 h-5 rounded border border-ctp-surface2 text-transparent hover:border-ctp-blue/50 hover:text-ctp-blue hover:bg-ctp-blue/10 transition-colors opacity-0 group-hover:opacity-100 focus:opacity-100 flex items-center justify-center"
|
className="absolute right-10 top-1/2 -translate-y-1/2 w-5 h-5 rounded border border-ctp-surface2 text-transparent hover:border-ctp-blue/50 hover:text-ctp-blue hover:bg-ctp-blue/10 transition-colors opacity-0 group-hover:opacity-100 focus:opacity-100 flex items-center justify-center"
|
||||||
title="View anime overview"
|
title="View in Library"
|
||||||
>
|
>
|
||||||
{'\u2197'}
|
{'\u2197'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export function TrendsTab() {
|
|||||||
type="line"
|
type="line"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SectionHeader>Anime — Per Day</SectionHeader>
|
<SectionHeader>Library — Per Day</SectionHeader>
|
||||||
<AnimeVisibilityFilter
|
<AnimeVisibilityFilter
|
||||||
animeTitles={animeTitles}
|
animeTitles={animeTitles}
|
||||||
hiddenAnime={activeHiddenAnime}
|
hiddenAnime={activeHiddenAnime}
|
||||||
@@ -239,21 +239,21 @@ export function TrendsTab() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<StackedTrendChart title="Episodes per Anime" data={filteredEpisodesPerAnime} />
|
<StackedTrendChart title="Videos per Title" data={filteredEpisodesPerAnime} />
|
||||||
<StackedTrendChart title="Watch Time per Anime (min)" data={filteredWatchTimePerAnime} />
|
<StackedTrendChart title="Watch Time per Title (min)" data={filteredWatchTimePerAnime} />
|
||||||
<StackedTrendChart
|
<StackedTrendChart
|
||||||
title="Cards Mined per Anime"
|
title="Cards Mined per Title"
|
||||||
data={filteredCardsPerAnime}
|
data={filteredCardsPerAnime}
|
||||||
colorPalette={cardsMinedStackedColors}
|
colorPalette={cardsMinedStackedColors}
|
||||||
/>
|
/>
|
||||||
<StackedTrendChart title="Words Seen per Anime" data={filteredWordsPerAnime} />
|
<StackedTrendChart title="Words Seen per Title" data={filteredWordsPerAnime} />
|
||||||
<StackedTrendChart title="Lookups per Anime" data={filteredLookupsPerAnime} />
|
<StackedTrendChart title="Lookups per Title" data={filteredLookupsPerAnime} />
|
||||||
<StackedTrendChart
|
<StackedTrendChart
|
||||||
title="Lookups/100w per Anime"
|
title="Lookups/100w per Title"
|
||||||
data={filteredLookupsPerHundredPerAnime}
|
data={filteredLookupsPerHundredPerAnime}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SectionHeader>Anime — Cumulative</SectionHeader>
|
<SectionHeader>Library — Cumulative</SectionHeader>
|
||||||
<StackedTrendChart title="Watch Time Progress (min)" data={filteredWatchTimeProgress} />
|
<StackedTrendChart title="Watch Time Progress (min)" data={filteredWatchTimeProgress} />
|
||||||
<StackedTrendChart title="Episodes Progress" data={filteredAnimeProgress} />
|
<StackedTrendChart title="Episodes Progress" data={filteredAnimeProgress} />
|
||||||
<StackedTrendChart
|
<StackedTrendChart
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export function CrossAnimeWordsTable({
|
|||||||
>
|
>
|
||||||
{'\u25B6'}
|
{'\u25B6'}
|
||||||
</span>
|
</span>
|
||||||
Words In Multiple Anime
|
Words Across Multiple Titles
|
||||||
</button>
|
</button>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{hasKnownData && (
|
{hasKnownData && (
|
||||||
@@ -97,8 +97,8 @@ export function CrossAnimeWordsTable({
|
|||||||
{collapsed ? null : ranked.length === 0 ? (
|
{collapsed ? null : ranked.length === 0 ? (
|
||||||
<div className="text-xs text-ctp-overlay2 mt-3">
|
<div className="text-xs text-ctp-overlay2 mt-3">
|
||||||
{hideKnown
|
{hideKnown
|
||||||
? 'All multi-anime words are already known!'
|
? 'All words that span multiple titles are already known!'
|
||||||
: 'No words found across multiple anime.'}
|
: 'No words found across multiple titles.'}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -109,7 +109,7 @@ export function CrossAnimeWordsTable({
|
|||||||
<th className="text-left py-2 pr-3 font-medium">Word</th>
|
<th className="text-left py-2 pr-3 font-medium">Word</th>
|
||||||
<th className="text-left py-2 pr-3 font-medium">Reading</th>
|
<th className="text-left py-2 pr-3 font-medium">Reading</th>
|
||||||
<th className="text-left py-2 pr-3 font-medium w-20">POS</th>
|
<th className="text-left py-2 pr-3 font-medium w-20">POS</th>
|
||||||
<th className="text-right py-2 pr-3 font-medium w-16">Anime</th>
|
<th className="text-right py-2 pr-3 font-medium w-16">Titles</th>
|
||||||
<th className="text-right py-2 font-medium w-16">Seen</th>
|
<th className="text-right py-2 font-medium w-16">Seen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
Reference in New Issue
Block a user