diff --git a/apps/demo/src/lib/components/CodePanel.svelte b/apps/demo/src/lib/components/CodePanel.svelte index 8cb1647a4..793754432 100644 --- a/apps/demo/src/lib/components/CodePanel.svelte +++ b/apps/demo/src/lib/components/CodePanel.svelte @@ -44,6 +44,19 @@ return blocks; }); + // All code blocks (including flow_config) for pulse dot positioning + const allCodeBlocks = $derived.by(() => { + const blocks: Array<{ stepSlug: string; startLine: number; endLine: number }> = []; + + for (const [stepSlug, section] of Object.entries(FLOW_SECTIONS)) { + if (section.startLine !== undefined && section.endLine !== undefined) { + blocks.push({ stepSlug, startLine: section.startLine, endLine: section.endLine }); + } + } + + return blocks; + }); + // Helper to trim common leading whitespace from code function trimLeadingWhitespace(code: string): string { const lines = code.split('\n'); @@ -144,6 +157,12 @@ const mobile = isMobile; const selected = selectedStep; + // Cleanup old handlers first + if (cleanupHandlers) { + cleanupHandlers(); + cleanupHandlers = undefined; + } + // Setup handlers for full code view (desktop or mobile with no selection) if (codeContainer && (!mobile || !selected || selected === 'flow_config')) { setupClickHandlersDelayed(); @@ -151,13 +170,19 @@ }); function setupClickHandlers() { - if (!codeContainer) return; + if (!codeContainer) { + console.log('[CodePanel] setupClickHandlers: codeContainer not found'); + return; + } + + console.log('[CodePanel] Setting up click handlers'); // Store handlers for cleanup const handlers: Array<{ element: Element; type: string; handler: EventListener }> = []; // Find all line elements const lines = codeContainer.querySelectorAll('.line'); + console.log('[CodePanel] Found', lines.length, 'lines'); lines.forEach((line, index) => { const lineNumber = index + 1; const stepSlug = getStepFromLine(lineNumber); @@ -170,7 +195,8 @@ // Click handler const clickHandler = () => { - console.log('CodePanel: Line clicked, stepSlug:', stepSlug); + console.log('[CodePanel] Line clicked, stepSlug:', stepSlug); + console.log('[CodePanel] isMobile:', isMobile, 'selectedStep:', selectedStep); // Clear hover state before navigating dispatch('step-hovered', { stepSlug: null }); @@ -313,23 +339,47 @@ {#each stepBlocks as block (block.stepSlug)} {@const stepStatus = getStepStatus(block.stepSlug)} {#if stepStatus} - {@const blockHeight = (block.endLine - block.startLine + 1) * 1.5} - {@const blockTop = (block.startLine - 1) * 1.5} - {@const iconTop = blockTop + blockHeight / 2} + {@const lineHeightPx = 19.5} + {@const paddingTopPx = 12} + {@const numLines = block.endLine - block.startLine + 1} + {@const blockHeightPx = numLines * lineHeightPx} + {@const blockTopPx = (block.startLine - 1) * lineHeightPx + paddingTopPx} + {@const iconTopPx = blockTopPx + blockHeightPx / 2} {@const isDimmed = selectedStep && block.stepSlug !== selectedStep} - + +
+ + {/if} {/each} + + + {#each allCodeBlocks as block (block.stepSlug)} + {@const lineHeightPx = 19.5} + {@const paddingTopPx = 12} + {@const numLines = block.endLine - block.startLine + 1} + {@const blockHeightPx = numLines * lineHeightPx} + {@const blockTopPx = (block.startLine - 1) * lineHeightPx + paddingTopPx} + {@const centerTopPx = blockTopPx + blockHeightPx / 2} + +{event.deltaDisplay}
+ {event.deltaDisplay}
{/if}
diff --git a/apps/demo/src/lib/components/EventsPanel.svelte b/apps/demo/src/lib/components/EventsPanel.svelte
new file mode 100644
index 000000000..0fdb69f8b
--- /dev/null
+++ b/apps/demo/src/lib/components/EventsPanel.svelte
@@ -0,0 +1,190 @@
+
+
+
+
+
+ Event Stream ({displayableEvents.length})
+
+
+
+
+ {#if displayableEvents.length === 0}
+
+ No events yet. Start a flow to see events.
+
+ {:else}
+ {#each displayableEvents as { badge, event, idx } (idx)}
+ {#if badge}
+
+ {/if}
+ {/each}
+ {/if}
+
+
+
+
+
diff --git a/apps/demo/src/lib/components/ExplanationPanel.svelte b/apps/demo/src/lib/components/ExplanationPanel.svelte
index ae5b44224..da759b9fb 100644
--- a/apps/demo/src/lib/components/ExplanationPanel.svelte
+++ b/apps/demo/src/lib/components/ExplanationPanel.svelte
@@ -376,9 +376,9 @@
-
-
-
+
+
+
{#if currentStepInfo.dependsOn.length > 0}
@@ -443,42 +443,32 @@
{/if}
-
-
-
-
- Input
- {#if highlightedInput}
-
-
- {@html highlightedInput}
-
- {:else if flowState.status === 'idle'}
+
+
+ Input
+ {#if highlightedInput}
+
+
+ {@html highlightedInput}
+
+ {:else if flowState.status === 'idle'}
+
+
+ Run the workflow to see input
+
+ {:else if currentStepInfo && currentStepInfo.dependsOn.length > 0}
+ {@const incompleteDeps = currentStepInfo.dependsOn.filter(
+ (dep) => getStepStatus(dep) !== 'completed'
+ )}
+ {#if incompleteDeps.length > 0}
-
- Run the workflow to see input
+
+ Waiting for {incompleteDeps.join(', ')} to complete
- {:else if currentStepInfo && currentStepInfo.dependsOn.length > 0}
- {@const incompleteDeps = currentStepInfo.dependsOn.filter(
- (dep) => getStepStatus(dep) !== 'completed'
- )}
- {#if incompleteDeps.length > 0}
-
-
- Waiting for {incompleteDeps.join(', ')} to complete
-
- {:else}
-
-
- Waiting for step to start
-
- {/if}
{:else}
Waiting for step to start
{/if}
-
+ {:else}
+
+
+ Waiting for step to start
+
+ {/if}
+
-
-
- Output
- {#if highlightedOutput}
-
-
- {@html highlightedOutput}
-
- {:else if flowState.status === 'idle'}
-
-
- Run the workflow to see output
-
- {:else if stepStatus === 'started'}
-
-
- Step is running...
-
- {:else}
-
-
- Waiting for step to complete
-
- {/if}
-
+
+
+ Output
+ {#if highlightedOutput}
+
+
+ {@html highlightedOutput}
+
+ {:else if flowState.status === 'idle'}
+
+
+ Run the workflow to see output
+
+ {:else if stepStatus === 'started'}
+
+
+ Step is running...
+
+ {:else}
+
+
+ Waiting for step to complete
+
+ {/if}
diff --git a/apps/demo/src/lib/stores/pgflow-state.svelte.ts b/apps/demo/src/lib/stores/pgflow-state.svelte.ts
index cd617cb55..489b9219a 100644
--- a/apps/demo/src/lib/stores/pgflow-state.svelte.ts
+++ b/apps/demo/src/lib/stores/pgflow-state.svelte.ts
@@ -132,11 +132,11 @@ export function createFlowState(
const now = new SvelteDate();
const occurredAt =
event.status === 'started' && 'started_at' in event
- ? new Date(event.started_at)
+ ? new SvelteDate(event.started_at)
: event.status === 'completed' && 'completed_at' in event
- ? new Date(event.completed_at)
+ ? new SvelteDate(event.completed_at)
: event.status === 'failed' && 'failed_at' in event
- ? new Date(event.failed_at)
+ ? new SvelteDate(event.failed_at)
: undefined;
// DEBUG: Log broadcast event
@@ -180,11 +180,11 @@ export function createFlowState(
const now = new SvelteDate();
const occurredAt =
event.status === 'started' && 'started_at' in event
- ? new Date(event.started_at)
+ ? new SvelteDate(event.started_at)
: event.status === 'completed' && 'completed_at' in event
- ? new Date(event.completed_at)
+ ? new SvelteDate(event.completed_at)
: event.status === 'failed' && 'failed_at' in event
- ? new Date(event.failed_at)
+ ? new SvelteDate(event.failed_at)
: undefined;
// DEBUG: Log broadcast event
@@ -245,32 +245,32 @@ export function createFlowState(
return {
get status() {
// Track stateVersion for reactivity
- stateVersion;
+ void stateVersion;
return run?.step(stepSlug).status || 'created';
},
get output() {
// Track stateVersion for reactivity
- stateVersion;
+ void stateVersion;
return run?.step(stepSlug).output;
},
get error() {
// Track stateVersion for reactivity
- stateVersion;
+ void stateVersion;
return run?.step(stepSlug).error_message;
},
get started_at() {
// Track stateVersion for reactivity
- stateVersion;
+ void stateVersion;
return run?.step(stepSlug).started_at;
},
get completed_at() {
// Track stateVersion for reactivity
- stateVersion;
+ void stateVersion;
return run?.step(stepSlug).completed_at;
},
get failed_at() {
// Track stateVersion for reactivity
- stateVersion;
+ void stateVersion;
return run?.step(stepSlug).failed_at;
}
};
diff --git a/apps/demo/src/routes/+page.svelte b/apps/demo/src/routes/+page.svelte
index 6fa2eacee..54193977b 100644
--- a/apps/demo/src/routes/+page.svelte
+++ b/apps/demo/src/routes/+page.svelte
@@ -4,15 +4,15 @@
import { createFlowState } from '$lib/stores/pgflow-state.svelte';
import { pulseDots } from '$lib/stores/pulse-dots.svelte';
import DAGVisualization from '$lib/components/DAGVisualization.svelte';
- import DebugPanel from '$lib/components/DebugPanel.svelte';
+ import EventsPanel from '$lib/components/EventsPanel.svelte';
import CodePanel from '$lib/components/CodePanel.svelte';
import ExplanationPanel from '$lib/components/ExplanationPanel.svelte';
import WelcomeModal from '$lib/components/WelcomeModal.svelte';
import PulseDot from '$lib/components/PulseDot.svelte';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
- import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
- import { Play, CheckCircle2 } from '@lucide/svelte';
+ import { Card } from '$lib/components/ui/card';
+ import { Play, CheckCircle2, Code, GitBranch, Radio } from '@lucide/svelte';
import { codeToHtml } from 'shiki';
import type ArticleFlow from '../../supabase/functions/article_flow_worker/article_flow';
@@ -168,13 +168,6 @@
const isRunning = $derived(flowState.status === 'starting' || flowState.status === 'started');
- // Event stream collapsed state
- let eventStreamCollapsed = $state(false);
-
- function toggleEventStream() {
- eventStreamCollapsed = !eventStreamCollapsed;
- }
-
// Mobile events panel state
let mobileEventsVisible = $state(false);
let mobileEventsContentVisible = $state(false); // Delayed for animation
@@ -263,10 +256,7 @@
}
// Helper to get event badge info
- function getEventBadgeInfo(event: {
- event_type: string;
- step_slug?: string;
- }): {
+ function getEventBadgeInfo(event: { event_type: string; step_slug?: string }): {
icon: typeof Play | typeof CheckCircle2;
color: string;
text: string;
@@ -295,7 +285,6 @@
.filter((e) => e.badge !== null)
);
-
// Fix for mobile viewport height (address bar issue)
// Set actual viewport height as CSS custom property for browsers without dvh support
function setViewportHeight() {
@@ -340,13 +329,13 @@
-
+
pgflow
|
@@ -360,10 +349,24 @@
>
-
+
+ {#if explanationVisible}
+
+
+
+ {:else}
+
+ {/if}
-
+
- {#if explanationVisible}
-
- {/if}
-
-
+
+
+
+
-
- {#if flowState.events.length > 0}
-
- {/if}
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
@@ -666,7 +600,7 @@
- {#each displayableEvents as { badge, event, idx }, i (idx)}
+ {#each displayableEvents as { badge, event, idx } (idx)}
{#if badge}