Persist stats exclusions in DB and fix word metrics filtering

- Stats vocabulary exclusions stored in `imm_stats_excluded_words` (schema v18); seeded from localStorage on first load
- Session, overview, trends, and library word metrics use filtered persisted occurrences with raw fallback
- Session known-word % chart uses filtered persisted totals as denominator for both known and total
- JLPT subtitle styling changed to underline-only; no longer overrides text color
This commit is contained in:
2026-05-03 19:40:54 -07:00
parent db30c61327
commit 25d0aa47db
32 changed files with 1541 additions and 211 deletions
+23 -1
View File
@@ -1,7 +1,11 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { renderToStaticMarkup } from 'react-dom/server';
import { SessionDetail, getKnownPctAxisMax } from '../components/sessions/SessionDetail';
import {
SessionDetail,
buildKnownWordsRatioChartData,
getKnownPctAxisMax,
} from '../components/sessions/SessionDetail';
import { buildSessionChartEvents } from './session-events';
import { EventType } from '../types/stats';
@@ -69,3 +73,21 @@ test('getKnownPctAxisMax adds headroom above the highest known percentage', () =
test('getKnownPctAxisMax caps the chart top at 100%', () => {
assert.equal(getKnownPctAxisMax([97.1, 98.6]), 100);
});
test('buildKnownWordsRatioChartData uses filtered known-word timeline totals', () => {
const chartData = buildKnownWordsRatioChartData(
[
{ sampleMs: 1_000, linesSeen: 1, tokensSeen: 10 },
{ sampleMs: 2_000, linesSeen: 2, tokensSeen: 20 },
],
new Map([
[1, { knownWordsSeen: 2, totalWordsSeen: 3 }],
[2, { knownWordsSeen: 3, totalWordsSeen: 4 }],
]),
);
assert.deepEqual(chartData, [
{ tsMs: 1_000, knownWords: 2, unknownWords: 1, totalWords: 3 },
{ tsMs: 2_000, knownWords: 3, unknownWords: 1, totalWords: 4 },
]);
});