Say how many rows the load is waiting on

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) <noreply@anthropic.com>
This commit is contained in:
Paul Trowbridge 2026-09-18 08:38:04 -04:00
parent c6ef350283
commit 6b63e9a5f3

View File

@ -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
<div className="relative flex-1 min-w-0 min-h-0">
{loading && (
<div className="absolute inset-0 flex flex-col items-center justify-center bg-gray-50 z-10 gap-2">
<span className="text-sm text-gray-400">Loading</span>
<span className="text-sm text-gray-400">
{loadRows ? `Loading ${loadRows.toLocaleString()} rows…` : 'Loading…'}
</span>
{loadProgress && (
<>
<span className="text-xs text-gray-400 font-mono">