@d34dman/flowdrop 0.0.23 → 0.0.24

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.
@@ -131,7 +131,7 @@
131
131
  if (inputEl.id && !inputEl.id.startsWith('ext-')) {
132
132
  if (inputEl instanceof HTMLInputElement && inputEl.type === 'checkbox') {
133
133
  updatedConfig[inputEl.id] = inputEl.checked;
134
- } else if (inputEl instanceof HTMLInputElement && inputEl.type === 'number') {
134
+ } else if (inputEl instanceof HTMLInputElement && (inputEl.type === 'number' || inputEl.type === 'range')) {
135
135
  updatedConfig[inputEl.id] = inputEl.value ? Number(inputEl.value) : inputEl.value;
136
136
  } else if (inputEl instanceof HTMLInputElement && inputEl.type === 'hidden') {
137
137
  // Parse hidden field values that might be JSON
@@ -5,7 +5,7 @@
5
5
  Features:
6
6
  - Automatically selects the correct field component based on schema
7
7
  - Wraps fields with FormFieldWrapper for consistent layout
8
- - Supports all current field types (string, number, boolean, select, checkbox group)
8
+ - Supports all current field types (string, number, boolean, select, checkbox group, range)
9
9
  - Extensible architecture for future complex types (array, object)
10
10
 
11
11
  Type Resolution Order:
@@ -13,11 +13,12 @@
13
13
  2. enum with multiple: true -> FormCheckboxGroup
14
14
  3. enum -> FormSelect
15
15
  4. format: 'multiline' -> FormTextarea
16
- 5. type: 'string' -> FormTextField
17
- 6. type: 'number' or 'integer' -> FormNumberField
18
- 7. type: 'boolean' -> FormToggle
19
- 8. type: 'select' or has options -> FormSelect
20
- 9. fallback -> FormTextField
16
+ 5. format: 'range' (number/integer) -> FormRangeField
17
+ 6. type: 'string' -> FormTextField
18
+ 7. type: 'number' or 'integer' -> FormNumberField
19
+ 8. type: 'boolean' -> FormToggle
20
+ 9. type: 'select' or has options -> FormSelect
21
+ 10. fallback -> FormTextField
21
22
  -->
22
23
 
23
24
  <script lang="ts">
@@ -25,6 +26,7 @@
25
26
  import FormTextField from "./FormTextField.svelte";
26
27
  import FormTextarea from "./FormTextarea.svelte";
27
28
  import FormNumberField from "./FormNumberField.svelte";
29
+ import FormRangeField from "./FormRangeField.svelte";
28
30
  import FormToggle from "./FormToggle.svelte";
29
31
  import FormSelect from "./FormSelect.svelte";
30
32
  import FormCheckboxGroup from "./FormCheckboxGroup.svelte";
@@ -94,6 +96,11 @@
94
96
  return "textarea";
95
97
  }
96
98
 
99
+ // Range slider for number/integer with format: "range"
100
+ if ((schema.type === "number" || schema.type === "integer") && schema.format === "range") {
101
+ return "range";
102
+ }
103
+
97
104
  // String -> text field
98
105
  if (schema.type === "string") {
99
106
  return "text";
@@ -218,6 +225,17 @@
218
225
  ariaDescribedBy={descriptionId}
219
226
  onChange={(val) => onChange(val)}
220
227
  />
228
+ {:else if fieldType === "range"}
229
+ <FormRangeField
230
+ id={fieldKey}
231
+ value={numberValue}
232
+ min={schema.minimum}
233
+ max={schema.maximum}
234
+ step={schema.step}
235
+ {required}
236
+ ariaDescribedBy={descriptionId}
237
+ onChange={(val) => onChange(val)}
238
+ />
221
239
  {:else if fieldType === "toggle"}
222
240
  <FormToggle
223
241
  id={fieldKey}
@@ -0,0 +1,252 @@
1
+ <!--
2
+ FormRangeField Component
3
+ Range slider input field for numeric values
4
+
5
+ Features:
6
+ - Modern styled range slider with custom track and thumb
7
+ - Min/max/step support for value constraints
8
+ - Real-time value display with tabular numeric font
9
+ - Proper ARIA attributes for accessibility
10
+ - Visual feedback for current value position
11
+ -->
12
+
13
+ <script lang="ts">
14
+ interface Props {
15
+ /** Field identifier */
16
+ id: string;
17
+ /** Current value */
18
+ value: number | string;
19
+ /** Minimum allowed value */
20
+ min?: number;
21
+ /** Maximum allowed value */
22
+ max?: number;
23
+ /** Step increment */
24
+ step?: number;
25
+ /** Whether the field is required */
26
+ required?: boolean;
27
+ /** ARIA description ID */
28
+ ariaDescribedBy?: string;
29
+ /** Callback when value changes */
30
+ onChange: (value: number) => void;
31
+ }
32
+
33
+ let {
34
+ id,
35
+ value = 0,
36
+ min = 0,
37
+ max = 100,
38
+ step = 1,
39
+ required = false,
40
+ ariaDescribedBy,
41
+ onChange
42
+ }: Props = $props();
43
+
44
+ /**
45
+ * Compute the current numeric value
46
+ * Handles string values and defaults
47
+ */
48
+ const numericValue = $derived.by((): number => {
49
+ if (typeof value === "number") {
50
+ return value;
51
+ }
52
+ const parsed = Number(value);
53
+ return isNaN(parsed) ? min : parsed;
54
+ });
55
+
56
+ /**
57
+ * Compute the percentage position for the filled track
58
+ * Used to show visual progress of the slider
59
+ */
60
+ const progressPercentage = $derived.by((): number => {
61
+ const range = max - min;
62
+ if (range === 0) return 0;
63
+ return ((numericValue - min) / range) * 100;
64
+ });
65
+
66
+ /**
67
+ * Handle input changes
68
+ * Converts the value to a number and triggers onChange
69
+ */
70
+ function handleInput(event: Event): void {
71
+ const target = event.currentTarget as HTMLInputElement;
72
+ const numValue = Number(target.value);
73
+ onChange(numValue);
74
+ }
75
+ </script>
76
+
77
+ <div class="form-range-container">
78
+ <div class="form-range-slider">
79
+ <input
80
+ {id}
81
+ type="range"
82
+ class="form-range-field"
83
+ value={numericValue}
84
+ {min}
85
+ {max}
86
+ {step}
87
+ aria-describedby={ariaDescribedBy}
88
+ aria-required={required}
89
+ aria-valuemin={min}
90
+ aria-valuemax={max}
91
+ aria-valuenow={numericValue}
92
+ oninput={handleInput}
93
+ style="--progress: {progressPercentage}%"
94
+ />
95
+ </div>
96
+ <div class="form-range-values">
97
+ <span class="form-range-min">{min}</span>
98
+ <span class="form-range-current">{numericValue}</span>
99
+ <span class="form-range-max">{max}</span>
100
+ </div>
101
+ </div>
102
+
103
+ <style>
104
+ .form-range-container {
105
+ display: flex;
106
+ flex-direction: column;
107
+ gap: 0.5rem;
108
+ width: 100%;
109
+ }
110
+
111
+ .form-range-slider {
112
+ position: relative;
113
+ width: 100%;
114
+ padding: 0.25rem 0;
115
+ }
116
+
117
+ .form-range-field {
118
+ width: 100%;
119
+ height: 6px;
120
+ border-radius: 3px;
121
+ appearance: none;
122
+ -webkit-appearance: none;
123
+ background: linear-gradient(
124
+ to right,
125
+ var(--color-ref-blue-500, #3b82f6) 0%,
126
+ var(--color-ref-blue-500, #3b82f6) var(--progress, 0%),
127
+ var(--color-ref-gray-200, #e5e7eb) var(--progress, 0%),
128
+ var(--color-ref-gray-200, #e5e7eb) 100%
129
+ );
130
+ cursor: pointer;
131
+ transition: background 0.15s ease;
132
+ }
133
+
134
+ /* Webkit (Chrome, Safari, Edge) - Track */
135
+ .form-range-field::-webkit-slider-runnable-track {
136
+ height: 6px;
137
+ border-radius: 3px;
138
+ }
139
+
140
+ /* Webkit - Thumb */
141
+ .form-range-field::-webkit-slider-thumb {
142
+ -webkit-appearance: none;
143
+ appearance: none;
144
+ width: 18px;
145
+ height: 18px;
146
+ border-radius: 50%;
147
+ background: linear-gradient(
148
+ 135deg,
149
+ #ffffff 0%,
150
+ var(--color-ref-gray-50, #f9fafb) 100%
151
+ );
152
+ border: 2px solid var(--color-ref-blue-500, #3b82f6);
153
+ box-shadow:
154
+ 0 2px 6px rgba(59, 130, 246, 0.25),
155
+ 0 1px 2px rgba(0, 0, 0, 0.1);
156
+ cursor: pointer;
157
+ margin-top: -6px;
158
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
159
+ }
160
+
161
+ .form-range-field::-webkit-slider-thumb:hover {
162
+ transform: scale(1.1);
163
+ box-shadow:
164
+ 0 4px 12px rgba(59, 130, 246, 0.35),
165
+ 0 2px 4px rgba(0, 0, 0, 0.1);
166
+ }
167
+
168
+ .form-range-field:focus::-webkit-slider-thumb {
169
+ box-shadow:
170
+ 0 0 0 3px rgba(59, 130, 246, 0.2),
171
+ 0 4px 12px rgba(59, 130, 246, 0.35);
172
+ }
173
+
174
+ /* Firefox - Track */
175
+ .form-range-field::-moz-range-track {
176
+ height: 6px;
177
+ border-radius: 3px;
178
+ background: var(--color-ref-gray-200, #e5e7eb);
179
+ }
180
+
181
+ .form-range-field::-moz-range-progress {
182
+ height: 6px;
183
+ border-radius: 3px;
184
+ background: var(--color-ref-blue-500, #3b82f6);
185
+ }
186
+
187
+ /* Firefox - Thumb */
188
+ .form-range-field::-moz-range-thumb {
189
+ width: 18px;
190
+ height: 18px;
191
+ border-radius: 50%;
192
+ background: linear-gradient(
193
+ 135deg,
194
+ #ffffff 0%,
195
+ var(--color-ref-gray-50, #f9fafb) 100%
196
+ );
197
+ border: 2px solid var(--color-ref-blue-500, #3b82f6);
198
+ box-shadow:
199
+ 0 2px 6px rgba(59, 130, 246, 0.25),
200
+ 0 1px 2px rgba(0, 0, 0, 0.1);
201
+ cursor: pointer;
202
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
203
+ }
204
+
205
+ .form-range-field::-moz-range-thumb:hover {
206
+ transform: scale(1.1);
207
+ box-shadow:
208
+ 0 4px 12px rgba(59, 130, 246, 0.35),
209
+ 0 2px 4px rgba(0, 0, 0, 0.1);
210
+ }
211
+
212
+ .form-range-field:focus::-moz-range-thumb {
213
+ box-shadow:
214
+ 0 0 0 3px rgba(59, 130, 246, 0.2),
215
+ 0 4px 12px rgba(59, 130, 246, 0.35);
216
+ }
217
+
218
+ /* Focus styles */
219
+ .form-range-field:focus {
220
+ outline: none;
221
+ }
222
+
223
+ .form-range-field:focus-visible {
224
+ outline: none;
225
+ }
226
+
227
+ /* Value display row */
228
+ .form-range-values {
229
+ display: flex;
230
+ justify-content: space-between;
231
+ align-items: center;
232
+ font-size: 0.75rem;
233
+ font-variant-numeric: tabular-nums;
234
+ }
235
+
236
+ .form-range-min,
237
+ .form-range-max {
238
+ color: var(--color-ref-gray-400, #9ca3af);
239
+ font-weight: 400;
240
+ }
241
+
242
+ .form-range-current {
243
+ font-weight: 600;
244
+ color: var(--color-ref-blue-600, #2563eb);
245
+ background-color: var(--color-ref-blue-50, #eff6ff);
246
+ padding: 0.125rem 0.5rem;
247
+ border-radius: 0.25rem;
248
+ min-width: 2.5rem;
249
+ text-align: center;
250
+ }
251
+ </style>
252
+
@@ -0,0 +1,21 @@
1
+ interface Props {
2
+ /** Field identifier */
3
+ id: string;
4
+ /** Current value */
5
+ value: number | string;
6
+ /** Minimum allowed value */
7
+ min?: number;
8
+ /** Maximum allowed value */
9
+ max?: number;
10
+ /** Step increment */
11
+ step?: number;
12
+ /** Whether the field is required */
13
+ required?: boolean;
14
+ /** ARIA description ID */
15
+ ariaDescribedBy?: string;
16
+ /** Callback when value changes */
17
+ onChange: (value: number) => void;
18
+ }
19
+ declare const FormRangeField: import("svelte").Component<Props, {}, "">;
20
+ type FormRangeField = ReturnType<typeof FormRangeField>;
21
+ export default FormRangeField;
@@ -35,6 +35,7 @@ export { default as FormFieldWrapper } from "./FormFieldWrapper.svelte";
35
35
  export { default as FormTextField } from "./FormTextField.svelte";
36
36
  export { default as FormTextarea } from "./FormTextarea.svelte";
37
37
  export { default as FormNumberField } from "./FormNumberField.svelte";
38
+ export { default as FormRangeField } from "./FormRangeField.svelte";
38
39
  export { default as FormToggle } from "./FormToggle.svelte";
39
40
  export { default as FormSelect } from "./FormSelect.svelte";
40
41
  export { default as FormCheckboxGroup } from "./FormCheckboxGroup.svelte";
@@ -39,6 +39,7 @@ export { default as FormFieldWrapper } from "./FormFieldWrapper.svelte";
39
39
  export { default as FormTextField } from "./FormTextField.svelte";
40
40
  export { default as FormTextarea } from "./FormTextarea.svelte";
41
41
  export { default as FormNumberField } from "./FormNumberField.svelte";
42
+ export { default as FormRangeField } from "./FormRangeField.svelte";
42
43
  export { default as FormToggle } from "./FormToggle.svelte";
43
44
  export { default as FormSelect } from "./FormSelect.svelte";
44
45
  export { default as FormCheckboxGroup } from "./FormCheckboxGroup.svelte";
@@ -14,8 +14,9 @@ export type FieldType = "string" | "number" | "integer" | "boolean" | "select" |
14
14
  * Field format for specialized rendering
15
15
  * - multiline: Renders as textarea
16
16
  * - hidden: Field is hidden from UI but included in form submission
17
+ * - range: Renders as range slider for numeric values
17
18
  */
18
- export type FieldFormat = "multiline" | "hidden" | string;
19
+ export type FieldFormat = "multiline" | "hidden" | "range" | string;
19
20
  /**
20
21
  * Option type for select and checkbox group fields
21
22
  */
@@ -84,6 +85,19 @@ export interface ToggleFieldProps extends BaseFieldProps {
84
85
  offLabel?: string;
85
86
  onChange: (value: boolean) => void;
86
87
  }
88
+ /**
89
+ * Properties for range slider fields
90
+ */
91
+ export interface RangeFieldProps extends BaseFieldProps {
92
+ value: number | string;
93
+ /** Minimum allowed value */
94
+ min?: number;
95
+ /** Maximum allowed value */
96
+ max?: number;
97
+ /** Step increment for the slider */
98
+ step?: number;
99
+ onChange: (value: number) => void;
100
+ }
87
101
  /**
88
102
  * Properties for select dropdown fields
89
103
  */
@@ -144,6 +158,8 @@ export interface FieldSchema {
144
158
  minimum?: number;
145
159
  /** Maximum value for numbers */
146
160
  maximum?: number;
161
+ /** Step increment for number/range inputs */
162
+ step?: number;
147
163
  /** Minimum length for strings */
148
164
  minLength?: number;
149
165
  /** Maximum length for strings */
@@ -10,24 +10,12 @@
10
10
 
11
11
  <script lang="ts">
12
12
  import { Position, Handle } from '@xyflow/svelte';
13
- import type { WorkflowNode, NodePort } from '../../types/index.js';
13
+ import type { WorkflowNode, NodePort, Branch } from '../../types/index.js';
14
14
  import Icon from '@iconify/svelte';
15
15
  import { getNodeIcon } from '../../utils/icons.js';
16
16
  import { getDataTypeColorToken, getCategoryColorToken } from '../../utils/colors.js';
17
17
  import { connectedHandles } from '../../stores/workflowStore.js';
18
18
 
19
- /**
20
- * Branch interface for gateway nodes
21
- * - name: Internal identifier used for handle IDs and connections
22
- * - label: Display label shown in the UI (optional, defaults to name)
23
- * - value: Optional value associated with the branch (e.g., for Switch matching)
24
- */
25
- interface Branch {
26
- name: string;
27
- label?: string;
28
- value?: string;
29
- }
30
-
31
19
  interface Props {
32
20
  data: WorkflowNode['data'] & {
33
21
  nodeId?: string;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import './styles/base.css';
6
6
  import './registry/builtinNodes.js';
7
- export type { NodeCategory, NodeDataType, NodePort, NodeMetadata, NodeExtensions, NodeUIExtensions, ConfigValues, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents, BuiltinNodeType } from './types/index.js';
7
+ export type { NodeCategory, NodeDataType, NodePort, DynamicPort, Branch, NodeMetadata, NodeExtensions, NodeUIExtensions, ConfigValues, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents, BuiltinNodeType } from './types/index.js';
8
8
  export type { WorkflowEditorConfig, EditorFeatures, UIConfig, APIConfig, ExecutionConfig, StorageConfig } from './types/config.js';
9
9
  export type { AuthProvider, StaticAuthConfig, CallbackAuthConfig } from './types/auth.js';
10
10
  export { StaticAuthProvider, CallbackAuthProvider, NoAuthProvider } from './types/auth.js';
@@ -87,6 +87,37 @@ export interface DynamicPort {
87
87
  /** Whether this port is required for execution */
88
88
  required?: boolean;
89
89
  }
90
+ /**
91
+ * Branch configuration for gateway nodes
92
+ *
93
+ * Branches define conditional output paths in gateway/switch nodes.
94
+ * Each branch creates an output handle that can be connected to downstream nodes.
95
+ * Branches are stored in `config.branches` array and support dynamic addition/removal
96
+ * through the node configuration panel.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const branches: Branch[] = [
101
+ * { name: "high", label: "High Priority", condition: "priority > 8" },
102
+ * { name: "medium", label: "Medium Priority", condition: "priority >= 4" },
103
+ * { name: "default", label: "Default", isDefault: true }
104
+ * ];
105
+ * ```
106
+ */
107
+ export interface Branch {
108
+ /** Unique identifier for the branch (used as handle ID and for connections) */
109
+ name: string;
110
+ /** Display label shown in the UI (optional, defaults to name) */
111
+ label?: string;
112
+ /** Description of when this branch is activated */
113
+ description?: string;
114
+ /** Optional value associated with the branch (e.g., for Switch matching) */
115
+ value?: string;
116
+ /** Optional condition expression for this branch */
117
+ condition?: string;
118
+ /** Whether this is the default/fallback branch */
119
+ isDefault?: boolean;
120
+ }
90
121
  /**
91
122
  * Convert a DynamicPort to a NodePort
92
123
  * @param port - The dynamic port configuration
@@ -180,6 +211,8 @@ export interface NodeMetadata {
180
211
  inputs: NodePort[];
181
212
  outputs: NodePort[];
182
213
  configSchema?: ConfigSchema;
214
+ /** Default configuration values for this node type */
215
+ config?: Record<string, unknown>;
183
216
  tags?: string[];
184
217
  /**
185
218
  * Custom extension properties for 3rd party integrations
@@ -314,13 +347,59 @@ export type SchemaProperty<T extends SchemaType> = T extends 'config' ? ConfigPr
314
347
  export type SchemaTypeMap<T extends SchemaType> = T extends 'config' ? ConfigSchema : T extends 'input' ? InputSchema : T extends 'output' ? OutputSchema : never;
315
348
  /**
316
349
  * Node configuration values
317
- * Key-value pairs of user-entered configuration values
350
+ *
351
+ * Key-value pairs of user-entered configuration values based on the node's configSchema.
352
+ * This is where all node-specific settings are stored, including:
353
+ *
354
+ * **Standard Properties:**
355
+ * - Any property defined in the node's `configSchema` (e.g., model, temperature, apiKey)
356
+ *
357
+ * **Special Properties (Dynamic Ports):**
358
+ * - `dynamicInputs`: Array of DynamicPort for user-defined input handles
359
+ * - `dynamicOutputs`: Array of DynamicPort for user-defined output handles
360
+ * - `branches`: Array of Branch for gateway node conditional output paths
361
+ *
362
+ * The backend uses this object to:
363
+ * - Store and retrieve node configuration
364
+ * - Pass configuration values to node processors during execution
365
+ * - Persist node state across sessions
366
+ *
367
+ * @example
368
+ * ```typescript
369
+ * const config: ConfigValues = {
370
+ * // Standard configuration from configSchema
371
+ * model: "gpt-4o-mini",
372
+ * temperature: 0.7,
373
+ * maxTokens: 1000,
374
+ *
375
+ * // Dynamic input ports
376
+ * dynamicInputs: [
377
+ * { name: "extra_data", label: "Extra Data", dataType: "json" }
378
+ * ],
379
+ *
380
+ * // Gateway branches
381
+ * branches: [
382
+ * { name: "success", label: "Success", condition: "status === 200" },
383
+ * { name: "error", label: "Error", isDefault: true }
384
+ * ]
385
+ * };
386
+ * ```
318
387
  */
319
388
  export interface ConfigValues {
389
+ /** Dynamic input ports for user-defined input handles */
390
+ dynamicInputs?: DynamicPort[];
391
+ /** Dynamic output ports for user-defined output handles */
392
+ dynamicOutputs?: DynamicPort[];
393
+ /** Branches for gateway node conditional output paths */
394
+ branches?: Branch[];
395
+ /** Any other configuration properties defined in configSchema */
320
396
  [key: string]: unknown;
321
397
  }
322
398
  /**
323
399
  * Extended node type for workflows
400
+ *
401
+ * Represents a node instance in a workflow, containing position, display data,
402
+ * configuration values, and metadata from the node type definition.
324
403
  */
325
404
  export interface WorkflowNode extends Node {
326
405
  id: string;
@@ -328,11 +407,30 @@ export interface WorkflowNode extends Node {
328
407
  position: XYPosition;
329
408
  deletable?: boolean;
330
409
  data: {
410
+ /** Display label for the node instance */
331
411
  label: string;
412
+ /**
413
+ * Node configuration values
414
+ *
415
+ * Contains all user-configured settings for this node instance based on the
416
+ * node type's configSchema. This includes standard properties defined in the
417
+ * schema as well as special dynamic port configurations.
418
+ *
419
+ * The backend uses this object to:
420
+ * - Store and retrieve node configuration
421
+ * - Pass configuration values to node processors during execution
422
+ * - Persist node state across sessions
423
+ *
424
+ * @see ConfigValues for detailed documentation of available properties
425
+ */
332
426
  config: ConfigValues;
427
+ /** Node type metadata (inputs, outputs, configSchema, etc.) */
333
428
  metadata: NodeMetadata;
429
+ /** Whether the node is currently processing/executing */
334
430
  isProcessing?: boolean;
431
+ /** Error message if the node execution failed */
335
432
  error?: string;
433
+ /** Alternative node identifier */
336
434
  nodeId?: string;
337
435
  /** Node execution tracking information */
338
436
  executionInfo?: NodeExecutionInfo;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@d34dman/flowdrop",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "0.0.23",
5
+ "version": "0.0.24",
6
6
  "scripts": {
7
7
  "dev": "vite dev",
8
8
  "build": "vite build && npm run prepack",