fix: align texthooker and stats formatting with CI expectations

This commit is contained in:
2026-03-18 19:01:29 -07:00
parent ec56970646
commit f4cce31d4a
11 changed files with 225 additions and 46 deletions

View File

@@ -55,7 +55,9 @@ export function MediaHeader({ detail, initialKnownWordsSummary = null }: MediaHe
<div className="text-xs text-ctp-overlay2">total watch time</div>
</div>
<div>
<div className="text-ctp-cards-mined font-medium">{formatNumber(detail.totalCards)}</div>
<div className="text-ctp-cards-mined font-medium">
{formatNumber(detail.totalCards)}
</div>
<div className="text-xs text-ctp-overlay2">cards mined</div>
</div>
<div>

View File

@@ -245,13 +245,13 @@ function AnimeGroupRow({
{group.sessions.length} sessions · {formatDuration(group.totalActiveMs)} active
</div>
</div>
<div className="flex gap-4 text-xs text-center shrink-0">
<div>
<div className="text-ctp-cards-mined font-medium font-mono tabular-nums">
{formatNumber(group.totalCards)}
<div className="flex gap-4 text-xs text-center shrink-0">
<div>
<div className="text-ctp-cards-mined font-medium font-mono tabular-nums">
{formatNumber(group.totalCards)}
</div>
<div className="text-ctp-overlay2">cards</div>
</div>
<div className="text-ctp-overlay2">cards</div>
</div>
<div>
<div className="text-ctp-mauve font-medium font-mono tabular-nums">
{formatNumber(group.totalWords)}

View File

@@ -1,4 +1,8 @@
import { formatEventSeconds, type SessionChartMarker, type SessionEventNoteInfo } from '../../lib/session-events';
import {
formatEventSeconds,
type SessionChartMarker,
type SessionEventNoteInfo,
} from '../../lib/session-events';
interface SessionEventPopoverProps {
marker: SessionChartMarker;
@@ -83,7 +87,8 @@ export function SessionEventPopover({
{marker.kind === 'seek' && (
<div className="space-y-1 text-xs text-ctp-subtext0">
<div>
From <span className="text-ctp-teal">{formatEventSeconds(marker.fromMs) ?? '\u2014'}</span>{' '}
From{' '}
<span className="text-ctp-teal">{formatEventSeconds(marker.fromMs) ?? '\u2014'}</span>{' '}
to <span className="text-ctp-teal">{formatEventSeconds(marker.toMs) ?? '\u2014'}</span>
</div>
<div>
@@ -120,7 +125,9 @@ export function SessionEventPopover({
) : null}
</div>
{info?.expression ? (
<div className="mb-1 text-sm font-medium text-ctp-text">{info.expression}</div>
<div className="mb-1 text-sm font-medium text-ctp-text">
{info.expression}
</div>
) : null}
{info?.context ? (
<div className="mb-1 text-xs text-ctp-subtext0">{info.context}</div>

View File

@@ -177,7 +177,12 @@ export function TrendsTab() {
color="#8aadf4"
type="bar"
/>
<TrendChart title="Cards Mined" data={data.activity.cards} color={cardsMinedColor} type="bar" />
<TrendChart
title="Cards Mined"
data={data.activity.cards}
color={cardsMinedColor}
type="bar"
/>
<TrendChart title="Tokens Seen" data={data.activity.words} color="#8bd5ca" type="bar" />
<TrendChart title="Sessions" data={data.activity.sessions} color="#b7bdf8" type="bar" />
@@ -196,7 +201,12 @@ export function TrendsTab() {
color="#c6a0f6"
type="line"
/>
<TrendChart title="Cards Mined" data={data.progress.cards} color={cardsMinedColor} type="line" />
<TrendChart
title="Cards Mined"
data={data.progress.cards}
color={cardsMinedColor}
type="line"
/>
<TrendChart
title="Episodes Watched"
data={data.progress.episodes}

View File

@@ -146,15 +146,18 @@ test('extractSessionEventNoteInfo ignores malformed notes without a numeric note
});
test('mergeSessionEventNoteInfos keys previews by both requested and returned note ids', () => {
const noteInfos = mergeSessionEventNoteInfos([111], [
{
noteId: 222,
fields: {
Expression: { value: '呪い' },
Sentence: { value: 'この剣は呪いだ' },
const noteInfos = mergeSessionEventNoteInfos(
[111],
[
{
noteId: 222,
fields: {
Expression: { value: '呪い' },
Sentence: { value: 'この剣は呪いだ' },
},
},
},
]);
],
);
assert.deepEqual(noteInfos.get(111), {
noteId: 222,

View File

@@ -237,17 +237,16 @@ export function collectPendingSessionEventNoteIds(
return next;
}
export function getSessionEventCardRequest(
marker: SessionChartMarker | null,
): { noteIds: number[]; requestKey: string | null } {
export function getSessionEventCardRequest(marker: SessionChartMarker | null): {
noteIds: number[];
requestKey: string | null;
} {
if (!marker || marker.kind !== 'card' || marker.noteIds.length === 0) {
return { noteIds: [], requestKey: null };
}
const noteIds = Array.from(
new Set(
marker.noteIds.filter((noteId) => Number.isInteger(noteId) && noteId > 0),
),
new Set(marker.noteIds.filter((noteId) => Number.isInteger(noteId) && noteId > 0)),
);
return {