fix(progress): currentPhase and live messages now update in real-time
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 35s

Three issues causing the 'prepping → done' jump:

1. +page.svelte updateQueueItem: never applied update.phase to
   currentPhase, so CookingHero always showed 'Prepping' regardless
   of actual backend state. Fixed: currentPhase: update.phase ?? prev.

2. +page.svelte updateQueueItem: progress events (type:'progress')
   were discarded. Fixed: append data.event to progressEvents array
   so live messages are available to components.

3. stream/+server.ts: initial SSE snapshot omitted phase field, so
   items already in-progress on page load showed wrong phase. Fixed.

Bonus: CookingHero now shows the latest user-friendly progress
message (status/complete/model_loading types) as a live scrolling
sub-line under the phase hint.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Giancarmine Salucci
2026-05-13 03:00:58 +02:00
parent a389b0db15
commit 61876f18e5
3 changed files with 41 additions and 5 deletions

View File

@@ -184,12 +184,21 @@
function updateQueueItem(update: QueueStatusUpdate) { function updateQueueItem(update: QueueStatusUpdate) {
const idx = items.findIndex((i) => i.id === update.itemId); const idx = items.findIndex((i) => i.id === update.itemId);
if (idx >= 0) { if (idx >= 0) {
const prev = items[idx];
// Append progress events (type: 'progress' carries data.event)
const newEvents = update.data?.event
? [...(prev.progressEvents ?? []), update.data.event]
: (prev.progressEvents ?? []);
items[idx] = { items[idx] = {
...items[idx], ...prev,
status: update.status, status: update.status,
phases: update.progress || items[idx].phases, // currentPhase is sent as update.phase on status_change events
results: update.results || items[idx].results, currentPhase: update.phase ?? prev.currentPhase,
error: update.error || items[idx].error, phases: update.progress || prev.phases,
progressEvents: newEvents,
results: update.results || prev.results,
error: update.error || prev.error,
updatedAt: update.timestamp updatedAt: update.timestamp
}; };
// Keep selectedItem in sync // Keep selectedItem in sync

View File

@@ -124,6 +124,7 @@ export const GET: RequestHandler = async ({ url, request }) => {
status: item.status, status: item.status,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
url: item.url, url: item.url,
phase: item.currentPhase,
progress: item.phases, progress: item.phases,
results: item.results, results: item.results,
error: item.error error: item.error

View File

@@ -40,6 +40,19 @@
if (diff < 3600) return Math.floor(diff / 60) + 'm ago'; if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
return Math.floor(diff / 3600) + 'h ago'; return Math.floor(diff / 3600) + 'h ago';
} }
// Latest progress message — shown as a live sub-status inside the hero.
// Only show user-friendly event types (skip low-level extraction method attempts).
const SHOW_TYPES = new Set(['status', 'complete', 'model_loading', 'error', 'retry']);
const latestMsg = $derived.by(() => {
const events = item.progressEvents ?? [];
for (let i = events.length - 1; i >= 0; i--) {
const ev = events[i];
if (!ev?.message || ev.message.length <= 3) continue;
if (SHOW_TYPES.has(ev.type)) return ev.message as string;
}
return null;
});
</script> </script>
<button class="ic-btn-reset hero-card" onclick={onTap}> <button class="ic-btn-reset hero-card" onclick={onTap}>
@@ -58,6 +71,9 @@
<div class="text-area"> <div class="text-area">
<div class="phase-label">{phaseMap[item.currentPhase ?? 'extraction']}</div> <div class="phase-label">{phaseMap[item.currentPhase ?? 'extraction']}</div>
<div class="phase-hint">{phaseHints[item.currentPhase ?? 'extraction']}</div> <div class="phase-hint">{phaseHints[item.currentPhase ?? 'extraction']}</div>
{#if latestMsg}
<div class="live-msg ic-pulse">{latestMsg}</div>
{/if}
<span class="source-chip">{username(item.url)}</span> <span class="source-chip">{username(item.url)}</span>
</div> </div>
</div> </div>
@@ -147,7 +163,17 @@
font-size: 13px; font-size: 13px;
color: var(--muted); color: var(--muted);
line-height: 1.35; line-height: 1.35;
margin-bottom: 8px; margin-bottom: 6px;
}
.live-msg {
font-size: 11px;
font-family: var(--font-mono);
color: var(--pink);
line-height: 1.35;
margin-bottom: 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.source-chip { .source-chip {
display: inline-block; display: inline-block;