From 6b63e9a5f3efbfc5bd1bf9e0bf6cc90e7df84c66 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Fri, 18 Sep 2026 08:38:04 -0400 Subject: [PATCH] Say how many rows the load is waiting on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Row-Count arrives with the headers, long before the body has been read, so the overlay can name the wait instead of saying "Loading…" over a grey screen for fifteen seconds. On this data the row count *is* the wait -- the bytes are quick and the rows are not -- so it is the number worth showing beside the transfer bar, which only ever measured the fast part. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/views/Forecast.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ui/src/views/Forecast.jsx b/ui/src/views/Forecast.jsx index 3f4fb3e..c5fc360 100644 --- a/ui/src/views/Forecast.jsx +++ b/ui/src/views/Forecast.jsx @@ -57,6 +57,11 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio const [loading, setLoading] = useState(false) const [largeDataset, setLargeDataset] = useState(false) const [loadProgress, setLoadProgress] = useState(null) // { received, total } + // Rows the server says it is sending, from X-Row-Count. Known as soon as the + // headers land, well before the body has been read, so the wait can say what + // it is waiting for -- on this data the row count is the wait (see CLAUDE.md, + // "Load time is dominated by row count"). + const [loadRows, setLoadRows] = useState(null) const [msg, setMsg] = useState(null) // layouts @@ -619,6 +624,7 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio if (!r.ok) { const { error } = await r.json(); throw new Error(error || 'Failed to load data') } const rowCount = parseInt(r.headers.get('X-Row-Count') || '0') const total = parseInt(r.headers.get('Content-Length') || '0') || null + setLoadRows(rowCount || null) const reader = r.body.getReader() const chunks = [] let received = 0 @@ -655,6 +661,7 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio setLoading(true) setLargeDataset(false) setLoadProgress(null) + setLoadRows(null) setSlices([]) setExpandDepth(null) adoptSplit([], 0) @@ -1820,7 +1827,9 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
{loading && (
- Loading… + + {loadRows ? `Loading ${loadRows.toLocaleString()} rows…` : 'Loading…'} + {loadProgress && ( <>