style: format web/src with oxfmt defaults (via vp fmt)

One-time mechanical sweep, isolated from any functional change.
- oxfmt defaults: double quotes, semicolons, 2-space indent, LF,
  printWidth 100, trailing commas, bracket spacing, arrow parens.
- vite.config.ts gains an fmt.ignorePatterns block for web/static:
  vendored/minified assets (htmx.min.js, compiled css) must never
  be reformatted.
- Verified: rebuild after the sweep produces byte-identical JS/CSS
  bundles; only sourcemaps changed (they embed sourcesContent).
This commit is contained in:
John O'Keefe committed 2026-10-21 13:18:24 -04:00
1 parent 8aeecaa0ef
commit 77cd268f4e
37 files changed
+1151 -1853

No files matched your search

+67 -28
View File
@@ -137,9 +137,13 @@ let libraryDataLoaded = false;
// Page-scoped helpers -----------------------------------------------------
async function chartCtor(): Promise<new (el: HTMLCanvasElement, cfg: ChartConfiguration) => ChartLike> {
async function chartCtor(): Promise<
new (el: HTMLCanvasElement, cfg: ChartConfiguration) => ChartLike
> {
const mod = await import("chart.js/auto");
return (mod as unknown as { default: new (el: HTMLCanvasElement, cfg: ChartConfiguration) => ChartLike }).default;
return (
mod as unknown as { default: new (el: HTMLCanvasElement, cfg: ChartConfiguration) => ChartLike }
).default;
}
function text(id: string, value: string): void {
@@ -302,10 +306,14 @@ function renderSummary(summary: Summary): void {
const hasData = summary.total_minutes > 0 || summary.session_count > 0;
document.getElementById("stats-empty")?.classList.toggle("hidden", hasData);
const windowLabel = document.getElementById("stats-window-label");
if (windowLabel) windowLabel.textContent = `${summary.start_date} → ${summary.end_date} (${summary.days} days)`;
if (windowLabel)
windowLabel.textContent = `${summary.start_date} → ${summary.end_date} (${summary.days} days)`;
const mins = summary.total_minutes;
text("stats-total-minutes", mins >= 60 ? `${Math.floor(mins / 60)} h ${mins % 60} min` : `${mins} min`);
text(
"stats-total-minutes",
mins >= 60 ? `${Math.floor(mins / 60)} h ${mins % 60} min` : `${mins} min`,
);
renderPagesCard(summary);
text(
"stats-streaks",
@@ -315,7 +323,9 @@ function renderSummary(summary: Summary): void {
);
text(
"stats-books-finished",
summary.books_with_activity > 0 ? `${summary.books_finished} of ${summary.books_with_activity}` : "–",
summary.books_with_activity > 0
? `${summary.books_finished} of ${summary.books_with_activity}`
: "–",
);
const mostActive = document.getElementById("stats-most-active");
@@ -357,7 +367,11 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
},
scales: {
x: { grid: { display: false }, ticks: { color: secondary, maxTicksLimit: 10 } },
y: { grid: { color: "rgba(128,128,128,.15)" }, ticks: { color: secondary }, beginAtZero: true },
y: {
grid: { color: "rgba(128,128,128,.15)" },
ticks: { color: secondary },
beginAtZero: true,
},
},
});
@@ -368,7 +382,9 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
pagesChart = null;
listeningChart = null;
const minutesCanvas = document.getElementById("stats-daily-minutes-chart") as HTMLCanvasElement | null;
const minutesCanvas = document.getElementById(
"stats-daily-minutes-chart",
) as HTMLCanvasElement | null;
if (minutesCanvas) {
minutesChart = new ChartCtor(minutesCanvas, {
type: "bar",
@@ -380,7 +396,9 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
});
}
const pagesCanvas = document.getElementById("stats-daily-pages-chart") as HTMLCanvasElement | null;
const pagesCanvas = document.getElementById(
"stats-daily-pages-chart",
) as HTMLCanvasElement | null;
if (pagesCanvas) {
pagesChart = new ChartCtor(pagesCanvas, {
type: "line",
@@ -404,7 +422,9 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
// Listening per Day (hrs) — the audio half of the side-by-side pages
// split; its panel is hidden by renderPagesCard unless the range has
// audio minutes.
const listeningCanvas = document.getElementById("stats-daily-listening-chart") as HTMLCanvasElement | null;
const listeningCanvas = document.getElementById(
"stats-daily-listening-chart",
) as HTMLCanvasElement | null;
if (listeningCanvas) {
listeningChart = new ChartCtor(listeningCanvas, {
type: "bar",
@@ -426,26 +446,32 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
function renderLibraries(rows: LibraryRow[]): void {
const container = document.getElementById("stats-library-breakdown");
if (!container) return;
renderBars(container, rows.map((r) => ({
label: r.name,
sub: `${fmtMinutes(r.minutes)} · ${r.sessions} sessions`,
value: r.minutes,
})));
renderBars(
container,
rows.map((r) => ({
label: r.name,
sub: `${fmtMinutes(r.minutes)} · ${r.sessions} sessions`,
value: r.minutes,
})),
);
}
function renderDevices(rows: DeviceRow[]): void {
const container = document.getElementById("stats-device-breakdown");
if (!container) return;
renderBars(container, rows.map((r) => ({
label: r.name || "Web",
sub: `${r.books} ${r.books === 1 ? "book" : "books"} · ${r.sessions} sessions`,
value: r.minutes,
})));
renderBars(
container,
rows.map((r) => ({
label: r.name || "Web",
sub: `${r.books} ${r.books === 1 ? "book" : "books"} · ${r.sessions} sessions`,
value: r.minutes,
})),
);
}
function fmtMinutes(minutes: number): string {
return minutes >= 60
? `${Math.floor(minutes / 60)}h${minutes % 60 ? ` ${(minutes % 60)}m` : ""}`
? `${Math.floor(minutes / 60)}h${minutes % 60 ? ` ${minutes % 60}m` : ""}`
: `${minutes} min`;
}
@@ -522,10 +548,9 @@ async function loadLibraries(): Promise<VisibleLibrary[]> {
function fillLibrarySelector(rows: VisibleLibrary[]): void {
const select = document.getElementById("stats-library-select") as HTMLSelectElement | null;
if (!select) return;
select.innerHTML = `<option value="">All libraries</option>` +
rows
.map((r) => `<option value="${escapeHtml(r.id)}">${escapeHtml(r.name)}</option>`)
.join("");
select.innerHTML =
`<option value="">All libraries</option>` +
rows.map((r) => `<option value="${escapeHtml(r.id)}">${escapeHtml(r.name)}</option>`).join("");
// Honor a URL-seeded selection initStatsPage may have set.
if (currentLibraryID) select.value = currentLibraryID;
}
@@ -545,9 +570,15 @@ async function loadLibraryUsage(): Promise<void> {
"lib-stat-narrators",
];
targets.forEach((id) => text(id, "–"));
for (const id of ["stats-lib-genres", "stats-lib-authors", "stats-lib-longest", "stats-lib-largest"]) {
for (const id of [
"stats-lib-genres",
"stats-lib-authors",
"stats-lib-longest",
"stats-lib-largest",
]) {
const el = document.getElementById(id);
if (el) el.innerHTML = `<div class="text-center py-6 text-sm" style="color: var(--text-secondary)">Loading…</div>`;
if (el)
el.innerHTML = `<div class="text-center py-6 text-sm" style="color: var(--text-secondary)">Loading…</div>`;
}
try {
@@ -598,7 +629,10 @@ function renderLibraryUsage(payload: LibraryUsage): void {
}
const gb = payload.total_size_bytes / 1e9;
text("lib-stat-size", gb >= 0.1 ? gb.toFixed(1) : (payload.total_size_bytes / 1e6).toFixed(1) + " MB");
text(
"lib-stat-size",
gb >= 0.1 ? gb.toFixed(1) : (payload.total_size_bytes / 1e6).toFixed(1) + " MB",
);
const itemTotal = Math.max(payload.items, 1);
renderBars(
@@ -619,7 +653,12 @@ function renderLibraryUsage(payload: LibraryUsage): void {
})),
"No authors recorded in this pool yet.",
);
renderUsageItems("stats-lib-longest", "stats-lib-longest-label", payload.longest_items, payload.hours_mode);
renderUsageItems(
"stats-lib-longest",
"stats-lib-longest-label",
payload.longest_items,
payload.hours_mode,
);
renderUsageItems("stats-lib-largest", null, payload.largest_items, payload.hours_mode, true);
}