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
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user