diff --git a/apps/demo/src/lib/components/DAGVisualization.svelte b/apps/demo/src/lib/components/DAGVisualization.svelte index 6061bdac6..11d9b5bf1 100644 --- a/apps/demo/src/lib/components/DAGVisualization.svelte +++ b/apps/demo/src/lib/components/DAGVisualization.svelte @@ -314,6 +314,7 @@ outline-offset: 2px !important; box-shadow: 0 0 20px rgba(88, 166, 255, 0.4) !important; cursor: pointer !important; + opacity: 1 !important; /* Ensure selected nodes are never dimmed */ } @keyframes pulse { diff --git a/apps/demo/src/lib/components/ExplanationPanel.svelte b/apps/demo/src/lib/components/ExplanationPanel.svelte index 53e7864bb..554d796d3 100644 --- a/apps/demo/src/lib/components/ExplanationPanel.svelte +++ b/apps/demo/src/lib/components/ExplanationPanel.svelte @@ -45,6 +45,18 @@ displayName: 'Article Processing Flow', description: 'This flow processes web articles by fetching content, generating summaries and keywords, then publishing the results.', + whatItDoes: + 'Demonstrates parallel execution, automatic retries, and dependency management—all core pgflow features.', + reliabilityFeatures: [ + { + setting: 'maxAttempts: 3', + explanation: 'Automatically retries failed steps up to 3 times before giving up' + }, + { + setting: 'timeout: 60', + explanation: 'Tasks become visible for retry after 60 seconds if worker crashes' + } + ], inputType: `{ url: string }`, @@ -57,6 +69,7 @@ { name: string; displayName: string; + whatItDoes: string; dependsOn: string[]; dependents: string[]; inputType: string; @@ -66,6 +79,7 @@ fetch_article: { name: 'fetch_article', displayName: 'Fetch Article', + whatItDoes: 'Fetches article content from the provided URL using r.jina.ai. Returns both the article text and title for downstream processing.', dependsOn: [], dependents: ['summarize', 'extract_keywords'], inputType: `{ @@ -81,6 +95,7 @@ summarize: { name: 'summarize', displayName: 'Summarize', + whatItDoes: 'Uses an LLM (Groq) to generate a concise summary of the article content. Runs in parallel with keyword extraction for efficiency.', dependsOn: ['fetch_article'], dependents: ['publish'], inputType: `{ @@ -94,6 +109,7 @@ extract_keywords: { name: 'extract_keywords', displayName: 'Extract Keywords', + whatItDoes: 'Uses an LLM (Groq) to extract key terms and topics from the article. Runs in parallel with summarization for efficiency.', dependsOn: ['fetch_article'], dependents: ['publish'], inputType: `{ @@ -106,6 +122,7 @@ publish: { name: 'publish', displayName: 'Publish', + whatItDoes: 'Combines the summary and keywords and publishes the processed article. In this demo, generates a mock article ID—in production, this would save to a database.', dependsOn: ['summarize', 'extract_keywords'], dependents: [], inputType: `{ @@ -254,6 +271,13 @@ {#if currentStepInfo} + +
+

+ {currentStepInfo.whatItDoes} +

+
+
@@ -333,10 +357,25 @@ {:else}
- + +
+

{flowInfo.description}

+

{flowInfo.whatItDoes}

+
+ +
-
Description
-

{flowInfo.description}

+
+ 🛡️ Reliability Configuration +
+
+ {#each flowInfo.reliabilityFeatures as feature} +
+ {feature.setting} +

{feature.explanation}

+
+ {/each} +
diff --git a/apps/demo/src/routes/+page.svelte b/apps/demo/src/routes/+page.svelte index 291f36584..ab0718897 100644 --- a/apps/demo/src/routes/+page.svelte +++ b/apps/demo/src/routes/+page.svelte @@ -152,6 +152,13 @@ onDestroy(() => flowState.dispose()); const isRunning = $derived(flowState.status === 'starting' || flowState.status === 'started'); + + // Event stream collapsed state + let eventStreamCollapsed = $state(false); + + function toggleEventStream() { + eventStreamCollapsed = !eventStreamCollapsed; + }
- -
- -
- - - - - {#if flowState.events.length > 0} - - {/if} + + - -
- -
- {#if explanationVisible} - - {/if} + +
+ +
+
- -
- -
+ + {#if flowState.events.length > 0} +
+ + +
+ Event Stream + + {eventStreamCollapsed ? '▶' : '▼'} Click to {eventStreamCollapsed ? 'expand' : 'collapse'} + +
+
+ {#if !eventStreamCollapsed} + + + + {/if} +
+
+ {/if} +
- -
@@ -355,41 +429,125 @@ width: 100%; max-width: 1440px; height: 100%; + display: flex; + flex-direction: column; + gap: 1rem; } - /* Desktop: Two columns using flexbox */ + /* Header section with logo and input */ + .header-section { + flex-shrink: 0; + align-items: center; + gap: 1rem; + padding: 0.75rem 0; + border-bottom: 1px solid hsl(var(--border)); + margin-bottom: 0.5rem; + } + + /* Main layout: Code + Event Stream (left) | DAG & Details (right) */ .main-layout { display: flex; - height: 100%; + flex: 1; + min-height: 0; gap: 1rem; } .left-column { - flex: 1; + flex: 1 1 auto; + min-width: 850px; /* Code panel needs generous space */ display: flex; flex-direction: column; - min-width: 0; + overflow: hidden; + gap: 0.75rem; + } + + .code-panel-container { + flex: 1; + min-height: 0; overflow: hidden; } + /* Event stream in left column (bottom) */ + .event-stream-section-left { + flex-shrink: 0; + max-height: 35vh; + overflow: hidden; + } + + .event-stream-section-left .collapsed { + max-height: 3rem; + } + + .event-stream-section-left .expanded { + display: flex; + flex-direction: column; + max-height: 35vh; + } + + .event-stream-section-left .expanded .event-stream-content { + flex: 1; + overflow: auto; + min-height: 0; + } + .right-column { flex: 0 0 auto; - width: 720px; - min-width: 720px; + width: 520px; + min-width: 520px; display: flex; flex-direction: column; min-height: 0; overflow: hidden; } - /* Mobile: Single column, hide left, right takes full width */ + .dag-card { + flex-shrink: 0; + } + + /* Tablet/Small Desktop (769px-1400px): Stack columns vertically */ + @media (max-width: 1400px) and (min-width: 769px) { + .main-layout { + flex-direction: column; + gap: 1rem; + } + + .left-column { + min-width: 0; + flex: 0 0 auto; + height: auto; + max-height: none; /* Remove height limit */ + } + + .code-panel-container { + height: 500px; /* Fixed height for code when stacked */ + } + + .event-stream-section-left { + max-height: 250px; /* Smaller event stream when stacked */ + } + + .event-stream-section-left .expanded { + max-height: 250px; + } + + .right-column { + flex: 1; + width: 100%; + min-width: 0; + } + } + + /* Mobile: Single column, simplified layout */ @media (max-width: 768px) { .main-layout { + flex-direction: column; gap: 0; } .left-column { - display: none; + flex: 0 0 auto; + height: auto; + min-width: 0; } .right-column { @@ -398,8 +556,8 @@ min-width: 0; } - .mobile-code-wrapper { - flex-shrink: 0; + .event-stream-section-left { + display: none; /* Hide on mobile */ } .mobile-dag-container { @@ -418,6 +576,15 @@ } } + /* Welcome guide card styling */ + :global(.welcome-guide) { + overflow-y: auto; + } + + :global(.welcome-guide h3) { + color: hsl(var(--foreground)); + } + /* Contact banner using pgflow palette */ :global(.contact-banner) { background: linear-gradient(135deg, #007b6e 0%, #9979d3 100%);