From bf2486534c6b54e0dfa842fdcd0a8a3df6273a7b Mon Sep 17 00:00:00 2001 From: Chris Czub Date: Wed, 22 Jul 2026 09:39:34 -0400 Subject: [PATCH] Add page-size selector (25/50/100/500) Defaults to 50; the selection persists in localStorage alongside the hidden-senders list, and changing it resets to page 1. Closes #23 --- web/app.css | 8 ++++++++ web/app.js | 32 ++++++++++++++++++++++++++++---- web/index.html | 8 ++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/web/app.css b/web/app.css index 2dc90de..f42752e 100644 --- a/web/app.css +++ b/web/app.css @@ -117,3 +117,11 @@ tr.hidden-row td.act { opacity: 1; } } .pager button:disabled { opacity: 0.4; cursor: default; } .pager button:disabled:hover { border-color: var(--border); color: var(--fg); } +.toggle select { + font: inherit; + color: var(--fg); + background: var(--card); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.25rem 0.4rem; +} diff --git a/web/app.js b/web/app.js index 7abb67f..d99f70a 100644 --- a/web/app.js +++ b/web/app.js @@ -3,8 +3,10 @@ // Sender strings come from arbitrary From: headers and are attacker-controlled. // This page only ever renders them via textContent — never innerHTML. -const PAGE_SIZE = 50; +const PAGE_SIZES = [25, 50, 100, 500]; +const DEFAULT_PAGE_SIZE = 50; const HIDDEN_KEY = "gmail-stats.hiddenSenders"; +const PAGE_SIZE_KEY = "gmail-stats.pageSize"; const views = ["loading", "setup", "error", "stats"]; @@ -12,10 +14,20 @@ const state = { data: null, query: "", page: 1, + pageSize: loadPageSize(), showHidden: false, hidden: loadHidden(), }; +function loadPageSize() { + try { + const stored = Number(localStorage.getItem(PAGE_SIZE_KEY)); + return PAGE_SIZES.includes(stored) ? stored : DEFAULT_PAGE_SIZE; + } catch (err) { + return DEFAULT_PAGE_SIZE; + } +} + function loadHidden() { try { const raw = localStorage.getItem(HIDDEN_KEY); @@ -107,10 +119,10 @@ function update() { listed = listed.filter(row => String(row.sender).toLowerCase().includes(query)); } - const pages = Math.max(1, Math.ceil(listed.length / PAGE_SIZE)); + const pages = Math.max(1, Math.ceil(listed.length / state.pageSize)); state.page = Math.min(Math.max(1, state.page), pages); - const start = (state.page - 1) * PAGE_SIZE; - const pageRows = listed.slice(start, start + PAGE_SIZE); + const start = (state.page - 1) * state.pageSize; + const pageRows = listed.slice(start, start + state.pageSize); const tbody = document.getElementById("sender-rows"); tbody.replaceChildren(); @@ -169,6 +181,18 @@ document.getElementById("search").addEventListener("input", event => { state.page = 1; update(); }); +document.getElementById("page-size").value = String(state.pageSize); +document.getElementById("page-size").addEventListener("change", event => { + const size = Number(event.target.value); + state.pageSize = PAGE_SIZES.includes(size) ? size : DEFAULT_PAGE_SIZE; + try { + localStorage.setItem(PAGE_SIZE_KEY, String(state.pageSize)); + } catch (err) { + // Storage unavailable — selection just won't survive reload. + } + state.page = 1; + update(); +}); document.getElementById("show-hidden").addEventListener("change", event => { state.showHidden = event.target.checked; state.page = 1; diff --git a/web/index.html b/web/index.html index f2a4751..e920a98 100644 --- a/web/index.html +++ b/web/index.html @@ -55,6 +55,14 @@

Something went wrong

+