Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions apps/demo/src/lib/components/CodePanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@
return null;
}

// Only show indicators for started and completed
if (status === 'started' || status === 'completed') {
// Show indicators for started, completed, and failed
if (status === 'started' || status === 'completed' || status === 'failed') {
return status;
}

Expand Down Expand Up @@ -464,6 +464,10 @@
background: #3b82f6; /* Blue */
}

.section-status-border.status-failed {
background: #ef4444; /* Red */
}

/* Pulse dot container - centers the dot */
.pulse-dot-container {
position: absolute;
Expand Down Expand Up @@ -624,6 +628,10 @@
background: #3b82f6; /* Blue */
}

.step-status-border.status-failed {
background: #ef4444; /* Red */
}

.step-status-border.status-dimmed {
opacity: 0.3;
}
Expand Down
8 changes: 6 additions & 2 deletions apps/demo/src/lib/components/DAGVisualization.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
return '';
}

// Define the actual DAG step slugs
const DAG_STEPS = ['fetchArticle', 'summarize', 'extractKeywords', 'publish'];

function getNodeClass(stepSlug: string): string {
const classes = ['dag-node'];

Expand All @@ -207,8 +210,9 @@
classes.push('node-created');
}

// Dimming: dim nodes that aren't selected when something is selected
if (selectedStep && stepSlug !== selectedStep) {
// Dimming: Only dim nodes when a DAG step is selected (not flow_config)
// If selectedStep is not a DAG step, don't dim anything
if (selectedStep && DAG_STEPS.includes(selectedStep) && stepSlug !== selectedStep) {
classes.push('node-dimmed');
}

Expand Down
51 changes: 47 additions & 4 deletions apps/demo/src/lib/components/EventsPanel.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { createFlowState } from '$lib/stores/pgflow-state.svelte';
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
import { Play, CheckCircle2 } from '@lucide/svelte';
import { Play, CheckCircle2, XCircle } from '@lucide/svelte';
import { codeToHtml } from 'shiki';
interface Props {
Expand All @@ -12,6 +12,21 @@
let expandedEventIdx = $state<number | null>(null);
let highlightedEventJson = $state<Record<number, string>>({});
let isMobile = $state(false);
// Detect mobile viewport
if (typeof window !== 'undefined') {
const mediaQuery = window.matchMedia('(max-width: 767px)');
isMobile = mediaQuery.matches;
const updateMobile = (e: MediaQueryListEvent) => {
isMobile = e.matches;
// Clear cache when switching to force regeneration with new truncation
highlightedEventJson = {};
};
mediaQuery.addEventListener('change', updateMobile);
}
// Helper to get short step name
function getShortStepName(stepSlug: string): string {
Expand All @@ -26,7 +41,7 @@
// Helper to get event badge info
function getEventBadgeInfo(event: { event_type: string; step_slug?: string }): {
icon: typeof Play | typeof CheckCircle2;
icon: typeof Play | typeof CheckCircle2 | typeof XCircle;
color: string;
text: string;
} | null {
Expand All @@ -44,10 +59,17 @@
text: getShortStepName(event.step_slug)
};
}
if (event.event_type === 'step:failed' && event.step_slug) {
return {
icon: XCircle,
color: 'red',
text: getShortStepName(event.step_slug)
};
}
return null;
}
// Get displayable events (started/completed steps only)
// Get displayable events (started/completed/failed steps only)
const displayableEvents = $derived(
flowState.timeline
.map((e, idx) => ({ event: e, badge: getEventBadgeInfo(e), idx }))
Expand Down Expand Up @@ -81,7 +103,9 @@
} else {
// Generate syntax-highlighted JSON if not already cached
if (!highlightedEventJson[idx]) {
const truncated = truncateDeep(event);
// Mobile: 50 chars, Desktop: 500 chars
const maxLength = isMobile ? 50 : 500;
const truncated = truncateDeep(event, maxLength);
const jsonString = JSON.stringify(truncated, null, 2);
const html = await codeToHtml(jsonString, {
lang: 'json',
Expand All @@ -93,6 +117,19 @@
expandedEventIdx = idx;
}
}
// Auto-expand failed events
$effect(() => {
// Find the most recent failed event
const failedEvents = displayableEvents.filter((e) => e.event.event_type === 'step:failed');
if (failedEvents.length > 0) {
const mostRecentFailed = failedEvents[failedEvents.length - 1];
// Auto-expand it
if (expandedEventIdx !== mostRecentFailed.idx) {
toggleEventExpanded(mostRecentFailed.idx, mostRecentFailed.event);
}
}
});
</script>

<Card class="h-full flex flex-col">
Expand Down Expand Up @@ -166,6 +203,12 @@
color: #2ec184;
}
.event-badge-row.event-badge-red {
background-color: rgba(220, 38, 38, 0.2);
border-color: rgba(239, 68, 68, 0.5);
color: #f87171;
}
/* Event JSON display */
.event-json-display {
max-height: 300px;
Expand Down
Loading