chore: apply remaining workspace formatting and updates

This commit is contained in:
2026-03-16 01:54:35 -07:00
parent 77c35c770d
commit a9e33618e7
82 changed files with 1530 additions and 736 deletions

View File

@@ -1,7 +1,13 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import type { DailyRollup, OverviewData, SessionSummary, StreakCalendarDay, VocabularyEntry } from '../types/stats';
import type {
DailyRollup,
OverviewData,
SessionSummary,
StreakCalendarDay,
VocabularyEntry,
} from '../types/stats';
import {
buildOverviewSummary,
buildStreakCalendar,
@@ -49,7 +55,14 @@ test('buildOverviewSummary aggregates tracked totals and recent windows', () =>
const overview: OverviewData = {
sessions,
rollups,
hints: { totalSessions: 1, activeSessions: 0, episodesToday: 2, activeAnimeCount: 3, totalEpisodesWatched: 5, totalAnimeCompleted: 1 },
hints: {
totalSessions: 1,
activeSessions: 0,
episodesToday: 2,
activeAnimeCount: 3,
totalEpisodesWatched: 5,
totalAnimeCompleted: 1,
},
};
const summary = buildOverviewSummary(overview, now);

View File

@@ -1,4 +1,10 @@
import type { DailyRollup, KanjiEntry, OverviewData, StreakCalendarDay, VocabularyEntry } from '../types/stats';
import type {
DailyRollup,
KanjiEntry,
OverviewData,
StreakCalendarDay,
VocabularyEntry,
} from '../types/stats';
import { epochDayToDate, localDayFromMs } from './formatters';
export interface ChartPoint {
@@ -110,7 +116,9 @@ function buildAggregatedDailyRows(rollups: DailyRollup[]) {
averageSessionMinutes:
value.sessions > 0 ? +(value.activeMin / value.sessions).toFixed(1) : 0,
lookupHitRate:
value.lookupWeight > 0 ? Math.round((value.lookupHitRateSum / value.lookupWeight) * 100) : 0,
value.lookupWeight > 0
? Math.round((value.lookupHitRateSum / value.lookupWeight) * 100)
: 0,
}));
}
@@ -142,7 +150,10 @@ export function buildOverviewSummary(
return {
todayActiveMs: Math.max(todayActiveFromRollup, todayActiveFromSessions),
todayCards: Math.max(todayRow?.cards ?? 0, sumBy(todaySessions, (session) => session.cardsMined)),
todayCards: Math.max(
todayRow?.cards ?? 0,
sumBy(todaySessions, (session) => session.cardsMined),
),
streakDays,
allTimeHours: Math.round(sumBy(aggregated, (row) => row.activeMin) / 60),
totalTrackedCards: Math.max(sessionCards, rollupCards),
@@ -152,17 +163,21 @@ export function buildOverviewSummary(
totalAnimeCompleted: overview.hints.totalAnimeCompleted ?? 0,
averageSessionMinutes:
overview.sessions.length > 0
? Math.round(sumBy(overview.sessions, (session) => session.activeWatchedMs) / overview.sessions.length / 60_000)
? Math.round(
sumBy(overview.sessions, (session) => session.activeWatchedMs) /
overview.sessions.length /
60_000,
)
: 0,
totalSessions: overview.hints.totalSessions,
activeDays: daysWithActivity.size,
recentWatchTime: aggregated.slice(-14).map((row) => ({ label: row.label, value: row.activeMin })),
recentWatchTime: aggregated
.slice(-14)
.map((row) => ({ label: row.label, value: row.activeMin })),
};
}
export function buildTrendDashboard(
rollups: DailyRollup[],
): TrendDashboard {
export function buildTrendDashboard(rollups: DailyRollup[]): TrendDashboard {
const aggregated = buildAggregatedDailyRows(rollups);
return {
watchTime: aggregated.map((row) => ({ label: row.label, value: row.activeMin })),

View File

@@ -1,12 +1,24 @@
import type {
OverviewData, DailyRollup, MonthlyRollup,
SessionSummary, SessionTimelinePoint, SessionEvent,
VocabularyEntry, KanjiEntry,
OverviewData,
DailyRollup,
MonthlyRollup,
SessionSummary,
SessionTimelinePoint,
SessionEvent,
VocabularyEntry,
KanjiEntry,
VocabularyOccurrenceEntry,
MediaLibraryItem, MediaDetailData,
AnimeLibraryItem, AnimeDetailData, AnimeWord,
StreakCalendarDay, EpisodesPerDay, NewAnimePerDay, WatchTimePerAnime,
WordDetailData, KanjiDetailData,
MediaLibraryItem,
MediaDetailData,
AnimeLibraryItem,
AnimeDetailData,
AnimeWord,
StreakCalendarDay,
EpisodesPerDay,
NewAnimePerDay,
WatchTimePerAnime,
WordDetailData,
KanjiDetailData,
EpisodeDetailData,
} from '../types/stats';
@@ -47,7 +59,9 @@ interface StatsElectronAPI {
getKanjiDetail: (kanjiId: number) => Promise<KanjiDetailData>;
getEpisodeDetail: (videoId: number) => Promise<EpisodeDetailData>;
ankiBrowse: (noteId: number) => Promise<void>;
ankiNotesInfo: (noteIds: number[]) => Promise<Array<{ noteId: number; fields: Record<string, { value: string }> }>>;
ankiNotesInfo: (
noteIds: number[],
) => Promise<Array<{ noteId: number; fields: Record<string, { value: string }> }>>;
hideOverlay: () => void;
};
}