Skip to content

Commit 412ff31

Browse files
committed
Fix download size reset, cached nav, deep paths
- Track peak download bytes separately from object counts so receive step never shows ~100 kB - Replace hasAutoStarted boolean with suppressedRepo string so cached repo clicks re-trigger analysis - Set paths.relative: false so deep URLs get absolute asset paths instead of broken relative ones
1 parent 9fb2fde commit 412ff31

3 files changed

Lines changed: 33 additions & 22 deletions

File tree

src/lib/components/PipelineProgress.svelte

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,15 @@ ${statBlock}`
8787
case 'receive': {
8888
const snap = snapshots['receive']
8989
const ts = phaseTimestamps['receive']
90+
const receivedBytes = peakDownloadedBytes > 0 ? peakDownloadedBytes : (snap?.loaded ?? 0)
9091
let statBlock = ''
91-
if (snap && snap.loaded > 0) {
92+
if (receivedBytes > 0) {
9293
const elapsedSec = ts ? ((ts.endTime ?? liveElapsedNow) - ts.startTime) / 1000 : 0
9394
const speed =
9495
elapsedSec > 0.5
95-
? ` at <span class="phase-info-stat-value">~${formatBytes(Math.round(snap.loaded / elapsedSec))}/s</span>`
96+
? ` at <span class="phase-info-stat-value">~${formatBytes(Math.round(receivedBytes / elapsedSec))}/s</span>`
9697
: ''
97-
statBlock = `<span class="phase-info-stat"><span class="phase-info-stat-value">${formatBytes(snap.loaded)}</span> received${speed}</span>`
98+
statBlock = `<span class="phase-info-stat"><span class="phase-info-stat-value">${formatBytes(receivedBytes)}</span> received${speed}</span>`
9899
}
99100
100101
// Size estimate block — when we know the repo size, estimate compressed download range
@@ -110,13 +111,12 @@ ${statBlock}`
110111
const lowPctLabel = Math.round(packRatioLow * 100)
111112
const highPctLabel = Math.round(packRatioHigh * 100)
112113
113-
const loaded = snap?.loaded ?? 0
114114
const progressLine =
115-
loaded > 0
115+
receivedBytes > 0
116116
? (() => {
117-
const lowPct = Math.min(Math.round((loaded / lowBytes) * 100), 100)
118-
const highPct = Math.min(Math.round((loaded / highBytes) * 100), 100)
119-
return ` With <span class="phase-info-stat-value">${formatMb(loaded)} MB</span> downloaded, that\u2019s somewhere between <span class="phase-info-stat-value">${highPct}%</span> and <span class="phase-info-stat-value">${lowPct}%</span> done. That's all we know.`
117+
const lowPct = Math.min(Math.round((receivedBytes / lowBytes) * 100), 100)
118+
const highPct = Math.min(Math.round((receivedBytes / highBytes) * 100), 100)
119+
return ` With <span class="phase-info-stat-value">${formatMb(receivedBytes)} MB</span> downloaded, that\u2019s somewhere between <span class="phase-info-stat-value">${highPct}%</span> and <span class="phase-info-stat-value">${lowPct}%</span> done. That's all we know.`
120120
})()
121121
: ''
122122
@@ -179,6 +179,9 @@ ${statBlock}`
179179
let previousPhaseId: PhaseId | null = $state(null)
180180
let seenFetching = $state(false)
181181
182+
/** Peak bytes seen from 'downloading' events — never decreases. */
183+
let peakDownloadedBytes = $state(0)
184+
182185
// Track which clonePhase string was already acknowledged as complete.
183186
// Because effects run after render, a new phase arriving already at loaded >= total
184187
// gets at least one render cycle as 'active' before being marked complete.
@@ -211,13 +214,19 @@ ${statBlock}`
211214
}
212215
})
213216
214-
// Keep snapshots up to date: live data for the active phase, frozen for completed phases
217+
// Keep snapshots up to date: live data for the active phase, frozen for completed phases.
218+
// 'downloading' events (HTTP byte counts) are tracked separately in peakDownloadedBytes
219+
// to avoid overwriting the 'receive' snapshot with object counts from 'Receiving objects'.
215220
$effect(() => {
216221
if (activePhaseId && activePhaseId !== previousPhaseId) {
217222
previousPhaseId = activePhaseId
218223
}
219224
if (activePhaseId && phase === 'cloning') {
220-
snapshots[activePhaseId] = { loaded: cloneLoaded, total: cloneTotal }
225+
if (clonePhase === 'downloading') {
226+
peakDownloadedBytes = Math.max(peakDownloadedBytes, cloneLoaded)
227+
} else {
228+
snapshots[activePhaseId] = { loaded: cloneLoaded, total: cloneTotal }
229+
}
221230
lastCloneProgressTime = Date.now()
222231
}
223232
})
@@ -327,9 +336,7 @@ ${statBlock}`
327336
const silenceMs = $derived(phase === 'cloning' ? Math.max(0, liveElapsedNow - lastCloneProgressTime) : 0)
328337
const isStale = $derived(silenceMs > staleThresholdMs)
329338
const silenceTimeoutMs = $derived(
330-
(snapshots['receive']?.loaded ?? 0) > silenceExtensionThreshold
331-
? silenceExtendedTimeoutMs
332-
: silenceBaseTimeoutMs,
339+
peakDownloadedBytes > silenceExtensionThreshold ? silenceExtendedTimeoutMs : silenceBaseTimeoutMs,
333340
)
334341
335342
// --- Info popup state ---
@@ -388,17 +395,16 @@ ${statBlock}`
388395
if (state === 'done' && snap.loaded > 0) return `${formatNumber(snap.loaded)} objects`
389396
return ''
390397
391-
case 'receive':
398+
case 'receive': {
399+
const bytes = peakDownloadedBytes > 0 ? peakDownloadedBytes : snap.loaded
392400
if (state === 'active' && !cloneSubPhaseComplete) {
393-
return snap.total > 0
394-
? `${formatBytes(snap.loaded)} / ~${formatBytes(snap.total)}`
395-
: formatBytes(snap.loaded)
401+
return bytes > 0 ? formatBytes(bytes) : ''
396402
}
397403
if (state === 'done') {
398-
const bytes = snap.total > 0 ? snap.total : snap.loaded
399404
return bytes > 0 ? formatBytes(bytes) : ''
400405
}
401406
return ''
407+
}
402408
403409
case 'resolve':
404410
if (state === 'active' && !cloneSubPhaseComplete && snap.total > 0)

src/routes/+page.svelte

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,15 @@
116116
// Auto-start from URL on initial page load only (e.g. shared links).
117117
// Must not re-trigger on programmatic goto — goto is async, so comparing
118118
// the URL-derived initialRepo with a stored string races and can start the wrong repo.
119-
let hasAutoStarted = $state(false)
119+
// Intentionally NOT $state — must not trigger the effect. The effect must only
120+
// fire when initialRepo changes (URL navigation). If suppressedRepo were reactive,
121+
// changing it in startAnalysis would re-trigger the effect while initialRepo still
122+
// holds the OLD URL (goto is async) → race condition.
123+
let suppressedRepo = ''
120124
121125
$effect(() => {
122-
if (browser && initialRepo && !hasAutoStarted) {
123-
hasAutoStarted = true
126+
if (browser && initialRepo && initialRepo !== suppressedRepo) {
127+
suppressedRepo = initialRepo
124128
void startAnalysis(initialRepo)
125129
}
126130
})
@@ -307,7 +311,7 @@
307311
308312
try {
309313
const parsed = parseRepoUrl(repoInput)
310-
hasAutoStarted = true // Prevent the URL-watching effect from re-triggering
314+
suppressedRepo = parsed.url // Prevent the URL-watching effect from re-triggering
311315
updateUrl(repoInput)
312316
313317
// Fetch repo size for display (fire-and-forget, non-blocking).

svelte.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const config = {
66
preprocess: vitePreprocess(),
77
kit: {
88
adapter: adapter({ fallback: '200.html' }),
9+
paths: { relative: false },
910
},
1011
}
1112

0 commit comments

Comments
 (0)