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