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:
parent
c6ef350283
commit
6b63e9a5f3
@ -57,6 +57,11 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [largeDataset, setLargeDataset] = useState(false)
|
const [largeDataset, setLargeDataset] = useState(false)
|
||||||
const [loadProgress, setLoadProgress] = useState(null) // { received, total }
|
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)
|
const [msg, setMsg] = useState(null)
|
||||||
|
|
||||||
// layouts
|
// 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') }
|
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 rowCount = parseInt(r.headers.get('X-Row-Count') || '0')
|
||||||
const total = parseInt(r.headers.get('Content-Length') || '0') || null
|
const total = parseInt(r.headers.get('Content-Length') || '0') || null
|
||||||
|
setLoadRows(rowCount || null)
|
||||||
const reader = r.body.getReader()
|
const reader = r.body.getReader()
|
||||||
const chunks = []
|
const chunks = []
|
||||||
let received = 0
|
let received = 0
|
||||||
@ -655,6 +661,7 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLargeDataset(false)
|
setLargeDataset(false)
|
||||||
setLoadProgress(null)
|
setLoadProgress(null)
|
||||||
|
setLoadRows(null)
|
||||||
setSlices([])
|
setSlices([])
|
||||||
setExpandDepth(null)
|
setExpandDepth(null)
|
||||||
adoptSplit([], 0)
|
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">
|
<div className="relative flex-1 min-w-0 min-h-0">
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center bg-gray-50 z-10 gap-2">
|
<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 && (
|
{loadProgress && (
|
||||||
<>
|
<>
|
||||||
<span className="text-xs text-gray-400 font-mono">
|
<span className="text-xs text-gray-400 font-mono">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user