@flowdrop/flowdrop 1.14.0 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/CHANGELOG.md +475 -0
  2. package/MIGRATION-2.0.md +472 -0
  3. package/README.md +23 -23
  4. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  5. package/dist/adapters/WorkflowAdapter.js +14 -8
  6. package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
  7. package/dist/chat/batchFeedback.d.ts +39 -0
  8. package/dist/chat/batchFeedback.js +51 -0
  9. package/dist/commands/executor.js +15 -1
  10. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  11. package/dist/commands/storeIntegration.svelte.js +26 -21
  12. package/dist/commands/types.d.ts +2 -0
  13. package/dist/components/App.svelte +162 -192
  14. package/dist/components/App.svelte.d.ts +47 -8
  15. package/dist/components/ConfigForm.svelte +110 -66
  16. package/dist/components/ConfigModal.svelte +7 -2
  17. package/dist/components/ConnectionLine.svelte +4 -2
  18. package/dist/components/Navbar.svelte +61 -1
  19. package/dist/components/NodeSidebar.svelte +27 -45
  20. package/dist/components/NodeStatusOverlay.svelte +94 -6
  21. package/dist/components/NodeSwapPicker.svelte +10 -8
  22. package/dist/components/PipelineStatus.svelte +16 -67
  23. package/dist/components/PortCoordinateTracker.svelte +5 -6
  24. package/dist/components/SchemaForm.stories.svelte +1 -3
  25. package/dist/components/SchemaForm.svelte +45 -40
  26. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  27. package/dist/components/SettingsModal.svelte +8 -3
  28. package/dist/components/SettingsPanel.svelte +20 -4
  29. package/dist/components/SwapMappingEditor.svelte +67 -49
  30. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  31. package/dist/components/UniversalNode.svelte +9 -7
  32. package/dist/components/WorkflowEditor.svelte +118 -111
  33. package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
  34. package/dist/components/chat/AIChatPanel.svelte +93 -89
  35. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  36. package/dist/components/chat/CommandPreview.svelte +2 -1
  37. package/dist/components/console/CommandConsole.svelte +7 -5
  38. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  39. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  40. package/dist/components/console/ConsoleInput.svelte +15 -6
  41. package/dist/components/console/ConsoleOutput.svelte +2 -1
  42. package/dist/components/form/FormArray.svelte +5 -9
  43. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  44. package/dist/components/form/FormAutocomplete.svelte +29 -13
  45. package/dist/components/form/FormField.svelte +4 -2
  46. package/dist/components/form/FormFieldLight.svelte +4 -2
  47. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  48. package/dist/components/form/FormRangeField.svelte +1 -0
  49. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  50. package/dist/components/form/FormToggle.svelte +5 -12
  51. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  52. package/dist/components/form/templateAutocomplete.js +1 -5
  53. package/dist/components/form/types.d.ts +1 -14
  54. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  55. package/dist/components/interrupt/InterruptBubble.svelte +16 -17
  56. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  57. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  58. package/dist/components/layouts/MainLayout.svelte +20 -13
  59. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  60. package/dist/components/nodes/AtomNode.svelte +292 -0
  61. package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
  62. package/dist/components/nodes/GatewayNode.svelte +19 -10
  63. package/dist/components/nodes/IdeaNode.svelte +7 -0
  64. package/dist/components/nodes/SimpleNode.svelte +11 -6
  65. package/dist/components/nodes/SquareNode.svelte +15 -8
  66. package/dist/components/nodes/TerminalNode.svelte +9 -4
  67. package/dist/components/nodes/ToolNode.svelte +7 -1
  68. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  69. package/dist/components/playground/ChatInput.svelte +11 -14
  70. package/dist/components/playground/ChatPanel.svelte +6 -49
  71. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  72. package/dist/components/playground/ControlPanel.svelte +134 -123
  73. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  74. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  75. package/dist/components/playground/InputCollector.svelte +11 -9
  76. package/dist/components/playground/MessageStream.svelte +17 -23
  77. package/dist/components/playground/PipelineKanbanView.svelte +65 -6
  78. package/dist/components/playground/PipelinePanel.svelte +11 -5
  79. package/dist/components/playground/PipelineTableView.svelte +186 -44
  80. package/dist/components/playground/Playground.svelte +95 -92
  81. package/dist/components/playground/Playground.svelte.d.ts +2 -0
  82. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  83. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  84. package/dist/components/playground/PlaygroundModal.svelte +13 -3
  85. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  86. package/dist/components/playground/PlaygroundStudio.svelte +34 -32
  87. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  88. package/dist/components/playground/SessionManager.svelte +9 -12
  89. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
  90. package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
  91. package/dist/config/endpoints.d.ts +0 -7
  92. package/dist/config/endpoints.js +2 -10
  93. package/dist/core/index.d.ts +4 -4
  94. package/dist/core/index.js +6 -6
  95. package/dist/display/index.d.ts +0 -2
  96. package/dist/display/index.js +0 -6
  97. package/dist/editor/index.d.ts +19 -20
  98. package/dist/editor/index.js +25 -35
  99. package/dist/form/code.d.ts +25 -15
  100. package/dist/form/code.js +44 -41
  101. package/dist/form/fieldRegistry.d.ts +17 -13
  102. package/dist/form/fieldRegistry.js +32 -12
  103. package/dist/form/full.d.ts +17 -13
  104. package/dist/form/full.js +22 -27
  105. package/dist/form/index.d.ts +3 -3
  106. package/dist/form/index.js +3 -3
  107. package/dist/form/markdown.d.ts +13 -8
  108. package/dist/form/markdown.js +22 -23
  109. package/dist/helpers/proximityConnect.d.ts +7 -3
  110. package/dist/helpers/proximityConnect.js +19 -6
  111. package/dist/helpers/workflowEditorHelper.d.ts +12 -5
  112. package/dist/helpers/workflowEditorHelper.js +27 -25
  113. package/dist/index.d.ts +28 -24
  114. package/dist/index.js +27 -50
  115. package/dist/messages/defaults.d.ts +2 -5
  116. package/dist/messages/defaults.js +3 -6
  117. package/dist/messages/index.d.ts +0 -1
  118. package/dist/messages/index.js +0 -1
  119. package/dist/mocks/app-forms.d.ts +6 -2
  120. package/dist/mocks/app-forms.js +11 -4
  121. package/dist/openapi/v1/openapi.yaml +227 -164
  122. package/dist/playground/index.d.ts +2 -3
  123. package/dist/playground/index.js +2 -30
  124. package/dist/playground/mount.d.ts +15 -0
  125. package/dist/playground/mount.js +46 -20
  126. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  127. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  128. package/dist/registry/builtinFormats.d.ts +9 -18
  129. package/dist/registry/builtinFormats.js +9 -39
  130. package/dist/registry/builtinNodes.d.ts +1 -26
  131. package/dist/registry/builtinNodes.js +14 -50
  132. package/dist/registry/index.d.ts +3 -4
  133. package/dist/registry/index.js +4 -6
  134. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  135. package/dist/registry/nodeComponentRegistry.js +235 -17
  136. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  137. package/dist/registry/workflowFormatRegistry.js +24 -8
  138. package/dist/{schema → schemas}/index.d.ts +2 -2
  139. package/dist/{schema → schemas}/index.js +2 -2
  140. package/dist/schemas/v1/workflow.schema.json +53 -6
  141. package/dist/services/agentSpecExecutionService.js +0 -1
  142. package/dist/services/apiVariableService.d.ts +2 -1
  143. package/dist/services/apiVariableService.js +5 -22
  144. package/dist/services/autoSaveService.d.ts +7 -0
  145. package/dist/services/autoSaveService.js +6 -4
  146. package/dist/services/chatService.d.ts +8 -4
  147. package/dist/services/chatService.js +15 -15
  148. package/dist/services/draftStorage.d.ts +129 -13
  149. package/dist/services/draftStorage.js +185 -37
  150. package/dist/services/dynamicSchemaService.d.ts +2 -1
  151. package/dist/services/dynamicSchemaService.js +5 -22
  152. package/dist/services/globalSave.d.ts +13 -12
  153. package/dist/services/globalSave.js +29 -51
  154. package/dist/services/historyService.d.ts +9 -3
  155. package/dist/services/historyService.js +9 -3
  156. package/dist/services/interruptService.d.ts +14 -9
  157. package/dist/services/interruptService.js +27 -27
  158. package/dist/services/nodeExecutionService.d.ts +18 -3
  159. package/dist/services/nodeExecutionService.js +71 -45
  160. package/dist/services/playgroundService.d.ts +14 -9
  161. package/dist/services/playgroundService.js +31 -30
  162. package/dist/services/variableService.d.ts +2 -1
  163. package/dist/services/variableService.js +2 -2
  164. package/dist/services/workflowStorage.js +6 -6
  165. package/dist/stores/apiContext.d.ts +45 -0
  166. package/dist/stores/apiContext.js +65 -0
  167. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  168. package/dist/stores/categoriesStore.svelte.js +70 -64
  169. package/dist/stores/getInstance.svelte.d.ts +39 -0
  170. package/dist/stores/getInstance.svelte.js +65 -0
  171. package/dist/stores/historyStore.svelte.d.ts +77 -93
  172. package/dist/stores/historyStore.svelte.js +134 -160
  173. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  174. package/dist/stores/instanceContainer.svelte.js +114 -0
  175. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  176. package/dist/stores/interruptStore.svelte.js +253 -226
  177. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  178. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  179. package/dist/stores/playgroundStore.svelte.d.ts +169 -216
  180. package/dist/stores/playgroundStore.svelte.js +515 -572
  181. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  182. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  183. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  184. package/dist/stores/settingsStore.svelte.js +47 -12
  185. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  186. package/dist/stores/workflowStore.svelte.js +449 -501
  187. package/dist/stories/EdgeDecorator.svelte +5 -2
  188. package/dist/stories/NodeDecorator.svelte +5 -3
  189. package/dist/svelte-app.d.ts +60 -10
  190. package/dist/svelte-app.js +157 -53
  191. package/dist/types/events.d.ts +6 -3
  192. package/dist/types/index.d.ts +71 -6
  193. package/dist/types/navbar.d.ts +7 -0
  194. package/dist/types/playground.d.ts +18 -3
  195. package/dist/types/settings.d.ts +13 -0
  196. package/dist/types/settings.js +1 -0
  197. package/dist/utils/colors.d.ts +47 -21
  198. package/dist/utils/colors.js +69 -68
  199. package/dist/utils/connections.d.ts +9 -15
  200. package/dist/utils/connections.js +13 -32
  201. package/dist/utils/duration.d.ts +13 -0
  202. package/dist/utils/duration.js +45 -0
  203. package/dist/utils/formMerge.d.ts +36 -0
  204. package/dist/utils/formMerge.js +70 -0
  205. package/dist/utils/icons.d.ts +5 -2
  206. package/dist/utils/icons.js +6 -5
  207. package/dist/utils/nodeSwap.d.ts +6 -2
  208. package/dist/utils/nodeSwap.js +62 -126
  209. package/dist/utils/nodeTypes.d.ts +17 -8
  210. package/dist/utils/nodeTypes.js +27 -19
  211. package/dist/utils/performanceUtils.js +7 -0
  212. package/package.json +6 -5
  213. package/dist/messages/deprecation.d.ts +0 -20
  214. package/dist/messages/deprecation.js +0 -33
  215. package/dist/registry/plugin.d.ts +0 -215
  216. package/dist/registry/plugin.js +0 -249
  217. package/dist/services/api.d.ts +0 -129
  218. package/dist/services/api.js +0 -217
@@ -1,5 +1,5 @@
1
1
  <script module lang="ts">
2
- const VIEW_MODE_KEY = 'fd-pipeline-view-mode';
2
+ const VIEW_MODE_KEY_BASE = 'fd-pipeline-view-mode';
3
3
  const BUILTIN_VIEWS = ['graph', 'kanban', 'table'] as const;
4
4
  // `string & {}` preserves autocomplete for built-in values while still accepting arbitrary strings from extraViews.
5
5
  type ViewMode = (typeof BUILTIN_VIEWS)[number] | (string & {});
@@ -7,6 +7,7 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import { onMount } from 'svelte';
10
+ import { getInstance } from '../../stores/getInstance.svelte.js';
10
11
  import PipelineStatus from '../PipelineStatus.svelte';
11
12
  import PipelineKanbanView from './PipelineKanbanView.svelte';
12
13
  import PipelineTableView from './PipelineTableView.svelte';
@@ -46,10 +47,16 @@
46
47
  extraViews = []
47
48
  }: Props = $props();
48
49
 
50
+ const fd = getInstance();
51
+
52
+ // The default instance keeps the legacy bare key; additional instances get
53
+ // a scoped key so two editors' view-mode choices don't overwrite each other.
54
+ const viewModeKey = fd.isDefault ? VIEW_MODE_KEY_BASE : `${VIEW_MODE_KEY_BASE}:${fd.id}`;
55
+
49
56
  let viewMode = $state<ViewMode>('graph');
50
57
 
51
58
  onMount(() => {
52
- const stored = localStorage.getItem(VIEW_MODE_KEY);
59
+ const stored = localStorage.getItem(viewModeKey);
53
60
  if (!stored) return;
54
61
  const validKeys = [...BUILTIN_VIEWS, ...extraViews.map((v) => v.key)];
55
62
  if (validKeys.includes(stored)) viewMode = stored;
@@ -58,7 +65,7 @@
58
65
  function selectViewMode(mode: ViewMode) {
59
66
  viewMode = mode;
60
67
  try {
61
- localStorage.setItem(VIEW_MODE_KEY, mode);
68
+ localStorage.setItem(viewModeKey, mode);
62
69
  } catch (e) {
63
70
  logger.warn('[FlowDrop] Could not persist view mode to localStorage:', e);
64
71
  }
@@ -268,8 +275,7 @@
268
275
  width="100%"
269
276
  showNavbar={false}
270
277
  disableSidebar={true}
271
- lockWorkflow={true}
272
- readOnly={true}
278
+ mode="locked"
273
279
  {endpointConfig}
274
280
  />
275
281
  {/if}
@@ -25,19 +25,26 @@
25
25
  idle: 'mdi:circle-outline'
26
26
  };
27
27
 
28
- function formatDuration(ms: number | null | undefined): string | null {
29
- if (ms == null) return null;
30
- if (ms < 1000) return `${ms}ms`;
31
- if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
32
- const mins = Math.floor(ms / 60000);
33
- const secs = Math.floor((ms % 60000) / 1000);
34
- return `${mins}m ${secs}s`;
35
- }
36
-
37
28
  function formatDateTime(iso: string | null | undefined): string | null {
38
29
  if (!iso) return null;
39
30
  return new Date(iso).toLocaleString();
40
31
  }
32
+
33
+ function formatJson(value: unknown): string {
34
+ if (typeof value === 'string') return value;
35
+ try {
36
+ return JSON.stringify(value, null, 2);
37
+ } catch {
38
+ return String(value);
39
+ }
40
+ }
41
+
42
+ /** Treat null/undefined and empty objects/arrays as "nothing to show". */
43
+ function hasData(value: unknown): boolean {
44
+ if (value == null) return false;
45
+ if (typeof value === 'object') return Object.keys(value).length > 0;
46
+ return value !== '';
47
+ }
41
48
  </script>
42
49
 
43
50
  <script lang="ts">
@@ -45,7 +52,7 @@
45
52
  import Icon from '@iconify/svelte';
46
53
  import { createPipelineDataFetcher, resolveStatus } from './pipelineViewUtils.svelte.js';
47
54
  import { getStatusTextColor } from '../../utils/nodeStatus.js';
48
- import type { NodeStatusData } from './pipelineViewUtils.svelte.js';
55
+ import { formatMicroseconds } from '../../utils/duration.js';
49
56
  import type { Workflow, WorkflowNode } from '../../types/index.js';
50
57
  import type { EndpointConfig } from '../../config/endpoints.js';
51
58
 
@@ -58,13 +65,26 @@
58
65
 
59
66
  let { pipelineId, workflow, endpointConfig, refreshTrigger = 0 }: Props = $props();
60
67
 
61
- interface NodeRow {
62
- node: WorkflowNode;
68
+ interface JobRow {
69
+ /** Stable key: job id, or node id for nodes without a job yet */
70
+ key: string;
71
+ label: string;
72
+ typeId: string;
73
+ nodeId: string;
63
74
  status: NodeStatus;
64
- statusData: NodeStatusData | undefined;
65
- }
66
-
67
- // svelte-ignore state_referenced_locally — endpointConfig is consumed once to build the API client; it must be stable
75
+ started?: string | null;
76
+ completed?: string | null;
77
+ /** Duration in microseconds */
78
+ executionTimeUs?: number | null;
79
+ error?: string | null;
80
+ retryCount?: number | null;
81
+ maxRetries?: number | null;
82
+ inputData?: unknown;
83
+ outputData?: unknown;
84
+ }
85
+
86
+ // endpointConfig is consumed once to build the API client; it must be stable
87
+ // svelte-ignore state_referenced_locally
68
88
  const fetcher = createPipelineDataFetcher(() => pipelineId, endpointConfig);
69
89
 
70
90
  $effect(() => {
@@ -73,28 +93,76 @@
73
93
  return () => clearTimeout(timer);
74
94
  });
75
95
 
76
- const sortedRows = $derived.by((): NodeRow[] =>
77
- workflow.nodes
96
+ // One row per job, timeline style: loop iterations create multiple jobs
97
+ // for the same node and each shows as its own row (label carries the #N
98
+ // suffix). Executed jobs sort by start time; never-started jobs keep
99
+ // pipeline order at the end, followed by nodes that have no job yet.
100
+ const sortedRows = $derived.by((): JobRow[] => {
101
+ const nodesById = new Map<string, WorkflowNode>(workflow.nodes.map((node) => [node.id, node]));
102
+
103
+ const jobRows: JobRow[] = [];
104
+ const nodesWithJobs = new Set<string>();
105
+ for (const job of fetcher.jobs) {
106
+ const node = nodesById.get(job.nodeId);
107
+ if (!node) continue;
108
+ nodesWithJobs.add(job.nodeId);
109
+ jobRows.push({
110
+ key: job.id,
111
+ label: job.label || node.data.label,
112
+ typeId: node.data.metadata.id,
113
+ nodeId: job.nodeId,
114
+ status: resolveStatus({ status: job.status }),
115
+ started: job.started,
116
+ completed: job.completed,
117
+ executionTimeUs: job.executionTimeUs,
118
+ error: job.error,
119
+ retryCount: job.retryCount,
120
+ maxRetries: job.maxRetries,
121
+ inputData: job.inputData,
122
+ outputData: job.outputData
123
+ });
124
+ }
125
+
126
+ const startedRows = jobRows
127
+ .filter((row) => row.started)
128
+ .sort((a, b) => Date.parse(a.started!) - Date.parse(b.started!));
129
+ const neverStartedRows = jobRows.filter((row) => !row.started);
130
+
131
+ const nodeRows: JobRow[] = workflow.nodes
132
+ .filter((node) => !nodesWithJobs.has(node.id))
78
133
  .map((node) => {
79
134
  const statusData = fetcher.nodeStatusMap[node.id];
80
- return { node, status: resolveStatus(statusData), statusData };
135
+ return {
136
+ key: node.id,
137
+ label: node.data.label,
138
+ typeId: node.data.metadata.id,
139
+ nodeId: node.id,
140
+ status: resolveStatus(statusData),
141
+ started: statusData?.last_executed,
142
+ executionTimeUs:
143
+ statusData?.execution_time_us ??
144
+ (statusData?.execution_time != null ? statusData.execution_time * 1000 : null),
145
+ error: statusData?.error
146
+ };
81
147
  })
82
- .sort((a, b) => (STATUS_ORDER[a.status] ?? Infinity) - (STATUS_ORDER[b.status] ?? Infinity))
83
- );
148
+ .sort((a, b) => (STATUS_ORDER[a.status] ?? Infinity) - (STATUS_ORDER[b.status] ?? Infinity));
149
+
150
+ return [...startedRows, ...neverStartedRows, ...nodeRows];
151
+ });
84
152
 
85
153
  let expandedIds = $state(new Set<string>());
86
154
 
87
- function hasDetails(row: NodeRow): boolean {
88
- return !!(row.statusData?.last_executed || row.statusData?.error);
155
+ function hasDetails(row: JobRow): boolean {
156
+ return !!(row.started || row.error || hasData(row.inputData) || hasData(row.outputData));
89
157
  }
90
158
 
91
- function toggleRow(row: NodeRow) {
159
+ function toggleRow(row: JobRow) {
92
160
  if (!hasDetails(row)) return;
93
161
  const next = new Set(expandedIds);
94
- if (next.has(row.node.id)) {
95
- next.delete(row.node.id);
162
+ if (next.has(row.key)) {
163
+ next.delete(row.key);
96
164
  } else {
97
- next.add(row.node.id);
165
+ next.add(row.key);
98
166
  }
99
167
  expandedIds = next;
100
168
  }
@@ -121,12 +189,13 @@
121
189
  <th class="pipeline-table__th">Node</th>
122
190
  <th class="pipeline-table__th">Type</th>
123
191
  <th class="pipeline-table__th">Status</th>
192
+ <th class="pipeline-table__th pipeline-table__th--duration">Duration</th>
124
193
  <th class="pipeline-table__th pipeline-table__th--id">ID</th>
125
194
  </tr>
126
195
  </thead>
127
196
  <tbody>
128
- {#each sortedRows as row (row.node.id)}
129
- {@const expanded = expandedIds.has(row.node.id)}
197
+ {#each sortedRows as row (row.key)}
198
+ {@const expanded = expandedIds.has(row.key)}
130
199
  {@const expandable = hasDetails(row)}
131
200
  <tr
132
201
  class="pipeline-table__row"
@@ -144,12 +213,11 @@
144
213
  />
145
214
  {/if}
146
215
  </td>
147
- <td class="pipeline-table__td pipeline-table__td--label" title={row.node.data.label}
148
- >{row.node.data.label}</td
216
+ <td class="pipeline-table__td pipeline-table__td--label" title={row.label}
217
+ >{row.label}</td
149
218
  >
150
- <td
151
- class="pipeline-table__td pipeline-table__td--muted"
152
- title={row.node.data.metadata.id}>{row.node.data.metadata.id}</td
219
+ <td class="pipeline-table__td pipeline-table__td--muted" title={row.typeId}
220
+ >{row.typeId}</td
153
221
  >
154
222
  <td class="pipeline-table__td">
155
223
  <span
@@ -163,33 +231,62 @@
163
231
  {row.status}
164
232
  </span>
165
233
  </td>
166
- <td class="pipeline-table__td pipeline-table__td--id" title={row.node.id}
167
- >{row.node.id}</td
234
+ <td class="pipeline-table__td pipeline-table__td--duration">
235
+ {formatMicroseconds(row.executionTimeUs) ?? '—'}
236
+ </td>
237
+ <td class="pipeline-table__td pipeline-table__td--id" title={row.nodeId}
238
+ >{row.nodeId}</td
168
239
  >
169
240
  </tr>
170
241
  {#if expanded && expandable}
171
242
  <tr class="pipeline-table__detail-row">
172
- <td colspan="5" class="pipeline-table__detail-cell">
243
+ <td colspan="6" class="pipeline-table__detail-cell">
173
244
  <dl class="pipeline-table__details">
174
- {#if row.statusData?.last_executed}
245
+ {#if row.started}
246
+ <div class="pipeline-table__detail-item">
247
+ <dt>Started</dt>
248
+ <dd>{formatDateTime(row.started)}</dd>
249
+ </div>
250
+ {/if}
251
+ {#if row.completed}
175
252
  <div class="pipeline-table__detail-item">
176
- <dt>Last executed</dt>
177
- <dd>{formatDateTime(row.statusData.last_executed)}</dd>
253
+ <dt>Completed</dt>
254
+ <dd>{formatDateTime(row.completed)}</dd>
178
255
  </div>
179
256
  {/if}
180
- {#if row.statusData?.execution_time != null}
257
+ {#if row.executionTimeUs != null}
181
258
  <div class="pipeline-table__detail-item">
182
259
  <dt>Duration</dt>
183
- <dd>{formatDuration(row.statusData.execution_time)}</dd>
260
+ <dd>{formatMicroseconds(row.executionTimeUs)}</dd>
184
261
  </div>
185
262
  {/if}
186
- {#if row.statusData?.error}
263
+ {#if row.retryCount != null && row.retryCount > 0}
264
+ <div class="pipeline-table__detail-item">
265
+ <dt>Retries</dt>
266
+ <dd>
267
+ {row.retryCount}{row.maxRetries != null ? ` / ${row.maxRetries}` : ''}
268
+ </dd>
269
+ </div>
270
+ {/if}
271
+ {#if row.error}
187
272
  <div class="pipeline-table__detail-item pipeline-table__detail-item--error">
188
273
  <dt>Error</dt>
189
- <dd>{row.statusData.error}</dd>
274
+ <dd>{row.error}</dd>
190
275
  </div>
191
276
  {/if}
192
277
  </dl>
278
+ {#if hasData(row.inputData)}
279
+ <details class="pipeline-table__data">
280
+ <summary class="pipeline-table__data-summary">Input data</summary>
281
+ <pre class="pipeline-table__data-pre">{formatJson(row.inputData)}</pre>
282
+ </details>
283
+ {/if}
284
+ {#if hasData(row.outputData)}
285
+ <details class="pipeline-table__data">
286
+ <summary class="pipeline-table__data-summary">Output data</summary>
287
+ <pre class="pipeline-table__data-pre">{formatJson(row.outputData)}</pre>
288
+ </details>
289
+ {/if}
193
290
  </td>
194
291
  </tr>
195
292
  {/if}
@@ -270,6 +367,19 @@
270
367
  font-family: var(--fd-font-mono, monospace);
271
368
  }
272
369
 
370
+ .pipeline-table__th--duration,
371
+ .pipeline-table__td--duration {
372
+ text-align: right;
373
+ white-space: nowrap;
374
+ font-variant-numeric: tabular-nums;
375
+ }
376
+
377
+ .pipeline-table__td--duration {
378
+ font-family: var(--fd-font-mono, monospace);
379
+ font-size: var(--fd-text-2xs);
380
+ color: var(--fd-muted-foreground);
381
+ }
382
+
273
383
  .pipeline-table__th--expand {
274
384
  width: 1.5rem;
275
385
  padding-right: 0;
@@ -375,6 +485,38 @@
375
485
  font-size: var(--fd-text-2xs);
376
486
  }
377
487
 
488
+ .pipeline-table__data {
489
+ margin-top: var(--fd-space-sm);
490
+ }
491
+
492
+ .pipeline-table__data-summary {
493
+ cursor: pointer;
494
+ font-size: var(--fd-text-2xs);
495
+ font-weight: 600;
496
+ text-transform: uppercase;
497
+ letter-spacing: 0.05em;
498
+ color: var(--fd-muted-foreground);
499
+ user-select: none;
500
+ }
501
+
502
+ .pipeline-table__data-summary:hover {
503
+ color: var(--fd-foreground);
504
+ }
505
+
506
+ .pipeline-table__data-pre {
507
+ margin: var(--fd-space-2xs) 0 0;
508
+ padding: var(--fd-space-sm);
509
+ max-height: 16rem;
510
+ overflow: auto;
511
+ font-family: var(--fd-font-mono, monospace);
512
+ font-size: var(--fd-text-2xs);
513
+ background-color: var(--fd-muted);
514
+ border: 1px solid var(--fd-border);
515
+ border-radius: var(--fd-radius-sm, 4px);
516
+ white-space: pre-wrap;
517
+ word-break: break-word;
518
+ }
519
+
378
520
  .pipeline-table__status {
379
521
  display: inline-flex;
380
522
  align-items: center;