@flowdrop/flowdrop 1.10.0 → 1.12.0

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 (53) hide show
  1. package/dist/api/enhanced-client.d.ts +29 -16
  2. package/dist/api/enhanced-client.js +0 -14
  3. package/dist/components/Navbar.svelte +1 -10
  4. package/dist/components/Navbar.svelte.d.ts +1 -9
  5. package/dist/components/PipelineStatus.svelte +9 -12
  6. package/dist/components/WorkflowEditor.svelte +3 -0
  7. package/dist/components/interrupt/ChoicePrompt.svelte +24 -5
  8. package/dist/components/interrupt/ConfirmationPrompt.svelte +5 -0
  9. package/dist/components/interrupt/InterruptBubble.svelte +12 -0
  10. package/dist/components/interrupt/ReviewPrompt.svelte +20 -0
  11. package/dist/components/interrupt/TextInputPrompt.svelte +5 -0
  12. package/dist/components/nodes/GatewayNode.svelte +2 -6
  13. package/dist/components/nodes/WorkflowNode.svelte +2 -6
  14. package/dist/components/playground/ChatInput.svelte +359 -0
  15. package/dist/components/playground/ChatInput.svelte.d.ts +14 -0
  16. package/dist/components/playground/ChatPanel.svelte +100 -724
  17. package/dist/components/playground/ChatPanel.svelte.d.ts +9 -26
  18. package/dist/components/playground/ControlPanel.svelte +496 -0
  19. package/dist/components/playground/ControlPanel.svelte.d.ts +20 -0
  20. package/dist/components/playground/ExecutionConsole.svelte +163 -0
  21. package/dist/components/playground/ExecutionConsole.svelte.d.ts +14 -0
  22. package/dist/components/playground/MessageStream.svelte +283 -0
  23. package/dist/components/playground/MessageStream.svelte.d.ts +27 -0
  24. package/dist/components/playground/PipelineKanbanView.svelte +284 -0
  25. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +11 -0
  26. package/dist/components/playground/PipelinePanel.svelte +204 -65
  27. package/dist/components/playground/PipelinePanel.svelte.d.ts +3 -1
  28. package/dist/components/playground/PipelineTableView.svelte +376 -0
  29. package/dist/components/playground/PipelineTableView.svelte.d.ts +11 -0
  30. package/dist/components/playground/Playground.svelte +262 -1200
  31. package/dist/components/playground/Playground.svelte.d.ts +0 -13
  32. package/dist/components/playground/PlaygroundApp.svelte +110 -0
  33. package/dist/components/playground/PlaygroundApp.svelte.d.ts +28 -0
  34. package/dist/components/playground/PlaygroundStudio.svelte +35 -61
  35. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -1
  36. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +22 -0
  37. package/dist/components/playground/pipelineViewUtils.svelte.js +77 -0
  38. package/dist/messages/defaults.d.ts +24 -0
  39. package/dist/messages/defaults.js +24 -0
  40. package/dist/playground/index.d.ts +8 -2
  41. package/dist/playground/index.js +8 -1
  42. package/dist/playground/mount.d.ts +59 -4
  43. package/dist/playground/mount.js +102 -9
  44. package/dist/stores/playgroundStore.svelte.d.ts +6 -0
  45. package/dist/stores/playgroundStore.svelte.js +21 -1
  46. package/dist/svelte-app.d.ts +2 -10
  47. package/dist/types/index.d.ts +28 -2
  48. package/dist/types/navbar.d.ts +14 -0
  49. package/dist/types/navbar.js +1 -0
  50. package/dist/types/playground.d.ts +5 -2
  51. package/dist/types/playground.js +5 -7
  52. package/dist/utils/nodeStatus.js +15 -5
  53. package/package.json +1 -1
@@ -34,6 +34,34 @@ export declare class ApiError extends Error {
34
34
  * const client = new EnhancedFlowDropApiClient(config);
35
35
  * ```
36
36
  */
37
+ export interface PipelineDataResponse {
38
+ status: string;
39
+ jobs: Array<Record<string, unknown>>;
40
+ node_statuses: Record<string, {
41
+ status: string;
42
+ [key: string]: unknown;
43
+ }>;
44
+ job_status_summary: {
45
+ total: number;
46
+ pending: number;
47
+ running: number;
48
+ completed: number;
49
+ failed: number;
50
+ cancelled: number;
51
+ skipped?: number;
52
+ paused?: number;
53
+ interrupted?: number;
54
+ };
55
+ kanban_config?: {
56
+ columns: Array<{
57
+ key: string;
58
+ label: string;
59
+ statuses: string[];
60
+ icon?: string;
61
+ color?: string;
62
+ }>;
63
+ };
64
+ }
37
65
  export declare class EnhancedFlowDropApiClient {
38
66
  private config;
39
67
  private authProvider;
@@ -164,20 +192,5 @@ export declare class EnhancedFlowDropApiClient {
164
192
  /**
165
193
  * Fetch pipeline data including job information and status
166
194
  */
167
- getPipelineData(pipelineId: string): Promise<{
168
- status: string;
169
- jobs: Array<Record<string, unknown>>;
170
- node_statuses: Record<string, {
171
- status: string;
172
- [key: string]: unknown;
173
- }>;
174
- job_status_summary: {
175
- total: number;
176
- pending: number;
177
- running: number;
178
- completed: number;
179
- failed: number;
180
- cancelled: number;
181
- };
182
- }>;
195
+ getPipelineData(pipelineId: string): Promise<PipelineDataResponse>;
183
196
  }
@@ -28,20 +28,6 @@ export class ApiError extends Error {
28
28
  this.errorData = errorData;
29
29
  }
30
30
  }
31
- /**
32
- * Enhanced HTTP API client for FlowDrop with configurable endpoints
33
- *
34
- * Supports pluggable authentication via AuthProvider interface.
35
- *
36
- * @example
37
- * ```typescript
38
- * // With AuthProvider
39
- * const client = new EnhancedFlowDropApiClient(config, authProvider);
40
- *
41
- * // Without authentication
42
- * const client = new EnhancedFlowDropApiClient(config);
43
- * ```
44
- */
45
31
  export class EnhancedFlowDropApiClient {
46
32
  config;
47
33
  authProvider;
@@ -11,18 +11,9 @@
11
11
  import Logo from './Logo.svelte';
12
12
  import SettingsModal from './SettingsModal.svelte';
13
13
  import type { SettingsCategory } from '../types/settings.js';
14
+ import type { NavbarAction } from '../types/navbar.js';
14
15
  import { m } from '../messages/index.js';
15
16
 
16
- interface NavbarAction {
17
- label: string;
18
- href: string;
19
- icon?: string;
20
- variant?: 'primary' | 'secondary' | 'outline';
21
- onclick?: (event: Event) => void;
22
- /** If true, opens link in new tab with proper security attributes */
23
- external?: boolean;
24
- }
25
-
26
17
  interface BreadcrumbItem {
27
18
  label: string;
28
19
  href?: string;
@@ -1,13 +1,5 @@
1
1
  import type { SettingsCategory } from '../types/settings.js';
2
- interface NavbarAction {
3
- label: string;
4
- href: string;
5
- icon?: string;
6
- variant?: 'primary' | 'secondary' | 'outline';
7
- onclick?: (event: Event) => void;
8
- /** If true, opens link in new tab with proper security attributes */
9
- external?: boolean;
10
- }
2
+ import type { NavbarAction } from '../types/navbar.js';
11
3
  interface BreadcrumbItem {
12
4
  label: string;
13
5
  href?: string;
@@ -117,23 +117,20 @@
117
117
  }
118
118
  };
119
119
 
120
- // Update node statuses based on job data
120
+ // Update node statuses based on job data — only set what the server reported
121
121
  if (jobStatusData.node_statuses) {
122
122
  const newNodeStatuses: Record<string, 'pending' | 'running' | 'completed' | 'error'> = {};
123
123
 
124
- // Initialize all nodes as pending
125
- if (workflow && workflow.nodes) {
126
- workflow.nodes.forEach((node) => {
127
- newNodeStatuses[node.id] = 'pending';
128
- });
129
- }
130
-
131
- // Override with actual job statuses
132
124
  for (const nodeId in jobStatusData.node_statuses) {
133
125
  const status = jobStatusData.node_statuses[nodeId].status;
134
- if (['pending', 'running', 'completed', 'failed', 'cancelled'].includes(status)) {
135
- newNodeStatuses[nodeId] =
136
- status === 'failed' ? 'error' : (status as 'pending' | 'running' | 'completed');
126
+ if (status === 'failed' || status === 'cancelled') {
127
+ newNodeStatuses[nodeId] = 'error';
128
+ } else if (status === 'running' || status === 'paused' || status === 'interrupted') {
129
+ newNodeStatuses[nodeId] = 'running';
130
+ } else if (status === 'completed' || status === 'skipped') {
131
+ newNodeStatuses[nodeId] = 'completed';
132
+ } else if (status === 'pending' || status === 'idle') {
133
+ newNodeStatuses[nodeId] = 'pending';
137
134
  }
138
135
  }
139
136
  nodeStatuses = newNodeStatuses;
@@ -855,6 +855,9 @@
855
855
  {initialViewport}
856
856
  colorMode={getResolvedTheme() as ColorMode}
857
857
  fitView={getEditorSettings().fitViewOnLoad}
858
+ nodesDraggable={!props.lockWorkflow && !props.readOnly}
859
+ nodesConnectable={!props.lockWorkflow && !props.readOnly}
860
+ elementsSelectable={!props.lockWorkflow && !props.readOnly}
858
861
  >
859
862
  <Controls />
860
863
  {#if !props.readOnly && !props.lockWorkflow && props.onToggleConsole}
@@ -45,6 +45,10 @@
45
45
  // Hoist the choice branch — counter, min, max, submit reads.
46
46
  const t = $derived(m().interrupt.choice);
47
47
 
48
+ // Unique name for the radio/checkbox group so multiple ChoicePrompts on
49
+ // screen don't share the same HTML group and interfere with each other.
50
+ const groupName = `choice-option-${Math.random().toString(36).slice(2, 8)}`;
51
+
48
52
  /** Local state for selected values */
49
53
  let selectedValues = $state<Set<string>>(new Set());
50
54
 
@@ -131,7 +135,7 @@
131
135
  {/if}
132
136
 
133
137
  <!-- Options -->
134
- <div class="choice-prompt__options" role={isMultiple ? 'group' : 'radiogroup'}>
138
+ <div class="choice-prompt__options" role={isMultiple ? 'group' : 'radiogroup'} aria-label={config.message}>
135
139
  {#each config.options as option (option.value)}
136
140
  {@const isChecked = isResolved ? isOptionResolved(option) : selectedValues.has(option.value)}
137
141
  <label
@@ -141,7 +145,7 @@
141
145
  >
142
146
  <input
143
147
  type={isMultiple ? 'checkbox' : 'radio'}
144
- name="choice-option"
148
+ name={groupName}
145
149
  value={option.value}
146
150
  checked={isChecked}
147
151
  disabled={isResolved || isSubmitting}
@@ -295,9 +299,19 @@
295
299
 
296
300
  .choice-prompt__input {
297
301
  position: absolute;
298
- opacity: 0;
299
- width: 0;
300
- height: 0;
302
+ width: 1px;
303
+ height: 1px;
304
+ padding: 0;
305
+ margin: -1px;
306
+ overflow: hidden;
307
+ clip: rect(0, 0, 0, 0);
308
+ white-space: nowrap;
309
+ border: 0;
310
+ }
311
+
312
+ .choice-prompt__option:focus-within {
313
+ outline: 2px solid var(--fd-ring);
314
+ outline-offset: 2px;
301
315
  }
302
316
 
303
317
  .choice-prompt__checkmark {
@@ -372,6 +386,11 @@
372
386
  transform: translateY(-1px);
373
387
  }
374
388
 
389
+ .choice-prompt__submit:focus-visible {
390
+ outline: 2px solid var(--fd-ring);
391
+ outline-offset: 2px;
392
+ }
393
+
375
394
  .choice-prompt__submit:disabled {
376
395
  opacity: 0.5;
377
396
  cursor: not-allowed;
@@ -184,6 +184,11 @@
184
184
  min-width: 100px;
185
185
  }
186
186
 
187
+ .confirmation-prompt__button:focus-visible {
188
+ outline: 2px solid var(--fd-ring);
189
+ outline-offset: 2px;
190
+ }
191
+
187
192
  .confirmation-prompt__button:disabled {
188
193
  cursor: not-allowed;
189
194
  }
@@ -256,6 +256,8 @@
256
256
  class:interrupt-bubble--cancelled={currentInterrupt.machineState.status === 'cancelled'}
257
257
  class:interrupt-bubble--submitting={isSubmitting}
258
258
  class:interrupt-bubble--error={currentInterrupt.machineState.status === 'error'}
259
+ role="group"
260
+ aria-label={getTypeLabel(currentInterrupt.type)}
259
261
  >
260
262
  <!-- Header -->
261
263
  <div class="interrupt-bubble__header">
@@ -517,6 +519,11 @@
517
519
  background-color: var(--fd-error-hover);
518
520
  }
519
521
 
522
+ .interrupt-bubble__retry-btn:focus-visible {
523
+ outline: 2px solid var(--fd-ring);
524
+ outline-offset: 2px;
525
+ }
526
+
520
527
  /* Body - prompt content area, full width */
521
528
  .interrupt-bubble__body {
522
529
  padding: var(--fd-space-xl);
@@ -589,6 +596,11 @@
589
596
  background-color: var(--fd-error-muted);
590
597
  }
591
598
 
599
+ .interrupt-bubble__cancel-btn:focus-visible {
600
+ outline: 2px solid var(--fd-ring);
601
+ outline-offset: 2px;
602
+ }
603
+
592
604
  .interrupt-bubble__cancel-btn:disabled {
593
605
  opacity: 0.5;
594
606
  cursor: not-allowed;
@@ -533,6 +533,11 @@
533
533
  color: var(--fd-error);
534
534
  }
535
535
 
536
+ .review-prompt__bulk-btn:focus-visible {
537
+ outline: 2px solid var(--fd-ring);
538
+ outline-offset: 2px;
539
+ }
540
+
536
541
  .review-prompt__bulk-btn:disabled {
537
542
  opacity: 0.5;
538
543
  cursor: not-allowed;
@@ -630,6 +635,11 @@
630
635
  color: var(--fd-error-foreground);
631
636
  }
632
637
 
638
+ .review-prompt__toggle-btn:focus-visible {
639
+ outline: 2px solid var(--fd-ring);
640
+ outline-offset: 2px;
641
+ }
642
+
633
643
  .review-prompt__toggle-btn:disabled {
634
644
  opacity: 0.5;
635
645
  cursor: not-allowed;
@@ -722,6 +732,11 @@
722
732
  border-color: var(--fd-border-strong);
723
733
  }
724
734
 
735
+ .review-prompt__html-toggle-btn:focus-visible {
736
+ outline: 2px solid var(--fd-ring);
737
+ outline-offset: 2px;
738
+ }
739
+
725
740
  /* Raw HTML code display */
726
741
  .review-prompt__raw-html {
727
742
  font-family: var(--fd-review-font-mono);
@@ -820,6 +835,11 @@
820
835
  transform: translateY(-1px);
821
836
  }
822
837
 
838
+ .review-prompt__submit:focus-visible {
839
+ outline: 2px solid var(--fd-ring);
840
+ outline-offset: 2px;
841
+ }
842
+
823
843
  .review-prompt__submit:disabled {
824
844
  opacity: 0.5;
825
845
  cursor: not-allowed;
@@ -315,6 +315,11 @@
315
315
  transform: translateY(-1px);
316
316
  }
317
317
 
318
+ .text-prompt__submit:focus-visible {
319
+ outline: 2px solid var(--fd-ring);
320
+ outline-offset: 2px;
321
+ }
322
+
318
323
  .text-prompt__submit:disabled {
319
324
  opacity: 0.5;
320
325
  cursor: not-allowed;
@@ -210,9 +210,7 @@
210
210
  style="top: 50%; transform: translateY(-50%); --fd-handle-fill: {getDataTypeColorToken(
211
211
  port.dataType
212
212
  )}; --fd-handle-border-color: var(--fd-handle-border);"
213
- role="button"
214
- tabindex={0}
215
- aria-label={graph.connectInputPort({ name: port.name })}
213
+ tabindex={-1}
216
214
  />
217
215
 
218
216
  <!-- Port Info: padding lives here so handle position is simple -->
@@ -298,9 +296,7 @@
298
296
  : getDataTypeColorToken(
299
297
  'trigger'
300
298
  )}; --fd-handle-border-color: var(--fd-handle-border);"
301
- role="button"
302
- tabindex={0}
303
- aria-label={graph.connectBranch({ name: branch.name })}
299
+ tabindex={-1}
304
300
  />
305
301
  </div>
306
302
  {/each}
@@ -264,9 +264,7 @@
264
264
  style="top: 50%; transform: translateY(-50%); --fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColorToken(
265
265
  port.dataType
266
266
  )}); --fd-handle-border-color: var(--fd-handle-border);"
267
- role="button"
268
- tabindex={0}
269
- aria-label={graph.connectInputPort({ name: port.name })}
267
+ tabindex={-1}
270
268
  />
271
269
 
272
270
  <!-- Port Info: padding lives here so handle position is simple -->
@@ -342,9 +340,7 @@
342
340
  style="top: 50%; transform: translateY(-50%); --fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColorToken(
343
341
  port.dataType
344
342
  )}); --fd-handle-border-color: var(--fd-handle-border);"
345
- role="button"
346
- tabindex={0}
347
- aria-label={graph.connectOutputPort({ name: port.name })}
343
+ tabindex={-1}
348
344
  />
349
345
  </div>
350
346
  {/each}