Sync Stats & History window, headless --sync-cli, and Windows remote support (#160)

This commit is contained in:
2026-07-13 18:56:51 -07:00
committed by GitHub
parent 66f8ca4f80
commit 49b926e08c
111 changed files with 6983 additions and 1130 deletions
+56
View File
@@ -0,0 +1,56 @@
import type { SyncMergeSummary } from '../shared/sync/sync-events';
export function formatMergeTargetHeading(target: 'local' | 'remote'): string {
return target === 'local' ? 'Merged into this machine' : 'Merged into remote machine';
}
export function formatBytes(bytes: number): string {
if (bytes < 1024) return `${Math.round(bytes)} B`;
const units = ['KB', 'MB', 'GB', 'TB'];
let value = bytes / 1024;
let unitIndex = 0;
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024;
unitIndex += 1;
}
return `${value.toFixed(1)} ${units[unitIndex]}`;
}
export function formatRelativeTime(atMs: number | null, nowMs: number): string {
if (atMs === null) return 'never';
const elapsed = Math.max(0, nowMs - atMs);
if (elapsed < 60_000) return 'just now';
if (elapsed < 3_600_000) return `${Math.floor(elapsed / 60_000)} min ago`;
if (elapsed < 86_400_000) return `${Math.floor(elapsed / 3_600_000)} h ago`;
return `${Math.floor(elapsed / 86_400_000)} d ago`;
}
export interface MergeCountLine {
label: string;
value: number;
}
const MERGE_COUNT_FIELDS: Array<{ key: keyof SyncMergeSummary; label: string }> = [
{ key: 'sessionsMerged', label: 'Sessions merged' },
{ key: 'sessionsAlreadyPresent', label: 'Already present' },
{ key: 'activeSessionsSkipped', label: 'Active skipped' },
{ key: 'animeAdded', label: 'Series added' },
{ key: 'videosAdded', label: 'Videos added' },
{ key: 'wordsAdded', label: 'Words added' },
{ key: 'kanjiAdded', label: 'Kanji added' },
{ key: 'subtitleLinesAdded', label: 'Subtitle lines' },
{ key: 'telemetryRowsAdded', label: 'Telemetry rows' },
{ key: 'eventsAdded', label: 'Events' },
{ key: 'excludedWordsAdded', label: 'Excluded words' },
{ key: 'dailyRollupsCopied', label: 'Daily rollups' },
{ key: 'monthlyRollupsCopied', label: 'Monthly rollups' },
{ key: 'rollupGroupsRecomputed', label: 'Rollups recomputed' },
];
// "Sessions merged" always shows (even at 0, it is the headline number);
// everything else appears only when non-zero.
export function summarizeMergeCounts(summary: SyncMergeSummary): MergeCountLine[] {
return MERGE_COUNT_FIELDS.filter(
(field) => field.key === 'sessionsMerged' || (summary[field.key] ?? 0) > 0,
).map((field) => ({ label: field.label, value: summary[field.key] ?? 0 }));
}