Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cb823f1
Improve UI of script record
tristantr Mar 6, 2026
84a75b4
Improve UI for scripts
tristantr Mar 6, 2026
646d000
Remove Result & Logs loading container while flow not finised
tristantr Mar 6, 2026
73c9c9a
Improve Graph view
tristantr Mar 6, 2026
1e78aeb
Add click on a step mention
tristantr Mar 6, 2026
3e39e51
Fix spacing when empty
tristantr Mar 6, 2026
c04c3f9
Fix step duration disappearing in recorded flows
tristantr Mar 6, 2026
c05206a
Modernize timeline tab
tristantr Mar 6, 2026
d685d64
Improve Script recording result UI
tristantr Mar 6, 2026
1b3dfb7
merge main
hugocasa Mar 26, 2026
64dd03b
feat: externalize recording player controls for fake-window embedding
hugocasa Mar 26, 2026
34ab31d
refactor: reorder FlowViewer tab sync effects for clarity
hugocasa Mar 26, 2026
0246251
refactor: eliminate tab sync effects in FlowViewer, use selectedTab d…
hugocasa Mar 26, 2026
8368475
refactor: remove unnecessary untrack in FlowViewer tab init
hugocasa Mar 26, 2026
568872c
fix: skip tab auto-selection when selectedTab is controlled externally
hugocasa Mar 26, 2026
2645291
feat: export recording types from package
hugocasa Mar 26, 2026
707be36
merge main
hugocasa Mar 26, 2026
ea6d87c
fix: non-null assertion for recording.flow in FlowGraphViewer
hugocasa Mar 26, 2026
0b91943
fix: replace banned $bindable(default_value) pattern and simplify tab…
hugocasa Mar 26, 2026
ff0b0a4
fix: use svelte 5 onclick syntax on replay page
hugocasa Mar 26, 2026
5018781
:qMerge remote-tracking branch 'origin/main' into tl/improve-rec-ui
hugocasa Mar 26, 2026
b668deb
fix: skip db clock endpoint during replay mode
hugocasa Mar 26, 2026
a1525cc
fix: remove line numbers from script recording code display
hugocasa Mar 26, 2026
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
7 changes: 7 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@
"svelte": "./package/components/recording/ScriptRecordingReplay.svelte",
"default": "./package/components/recording/ScriptRecordingReplay.svelte"
},
"./components/recording/types": {
"types": "./package/components/recording/types.d.ts",
"default": "./package/components/recording/types.js"
},
"./components/FlowWrapper.svelte": {
"types": "./package/components/FlowWrapper.svelte.d.ts",
"svelte": "./package/components/FlowWrapper.svelte",
Expand Down Expand Up @@ -500,6 +504,9 @@
"components/ScriptRecordingReplay.svelte": [
"./package/components/recording/ScriptRecordingReplay.svelte.d.ts"
],
"components/recording/types": [
"./package/components/recording/types.d.ts"
],
"components/FlowBuilder.svelte": [
"./package/components/FlowBuilder.svelte.d.ts"
],
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/lib/components/FlowGraphViewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
workspace?: string | undefined
minHeight?: number
noBorder?: boolean
hideDefaultInputs?: boolean
}

let {
Expand All @@ -38,7 +39,8 @@
stepDetail = $bindable(undefined),
workspace = $workspaceStore,
minHeight = 400,
noBorder = false
noBorder = false,
hideDefaultInputs = false
}: Props = $props()

const dispatch = createEventDispatcher()
Expand All @@ -47,7 +49,9 @@
<div class="grid grid-cols-3 w-full h-full">
{#if !noGraph}
<div
class="{noSide ? 'col-span-3' : 'sm:col-span-2 col-span-3'} w-full max-h-full"
class="{noSide || (hideDefaultInputs && stepDetail == undefined)
? 'col-span-3'
: 'sm:col-span-2 col-span-3'} w-full max-h-full"
class:overflow-auto={overflowAuto}
class:border={!noBorder}
>
Expand Down Expand Up @@ -81,14 +85,14 @@
/>
</div>
{/if}
{#if !noSide}
{#if !noSide && !(hideDefaultInputs && stepDetail == undefined)}
<div
class={twMerge(
'relative w-full h-full min-h-[150px] max-h-[90vh] border-r border-b border-t p-2 pt-0 overflow-auto hidden sm:flex flex-col gap-4',
noGraph ? 'border-0 w-max' : ''
)}
>
<FlowGraphViewerStep schema={flow.schema} {stepDetail} />
<FlowGraphViewerStep schema={flow.schema} {stepDetail} {hideDefaultInputs} />
</div>
{/if}
</div>
7 changes: 4 additions & 3 deletions frontend/src/lib/components/FlowGraphViewerStep.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
schema?: any | undefined
stepDetail?: FlowModule | string | undefined
jobScriptHash?: string | undefined
hideDefaultInputs?: boolean
}

let { schema = undefined, stepDetail = undefined, jobScriptHash = undefined }: Props = $props()
let { schema = undefined, stepDetail = undefined, jobScriptHash = undefined, hideDefaultInputs = false }: Props = $props()
let codeViewer: Drawer | undefined = $state()
</script>

Expand Down Expand Up @@ -92,10 +93,10 @@
<div class={twMerge('p-2 overflow-y-scroll')}>
{#if stepDetail == undefined}
<div>
<p class="font-medium text-secondary text-center pt-4 pb-8">
<p class="text-secondary text-xs italic px-2 pt-2">
Click on a step to see its details
</p>
{#if schema}
{#if schema && !hideDefaultInputs}
<h3 class="mb-2 font-semibold">Flow Inputs</h3>
<SchemaViewer {schema} />
{/if}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/components/FlowStatusViewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
workspaceId = undefined,
flowState = $bindable({}),
selectedJobStep = $bindable(undefined),
hideFlowResult = false,
hideTimeline = false,
hideDownloadInGraph = false,
hideNodeDefinition = false,
Expand Down Expand Up @@ -175,6 +176,7 @@
}
}}
{showLogsWithResult}
{hideFlowResult}
notes={notesProp}
groups={groupsProp}
/>
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/lib/components/FlowStatusViewerInner.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
}
showLogsWithResult?: boolean
showJobDetailHeader?: boolean
hideFlowResult?: boolean
notes?: FlowNote[]
groups?: FlowValue['groups']
}
Expand Down Expand Up @@ -178,6 +179,7 @@
toolCallStore,
showLogsWithResult = false,
showJobDetailHeader = false,
hideFlowResult = false,
notes: notesProp = undefined,
groups: groupsProp = undefined
}: Props = $props()
Expand Down Expand Up @@ -1356,7 +1358,7 @@
/>
</div>
{/if}
{:else if render}
{:else if render && !hideFlowResult}
<div class={'flex flex-col w-full'}>
{#if showLogsWithResult && job}
<!-- Side-by-side result and logs for simple jobs -->
Expand Down Expand Up @@ -2141,7 +2143,7 @@
likely did not run yet</p
>
{/if}
{:else}<p class="p-2 text-primary italic"
{:else}<p class="text-secondary text-xs italic"
>Select a node to see its details here</p
>{/if}
</div>
Expand All @@ -2157,7 +2159,7 @@
{#if node?.job_id}
<JobOtelTraces jobId={node.job_id} />
{:else}
<div class="p-4 text-secondary"
<div class="p-4 text-secondary text-xs italic"
>Select a node with a job to see HTTP request traces</div
>
{/if}
Expand Down
160 changes: 76 additions & 84 deletions frontend/src/lib/components/FlowTimeline.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -81,36 +81,31 @@
}}
/>
{#if items}
<div class="divide-y border-b">
<div class="px-2 py-2 grid grid-cols-12 w-full"
><div></div>
<div class="col-span-11 pt-1 px-2 flex text-2xs text-secondary justify-between"
><div>{min ? displayDate(new Date(min), true) : ''}</div>{#if max && min}<div
class="hidden lg:block">{msToSec(max - min, 1)}s</div
>
{/if}<div class="flex gap-1 items-center font-mono"
>{max ? displayDate(new Date(max), true) : ''}{#if !max && min}{#if now}
{msToSec(now - min, 1)}s
{/if}<Loader2 size={14} class="animate-spin" />{/if}</div
></div
>
</div>
<div class="flex flex-row-reverse items-center text-sm text-secondary p-2">
<div class="flex gap-4 items-center text-2xs">
<div class="flex gap-2 items-center">
<div>Waiting for executor/Suspend</div>
<div class="h-4 w-4 bg-gray-500"></div>
<div class="divide-y">
<div class="px-3 py-1.5 flex items-center justify-between text-2xs text-secondary">
<div class="flex gap-1 items-center font-mono">
{min ? displayDate(new Date(min), true) : ''}
</div>
<div class="flex gap-3 items-center">
<div class="flex gap-1.5 items-center">
<div class="h-2.5 w-2.5 rounded-sm bg-gray-400 dark:bg-gray-500"></div>
<span>Wait</span>
</div>

<div class="flex gap-2 items-center">
<div>Execution</div>
<div class="h-4 w-4 bg-blue-500/90"></div>
<div class="flex gap-1.5 items-center">
<div class="h-2.5 w-2.5 rounded-sm bg-blue-500/90"></div>
<span>Execution</span>
</div>
{#if max && min}
<span class="font-mono">{msToSec(max - min, 1)}s</span>
{/if}
{#if !max && min}{#if now}
<span class="font-mono">{msToSec(now - min, 1)}s</span>
{/if}<Loader2 size={14} class="animate-spin" />{/if}
</div>
</div>
{#if selfWaitTime}
<div class="px-2 py-2 grid grid-cols-6 w-full">
root:
<div class="px-3 py-1.5 flex items-center gap-2">
<span class="text-xs text-secondary">root:</span>
<WaitTimeWarning
self_wait_time_ms={selfWaitTime}
aggregate_wait_time_ms={aggregateWaitTime}
Expand All @@ -120,9 +115,9 @@
{/if}
{#each flowModules as { id: k, type: typ } (k)}
{@const subItems = items?.[k]?.filter((x) => x.created_at && x.started_at)}
<div class="shadow-inner dark:shadow-gray-700 relative">
<div class="px-2 py-2 grid grid-cols-6 w-full">
<div class="truncate"
<div class="relative px-3 py-1.5">
<div class="flex items-center justify-between mb-0.5">
<div class="text-xs font-medium"
>{k.startsWith('subflow:') ? k.substring(8) : k}
{#if localModuleStates[k]?.selectedForloop && (typ == 'forloopflow' || typ == 'whileloopflow')}
<span class="text-xs font-mono font-medium inline-flex items-center -my-2">
Expand All @@ -141,70 +136,67 @@
</span>
{/if}
</div>
<div class="col-span-5 flex">
{#if subItems?.length > 1}
<div class="text-xs text-secondary absolute top-1 right-2">
{subItems?.length} jobs
</div>
{/if}
{#if min && total}
<VirtualList
width="100%"
height={Math.min(400, (subItems?.length ?? 0) * barHeight)}
itemCount={subItems?.length ?? 0}
itemSize={barHeight}
getKey={(index) => subItems?.[index]?.id}
>
{#snippet item({ index, style })}
{@const b = subItems?.[index]}
{#if b?.created_at}
<!-- <div class="text-xs text-secondary">{JSON.stringify(b)}</div> -->
{@const waitingLen = b?.created_at
? b.started_at
? b.started_at - b?.created_at
: b.duration_ms
? 0
: now - b?.created_at
: 0}
<div class="flex w-full p-1 pb-2 pl-12" {style}>
{#if subItems?.length > 1}
<span class="text-2xs text-secondary bg-surface-hover px-1.5 py-0.5 rounded-full">
{subItems?.length} jobs
</span>
{/if}
</div>
<div class="w-full">
{#if min && total}
<VirtualList
width="100%"
height={Math.min(400, (subItems?.length ?? 0) * barHeight)}
itemCount={subItems?.length ?? 0}
itemSize={barHeight}
getKey={(index) => subItems?.[index]?.id}
>
{#snippet item({ index, style })}
{@const b = subItems?.[index]}
{#if b?.created_at}
{@const waitingLen = b?.created_at
? b.started_at
? b.started_at - b?.created_at
: b.duration_ms
? 0
: now - b?.created_at
: 0}
<div class="flex w-full py-0.5 items-center" {style}>
<TimelineBar
position="left"
id={b?.id}
{total}
{min}
gray
spacerClass="bg-gray-100 dark:bg-gray-800/50 rounded-l-md"
started_at={b.created_at}
len={waitingLen < 100 ? 0 : waitingLen - 100}
running={b?.started_at == undefined}
/>
{#if b.started_at}
<TimelineBar
position="left"
position={waitingLen < 100 ? 'center' : 'right'}
id={b?.id}
{total}
{min}
gray
started_at={b.created_at}
len={waitingLen < 100 ? 0 : waitingLen - 100}
running={b?.started_at == undefined}
concat
started_at={b.started_at}
len={b.started_at ? (b?.duration_ms ?? now - b?.started_at) : 0}
running={b?.duration_ms == undefined}
/>
{#if b.started_at}
<TimelineBar
position={waitingLen < 100 ? 'center' : 'right'}
id={b?.id}
{total}
{min}
concat
started_at={b.started_at}
len={b.started_at ? (b?.duration_ms ?? now - b?.started_at) : 0}
running={b?.duration_ms == undefined}
/>
{/if}
</div>
{:else}
<div class="flex w-full p-1 pb-2 pl-12">
<div class="text-xs text-secondary">
<!-- Waiting for executor/Suspend {JSON.stringify(b)} -->
</div>
</div>
{/if}
{/snippet}
</VirtualList>
{/if}</div
></div
>
{/if}
</div>
{:else}
<div class="flex w-full py-0.5"></div>
{/if}
{/snippet}
</VirtualList>
{/if}
</div>
</div>
{/each}
</div>

{:else}
<Loader2 class="animate-spin" />
{/if}
Loading
Loading