Skip to content

Commit acc7fe9

Browse files
committed
feat: final tweaks v2
1 parent 97a9568 commit acc7fe9

File tree

7 files changed

+97
-36
lines changed

7 files changed

+97
-36
lines changed

apps/demo/src/lib/components/DAGVisualization.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@
194194
return '';
195195
}
196196
197+
// Define the actual DAG step slugs
198+
const DAG_STEPS = ['fetchArticle', 'summarize', 'extractKeywords', 'publish'];
199+
197200
function getNodeClass(stepSlug: string): string {
198201
const classes = ['dag-node'];
199202
@@ -207,8 +210,9 @@
207210
classes.push('node-created');
208211
}
209212
210-
// Dimming: dim nodes that aren't selected when something is selected
211-
if (selectedStep && stepSlug !== selectedStep) {
213+
// Dimming: Only dim nodes when a DAG step is selected (not flow_config)
214+
// If selectedStep is not a DAG step, don't dim anything
215+
if (selectedStep && DAG_STEPS.includes(selectedStep) && stepSlug !== selectedStep) {
212216
classes.push('node-dimmed');
213217
}
214218

apps/demo/src/lib/components/EventsPanel.svelte

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@
1212
1313
let expandedEventIdx = $state<number | null>(null);
1414
let highlightedEventJson = $state<Record<number, string>>({});
15+
let isMobile = $state(false);
16+
17+
// Detect mobile viewport
18+
if (typeof window !== 'undefined') {
19+
const mediaQuery = window.matchMedia('(max-width: 767px)');
20+
isMobile = mediaQuery.matches;
21+
22+
const updateMobile = (e: MediaQueryListEvent) => {
23+
isMobile = e.matches;
24+
// Clear cache when switching to force regeneration with new truncation
25+
highlightedEventJson = {};
26+
};
27+
28+
mediaQuery.addEventListener('change', updateMobile);
29+
}
1530
1631
// Helper to get short step name
1732
function getShortStepName(stepSlug: string): string {
@@ -81,7 +96,9 @@
8196
} else {
8297
// Generate syntax-highlighted JSON if not already cached
8398
if (!highlightedEventJson[idx]) {
84-
const truncated = truncateDeep(event);
99+
// Mobile: 50 chars, Desktop: 500 chars
100+
const maxLength = isMobile ? 50 : 500;
101+
const truncated = truncateDeep(event, maxLength);
85102
const jsonString = JSON.stringify(truncated, null, 2);
86103
const html = await codeToHtml(jsonString, {
87104
lang: 'json',

apps/demo/src/lib/components/ExplanationPanel.svelte

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
'Demonstrates parallel execution, automatic retries, and dependency management—all core pgflow features.',
4646
reliabilityFeatures: [
4747
{
48-
setting: 'maxAttempts: 3',
48+
setting: 'maxAttempts: 2',
4949
explanation: 'Automatically retries failed steps up to 3 times before giving up'
5050
}
5151
],
@@ -157,6 +157,19 @@
157157
let panelElement: HTMLElement | undefined = $state(undefined);
158158
let highlightedInput = $state<string>('');
159159
let highlightedOutput = $state<string>('');
160+
let isMobile = $state(false);
161+
162+
// Detect mobile viewport
163+
if (typeof window !== 'undefined') {
164+
const mediaQuery = window.matchMedia('(max-width: 767px)');
165+
isMobile = mediaQuery.matches;
166+
167+
const updateMobile = (e: MediaQueryListEvent) => {
168+
isMobile = e.matches;
169+
};
170+
171+
mediaQuery.addEventListener('change', updateMobile);
172+
}
160173
161174
// Replace long strings with placeholders for mobile display
162175
function truncateDeep(obj: unknown, maxLength = 80): unknown {
@@ -183,7 +196,9 @@
183196
$effect(() => {
184197
const input = stepInput;
185198
if (input) {
186-
const truncated = truncateDeep(input);
199+
// Mobile: 50 chars, Desktop: 500 chars
200+
const maxLength = isMobile ? 50 : 500;
201+
const truncated = truncateDeep(input, maxLength);
187202
const jsonString = JSON.stringify(truncated, null, 2);
188203
codeToHtml(jsonString, {
189204
lang: 'json',
@@ -200,7 +215,9 @@
200215
$effect(() => {
201216
const output = stepOutput;
202217
if (output) {
203-
const truncated = truncateDeep(output);
218+
// Mobile: 50 chars, Desktop: 500 chars
219+
const maxLength = isMobile ? 50 : 500;
220+
const truncated = truncateDeep(output, maxLength);
204221
const jsonString = JSON.stringify(truncated, null, 2);
205222
codeToHtml(jsonString, {
206223
lang: 'json',
@@ -338,7 +355,7 @@
338355
</div>
339356
{/if}
340357

341-
<div class="explanation-content text-sm p-4 space-y-3">
358+
<div class="explanation-content text-sm p-4 pb-24 md:pb-4 space-y-3">
342359
{#if currentStepInfo}
343360
{#key selectedStep}
344361
<div in:fade={{ duration: 300, delay: 150 }} out:fade={{ duration: 150 }}>
@@ -523,47 +540,53 @@
523540
{/key}
524541
{:else}
525542
<!-- Flow-level view -->
526-
<div class="space-y-3">
543+
<div class="space-y-4">
527544
<!-- What it does -->
528-
<div class="bg-accent/30 rounded-lg p-3 border border-accent">
529-
<p class="text-sm text-foreground leading-relaxed mb-2">{flowInfo.description}</p>
530-
<p class="text-xs text-muted-foreground">{flowInfo.whatItDoes}</p>
531-
</div>
545+
<p class="text-foreground leading-relaxed">
546+
Processes web articles by fetching content, generating summaries and keywords in
547+
parallel, then publishing the results. Demonstrates parallel execution, automatic
548+
retries, and dependency management.
549+
</p>
532550

533551
<!-- How orchestration works (collapsible) -->
534-
<details class="flow-orchestration-explainer">
552+
<details
553+
class="concept-explainer bg-background/50 border border-border rounded-lg"
554+
open
555+
>
535556
<summary
536-
class="font-semibold text-sm text-primary cursor-pointer hover:text-primary/80 flex items-center gap-2 mb-2"
557+
class="font-semibold text-sm text-foreground cursor-pointer hover:bg-background/80 flex items-center gap-2 p-3 rounded-lg transition-colors"
537558
>
538-
<span>⚙️</span> How SQL Core orchestrates this flow
559+
<span class="concept-caret">▸</span>
560+
<span class="flex-1">How pgflow orchestrates this flow</span>
539561
</summary>
540-
<div
541-
class="text-xs text-muted-foreground leading-relaxed bg-secondary/30 rounded p-3 space-y-2"
542-
>
562+
<div class="text-xs text-muted-foreground leading-relaxed px-3 pb-3 space-y-2">
543563
<p>
544-
When you call <code class="bg-muted px-1 rounded font-mono"
545-
>start_flow('article_flow', {'{url}'})</code
546-
>, SQL Core creates a run and initializes state rows for each step. Root steps (no
547-
dependencies) get messages pushed to the queue immediately.
564+
<code class="bg-muted px-1 rounded font-mono">start_flow()</code> creates a run and
565+
initializes state for each step. Root steps (no dependencies) get tasks queued
566+
immediately.
548567
</p>
549568
<p>
550-
As Workers execute handlers and call <code class="bg-muted px-1 rounded font-mono"
551-
>complete_task()</code
552-
>, SQL Core acknowledges completion, saves outputs, checks dependent steps, and
553-
starts those with all dependencies satisfied. The run completes when
569+
<strong>Edge Function worker</strong> polls the queue, calls
570+
<code class="bg-muted px-1 rounded font-mono">start_tasks()</code> to reserve tasks,
571+
executes handlers, then calls
572+
<code class="bg-muted px-1 rounded font-mono">complete_task()</code> to save outputs.
573+
</p>
574+
<p>
575+
<strong>SQL Core</strong> checks dependencies after each completion, creates tasks for
576+
steps with all dependencies met, and marks the run complete when
554577
<code class="bg-muted px-1 rounded font-mono">remaining_steps = 0</code>.
555578
</p>
556-
<p class="text-muted-foreground/80 italic">
557-
This demo uses Supabase Realtime to broadcast graph state changes from SQL Core back
558-
to the browser in real-time.
579+
<p>
580+
<strong>Supabase Realtime</strong> broadcasts state changes back to this UI for live
581+
updates.
559582
</p>
560583
</div>
561584
</details>
562585

563586
<!-- Reliability Features -->
564587
<div>
565-
<div class="font-semibold text-muted-foreground mb-1.5 text-sm flex items-center gap-2">
566-
<span>🛡️</span> Reliability Configuration
588+
<div class="font-semibold text-muted-foreground mb-1.5 text-sm">
589+
Reliability Configuration
567590
</div>
568591
<div class="space-y-2">
569592
{#each flowInfo.reliabilityFeatures as feature (feature.setting)}

apps/demo/src/lib/data/flow-code.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const FLOW_SECTIONS: Record<string, CodeSection> = {
1919
flow_config: {
2020
code: `new Flow<{ url: string }>({
2121
slug: 'article_flow',
22-
maxAttempts: 3
22+
maxAttempts: 2
2323
})`
2424
},
2525
fetchArticle: {

apps/demo/src/routes/+page.svelte

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
let eventsScrollContainer: HTMLDivElement | undefined;
175175
let expandedEventIdx = $state<number | null>(null);
176176
let highlightedEventJson = $state<Record<number, string>>({});
177+
let isMobile = $state(false);
177178
178179
function toggleMobileEvents() {
179180
if (!mobileEventsVisible) {
@@ -200,7 +201,9 @@
200201
} else {
201202
// Generate syntax-highlighted JSON if not already cached
202203
if (!highlightedEventJson[idx]) {
203-
const truncated = truncateDeep(event);
204+
// Mobile: 50 chars, Desktop: 500 chars
205+
const maxLength = isMobile ? 50 : 500;
206+
const truncated = truncateDeep(event, maxLength);
204207
const jsonString = JSON.stringify(truncated, null, 2);
205208
const html = await codeToHtml(jsonString, {
206209
lang: 'json',
@@ -297,6 +300,18 @@
297300
if (typeof window !== 'undefined') {
298301
setViewportHeight();
299302
303+
// Detect mobile viewport for truncation
304+
const mediaQuery = window.matchMedia('(max-width: 767px)');
305+
isMobile = mediaQuery.matches;
306+
307+
const updateMobile = (e: MediaQueryListEvent) => {
308+
isMobile = e.matches;
309+
// Clear cache when switching to force regeneration with new truncation
310+
highlightedEventJson = {};
311+
};
312+
313+
mediaQuery.addEventListener('change', updateMobile);
314+
300315
// Listen to visualViewport resize if available (better for mobile)
301316
if (window.visualViewport) {
302317
window.visualViewport.addEventListener('resize', setViewportHeight);
@@ -307,6 +322,7 @@
307322
308323
// Clean up on destroy
309324
onDestroy(() => {
325+
mediaQuery.removeEventListener('change', updateMobile);
310326
if (window.visualViewport) {
311327
window.visualViewport.removeEventListener('resize', setViewportHeight);
312328
} else {

apps/demo/supabase/functions/article_flow_worker/article_flow.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ function sleep(ms: number) {
1414
// Flow definition - clean and minimal
1515
export default new Flow<{ url: string }>({
1616
slug: 'article_flow',
17-
maxAttempts: 3
17+
baseDelay: 1,
18+
maxAttempts: 2
1819
})
1920
.step({ slug: 'fetchArticle' }, async (input) => {
2021
await sleep(SLEEP_MS);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
select pgflow.create_flow('article_flow', max_attempts => 3);
1+
select pgflow.create_flow('article_flow', max_attempts => 2, base_delay => 1);
22
select pgflow.add_step('article_flow', 'fetchArticle');
3-
select pgflow.add_step('article_flow', 'summarize', array['fetchArticle'], base_delay => 1);
3+
select pgflow.add_step('article_flow', 'summarize', array['fetchArticle']);
44
select pgflow.add_step('article_flow', 'extractKeywords', array['fetchArticle']);
55
select pgflow.add_step('article_flow', 'publish', array['summarize', 'extractKeywords']);

0 commit comments

Comments
 (0)