@d34dman/flowdrop 0.0.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.
- package/README.md +293 -0
- package/dist/adapters/WorkflowAdapter.d.ts +166 -0
- package/dist/adapters/WorkflowAdapter.js +337 -0
- package/dist/api/client.d.ts +79 -0
- package/dist/api/client.js +208 -0
- package/dist/app.css +0 -0
- package/dist/clients/ApiClient.d.ts +203 -0
- package/dist/clients/ApiClient.js +212 -0
- package/dist/components/App.svelte +237 -0
- package/dist/components/App.svelte.d.ts +3 -0
- package/dist/components/CanvasBanner.svelte +51 -0
- package/dist/components/CanvasBanner.svelte.d.ts +22 -0
- package/dist/components/LoadingSpinner.svelte +36 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +8 -0
- package/dist/components/Node.svelte +38 -0
- package/dist/components/Node.svelte.d.ts +4 -0
- package/dist/components/NodeSidebar.svelte +500 -0
- package/dist/components/NodeSidebar.svelte.d.ts +9 -0
- package/dist/components/WorkflowEditor.svelte +542 -0
- package/dist/components/WorkflowEditor.svelte.d.ts +10 -0
- package/dist/components/WorkflowNode.svelte +558 -0
- package/dist/components/WorkflowNode.svelte.d.ts +11 -0
- package/dist/data/samples.d.ts +17 -0
- package/dist/data/samples.js +1193 -0
- package/dist/examples/adapter-usage.d.ts +66 -0
- package/dist/examples/adapter-usage.js +138 -0
- package/dist/examples/api-client-usage.d.ts +31 -0
- package/dist/examples/api-client-usage.js +241 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +27 -0
- package/dist/services/api.d.ts +110 -0
- package/dist/services/api.js +149 -0
- package/dist/services/workflowStorage.d.ts +37 -0
- package/dist/services/workflowStorage.js +116 -0
- package/dist/styles/base.css +858 -0
- package/dist/svelte-app.d.ts +17 -0
- package/dist/svelte-app.js +30 -0
- package/dist/types/index.d.ts +179 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/colors.d.ts +121 -0
- package/dist/utils/colors.js +240 -0
- package/dist/utils/connections.d.ts +47 -0
- package/dist/utils/connections.js +240 -0
- package/dist/utils/icons.d.ts +102 -0
- package/dist/utils/icons.js +149 -0
- package/package.json +99 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection validation utilities for FlowDrop
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Check if two data types are compatible for connection
|
|
6
|
+
*/
|
|
7
|
+
export function areDataTypesCompatible(outputType, inputType) {
|
|
8
|
+
// Direct type matches
|
|
9
|
+
if (outputType === inputType)
|
|
10
|
+
return true;
|
|
11
|
+
// String can be converted to most types
|
|
12
|
+
if (outputType === "string") {
|
|
13
|
+
return ["string", "number", "boolean"].includes(inputType);
|
|
14
|
+
}
|
|
15
|
+
// Number can be converted to string
|
|
16
|
+
if (outputType === "number") {
|
|
17
|
+
return ["string", "number"].includes(inputType);
|
|
18
|
+
}
|
|
19
|
+
// Boolean can be converted to string
|
|
20
|
+
if (outputType === "boolean") {
|
|
21
|
+
return ["string", "boolean"].includes(inputType);
|
|
22
|
+
}
|
|
23
|
+
// Object can be converted to string (JSON)
|
|
24
|
+
if (outputType === "object") {
|
|
25
|
+
return ["string", "object"].includes(inputType);
|
|
26
|
+
}
|
|
27
|
+
// Array can be converted to string (JSON)
|
|
28
|
+
if (outputType === "array") {
|
|
29
|
+
return ["string", "array"].includes(inputType);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get all possible connections from a source node to target nodes
|
|
35
|
+
*/
|
|
36
|
+
export function getPossibleConnections(sourceNode, targetNodes, nodeTypes) {
|
|
37
|
+
const sourceMetadata = nodeTypes.find(nt => nt.id === sourceNode.data.metadata.id);
|
|
38
|
+
if (!sourceMetadata)
|
|
39
|
+
return [];
|
|
40
|
+
const possibleConnections = [];
|
|
41
|
+
// Get all output ports from source node
|
|
42
|
+
const sourceOutputs = sourceMetadata.outputs;
|
|
43
|
+
// Check each target node
|
|
44
|
+
for (const targetNode of targetNodes) {
|
|
45
|
+
if (targetNode.id === sourceNode.id)
|
|
46
|
+
continue; // Skip self-connection
|
|
47
|
+
const targetMetadata = nodeTypes.find(nt => nt.id === targetNode.data.metadata.id);
|
|
48
|
+
if (!targetMetadata)
|
|
49
|
+
continue;
|
|
50
|
+
// Get all input ports from target node
|
|
51
|
+
const targetInputs = targetMetadata.inputs;
|
|
52
|
+
// Check each output-input combination
|
|
53
|
+
for (const sourcePort of sourceOutputs) {
|
|
54
|
+
for (const targetPort of targetInputs) {
|
|
55
|
+
const compatible = areDataTypesCompatible(sourcePort.dataType, targetPort.dataType);
|
|
56
|
+
possibleConnections.push({
|
|
57
|
+
sourceNodeId: sourceNode.id,
|
|
58
|
+
sourcePortId: sourcePort.id,
|
|
59
|
+
sourcePort,
|
|
60
|
+
targetNodeId: targetNode.id,
|
|
61
|
+
targetPortId: targetPort.id,
|
|
62
|
+
targetPort,
|
|
63
|
+
compatible
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return possibleConnections;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validate if a specific connection is valid
|
|
72
|
+
*/
|
|
73
|
+
export function validateConnection(sourceNodeId, sourcePortId, targetNodeId, targetPortId, nodes, nodeTypes) {
|
|
74
|
+
// Check if nodes exist
|
|
75
|
+
const sourceNode = nodes.find(n => n.id === sourceNodeId);
|
|
76
|
+
const targetNode = nodes.find(n => n.id === targetNodeId);
|
|
77
|
+
if (!sourceNode) {
|
|
78
|
+
return { valid: false, error: "Source node not found" };
|
|
79
|
+
}
|
|
80
|
+
if (!targetNode) {
|
|
81
|
+
return { valid: false, error: "Target node not found" };
|
|
82
|
+
}
|
|
83
|
+
// Check for self-connection
|
|
84
|
+
if (sourceNodeId === targetNodeId) {
|
|
85
|
+
return { valid: false, error: "Cannot connect node to itself" };
|
|
86
|
+
}
|
|
87
|
+
// Get node metadata
|
|
88
|
+
const sourceMetadata = nodeTypes.find(nt => nt.id === sourceNode.data.metadata.id);
|
|
89
|
+
const targetMetadata = nodeTypes.find(nt => nt.id === targetNode.data.metadata.id);
|
|
90
|
+
if (!sourceMetadata || !targetMetadata) {
|
|
91
|
+
console.log("Metadata lookup failed:", {
|
|
92
|
+
sourceNodeId,
|
|
93
|
+
targetNodeId,
|
|
94
|
+
sourceMetadataId: sourceNode.data.metadata.id,
|
|
95
|
+
targetMetadataId: targetNode.data.metadata.id,
|
|
96
|
+
availableNodeTypes: nodeTypes.map(nt => nt.id)
|
|
97
|
+
});
|
|
98
|
+
return { valid: false, error: "Node metadata not found" };
|
|
99
|
+
}
|
|
100
|
+
// Find ports
|
|
101
|
+
const sourcePort = sourceMetadata.outputs.find(p => p.id === sourcePortId);
|
|
102
|
+
const targetPort = targetMetadata.inputs.find(p => p.id === targetPortId);
|
|
103
|
+
if (!sourcePort) {
|
|
104
|
+
return { valid: false, error: "Source port not found" };
|
|
105
|
+
}
|
|
106
|
+
if (!targetPort) {
|
|
107
|
+
return { valid: false, error: "Target port not found" };
|
|
108
|
+
}
|
|
109
|
+
// Check data type compatibility
|
|
110
|
+
if (!areDataTypesCompatible(sourcePort.dataType, targetPort.dataType)) {
|
|
111
|
+
return {
|
|
112
|
+
valid: false,
|
|
113
|
+
error: `Incompatible data types: ${sourcePort.dataType} cannot connect to ${targetPort.dataType}`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return { valid: true };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get connection suggestions for a node
|
|
120
|
+
*/
|
|
121
|
+
export function getConnectionSuggestions(nodeId, nodes, nodeTypes) {
|
|
122
|
+
const node = nodes.find(n => n.id === nodeId);
|
|
123
|
+
if (!node)
|
|
124
|
+
return [];
|
|
125
|
+
const metadata = nodeTypes.find(nt => nt.id === node.data.metadata.id);
|
|
126
|
+
if (!metadata)
|
|
127
|
+
return [];
|
|
128
|
+
const suggestions = [];
|
|
129
|
+
// Get all other nodes
|
|
130
|
+
const otherNodes = nodes.filter(n => n.id !== nodeId);
|
|
131
|
+
for (const otherNode of otherNodes) {
|
|
132
|
+
const otherMetadata = nodeTypes.find(nt => nt.id === otherNode.data.metadata.id);
|
|
133
|
+
if (!otherMetadata)
|
|
134
|
+
continue;
|
|
135
|
+
// Check outputs from other nodes to inputs of current node
|
|
136
|
+
for (const output of otherMetadata.outputs) {
|
|
137
|
+
for (const input of metadata.inputs) {
|
|
138
|
+
const compatible = areDataTypesCompatible(output.dataType, input.dataType);
|
|
139
|
+
suggestions.push({
|
|
140
|
+
nodeId: otherNode.id,
|
|
141
|
+
nodeName: otherNode.data.label,
|
|
142
|
+
portId: output.id,
|
|
143
|
+
portName: output.name,
|
|
144
|
+
portType: "output",
|
|
145
|
+
dataType: output.dataType,
|
|
146
|
+
compatible
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Check outputs from current node to inputs of other nodes
|
|
151
|
+
for (const output of metadata.outputs) {
|
|
152
|
+
for (const input of otherMetadata.inputs) {
|
|
153
|
+
const compatible = areDataTypesCompatible(output.dataType, input.dataType);
|
|
154
|
+
suggestions.push({
|
|
155
|
+
nodeId: otherNode.id,
|
|
156
|
+
nodeName: otherNode.data.label,
|
|
157
|
+
portId: input.id,
|
|
158
|
+
portName: input.name,
|
|
159
|
+
portType: "input",
|
|
160
|
+
dataType: input.dataType,
|
|
161
|
+
compatible
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return suggestions;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if a workflow has any cycles (prevent infinite loops)
|
|
170
|
+
*/
|
|
171
|
+
export function hasCycles(nodes, edges) {
|
|
172
|
+
const visited = new Set();
|
|
173
|
+
const recursionStack = new Set();
|
|
174
|
+
function hasCycleUtil(nodeId) {
|
|
175
|
+
if (recursionStack.has(nodeId))
|
|
176
|
+
return true;
|
|
177
|
+
if (visited.has(nodeId))
|
|
178
|
+
return false;
|
|
179
|
+
visited.add(nodeId);
|
|
180
|
+
recursionStack.add(nodeId);
|
|
181
|
+
// Get all outgoing edges from this node
|
|
182
|
+
const outgoingEdges = edges.filter(e => e.source === nodeId);
|
|
183
|
+
for (const edge of outgoingEdges) {
|
|
184
|
+
if (hasCycleUtil(edge.target))
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
recursionStack.delete(nodeId);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
// Check each node
|
|
191
|
+
for (const node of nodes) {
|
|
192
|
+
if (!visited.has(node.id)) {
|
|
193
|
+
if (hasCycleUtil(node.id))
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get the execution order for a workflow (topological sort)
|
|
201
|
+
*/
|
|
202
|
+
export function getExecutionOrder(nodes, edges) {
|
|
203
|
+
const inDegree = new Map();
|
|
204
|
+
const graph = new Map();
|
|
205
|
+
// Initialize
|
|
206
|
+
for (const node of nodes) {
|
|
207
|
+
inDegree.set(node.id, 0);
|
|
208
|
+
graph.set(node.id, []);
|
|
209
|
+
}
|
|
210
|
+
// Build graph and calculate in-degrees
|
|
211
|
+
for (const edge of edges) {
|
|
212
|
+
const current = inDegree.get(edge.target) || 0;
|
|
213
|
+
inDegree.set(edge.target, current + 1);
|
|
214
|
+
const neighbors = graph.get(edge.source) || [];
|
|
215
|
+
neighbors.push(edge.target);
|
|
216
|
+
graph.set(edge.source, neighbors);
|
|
217
|
+
}
|
|
218
|
+
// Topological sort
|
|
219
|
+
const queue = [];
|
|
220
|
+
const result = [];
|
|
221
|
+
// Add nodes with no incoming edges
|
|
222
|
+
for (const [nodeId, degree] of inDegree) {
|
|
223
|
+
if (degree === 0) {
|
|
224
|
+
queue.push(nodeId);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
while (queue.length > 0) {
|
|
228
|
+
const nodeId = queue.shift();
|
|
229
|
+
result.push(nodeId);
|
|
230
|
+
const neighbors = graph.get(nodeId) || [];
|
|
231
|
+
for (const neighbor of neighbors) {
|
|
232
|
+
const degree = inDegree.get(neighbor) - 1;
|
|
233
|
+
inDegree.set(neighbor, degree);
|
|
234
|
+
if (degree === 0) {
|
|
235
|
+
queue.push(neighbor);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized icon management for FlowDrop
|
|
3
|
+
* Ensures consistent icon usage across all components
|
|
4
|
+
*/
|
|
5
|
+
import type { NodeCategory } from "../types/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Default icons for different contexts
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_ICONS: {
|
|
10
|
+
readonly NODE: "mdi:cube";
|
|
11
|
+
readonly CATEGORY: "mdi:folder";
|
|
12
|
+
readonly ADD: "mdi:plus";
|
|
13
|
+
readonly REMOVE: "mdi:minus";
|
|
14
|
+
readonly EDIT: "mdi:pencil";
|
|
15
|
+
readonly SAVE: "mdi:content-save";
|
|
16
|
+
readonly LOAD: "mdi:folder-open";
|
|
17
|
+
readonly EXPORT: "mdi:download";
|
|
18
|
+
readonly IMPORT: "mdi:upload";
|
|
19
|
+
readonly SEARCH: "mdi:magnify";
|
|
20
|
+
readonly CLOSE: "mdi:close";
|
|
21
|
+
readonly SETTINGS: "mdi:cog";
|
|
22
|
+
readonly HELP: "mdi:help-circle";
|
|
23
|
+
readonly SUCCESS: "mdi:check-circle";
|
|
24
|
+
readonly ERROR: "mdi:alert-circle";
|
|
25
|
+
readonly WARNING: "mdi:alert";
|
|
26
|
+
readonly INFO: "mdi:information";
|
|
27
|
+
readonly LOADING: "mdi:loading";
|
|
28
|
+
readonly HOME: "mdi:home";
|
|
29
|
+
readonly BACK: "mdi:arrow-left";
|
|
30
|
+
readonly FORWARD: "mdi:arrow-right";
|
|
31
|
+
readonly UP: "mdi:arrow-up";
|
|
32
|
+
readonly DOWN: "mdi:arrow-down";
|
|
33
|
+
readonly WORKFLOW: "mdi:graph";
|
|
34
|
+
readonly NODE_ADD: "mdi:plus-circle";
|
|
35
|
+
readonly NODE_DELETE: "mdi:minus-circle";
|
|
36
|
+
readonly CONNECTION: "mdi:connection";
|
|
37
|
+
readonly DATA: "mdi:database";
|
|
38
|
+
readonly FILE: "mdi:file";
|
|
39
|
+
readonly TEXT: "mdi:text";
|
|
40
|
+
readonly JSON: "mdi:code-json";
|
|
41
|
+
readonly MODEL: "mdi:chip";
|
|
42
|
+
readonly BRAIN: "mdi:brain";
|
|
43
|
+
readonly VECTOR: "mdi:vector-point";
|
|
44
|
+
readonly ROBOT: "mdi:robot";
|
|
45
|
+
readonly TOOL: "mdi:wrench";
|
|
46
|
+
readonly CALCULATOR: "mdi:calculator";
|
|
47
|
+
readonly CLOCK: "mdi:clock";
|
|
48
|
+
readonly CHAT: "mdi:chat";
|
|
49
|
+
readonly MESSAGE: "mdi:message";
|
|
50
|
+
readonly EMAIL: "mdi:email";
|
|
51
|
+
readonly WEBHOOK: "mdi:webhook";
|
|
52
|
+
readonly STORAGE: "mdi:database";
|
|
53
|
+
readonly MEMORY: "mdi:memory";
|
|
54
|
+
readonly CACHE: "mdi:cached";
|
|
55
|
+
readonly PROCESS: "mdi:cog";
|
|
56
|
+
readonly FILTER: "mdi:filter";
|
|
57
|
+
readonly SORT: "mdi:sort";
|
|
58
|
+
readonly TRANSFORM: "mdi:transform";
|
|
59
|
+
readonly LOGIC: "mdi:git-branch";
|
|
60
|
+
readonly CONDITION: "mdi:source-fork";
|
|
61
|
+
readonly LOOP: "mdi:loop";
|
|
62
|
+
readonly API: "mdi:api";
|
|
63
|
+
readonly LINK: "mdi:link";
|
|
64
|
+
readonly PLUGIN: "mdi:puzzle";
|
|
65
|
+
readonly BUNDLE: "mdi:package-variant";
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Category-specific icons matching Langflow's visual style
|
|
69
|
+
*/
|
|
70
|
+
export declare const CATEGORY_ICONS: Record<NodeCategory, string>;
|
|
71
|
+
/**
|
|
72
|
+
* Get the appropriate icon for a node
|
|
73
|
+
* @param nodeIcon - The node's specific icon
|
|
74
|
+
* @param category - The node's category
|
|
75
|
+
* @returns The icon to use
|
|
76
|
+
*/
|
|
77
|
+
export declare function getNodeIcon(nodeIcon?: string, category?: NodeCategory): string;
|
|
78
|
+
/**
|
|
79
|
+
* Get the appropriate icon for a category
|
|
80
|
+
* @param category - The category
|
|
81
|
+
* @returns The icon to use
|
|
82
|
+
*/
|
|
83
|
+
export declare function getCategoryIcon(category: NodeCategory): string;
|
|
84
|
+
/**
|
|
85
|
+
* Get a default icon by key
|
|
86
|
+
* @param key - The icon key from DEFAULT_ICONS
|
|
87
|
+
* @returns The icon string
|
|
88
|
+
*/
|
|
89
|
+
export declare function getDefaultIcon(key: keyof typeof DEFAULT_ICONS): string;
|
|
90
|
+
/**
|
|
91
|
+
* Validate if an icon string is properly formatted
|
|
92
|
+
* @param icon - The icon string to validate
|
|
93
|
+
* @returns True if valid, false otherwise
|
|
94
|
+
*/
|
|
95
|
+
export declare function isValidIcon(icon: string): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Get a fallback icon if the provided icon is invalid
|
|
98
|
+
* @param icon - The icon to check
|
|
99
|
+
* @param fallback - The fallback icon to use
|
|
100
|
+
* @returns The valid icon string
|
|
101
|
+
*/
|
|
102
|
+
export declare function getValidIcon(icon: string, fallback?: string): string;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized icon management for FlowDrop
|
|
3
|
+
* Ensures consistent icon usage across all components
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Default icons for different contexts
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_ICONS = {
|
|
9
|
+
// Node fallback icons
|
|
10
|
+
NODE: "mdi:cube",
|
|
11
|
+
CATEGORY: "mdi:folder",
|
|
12
|
+
// UI action icons
|
|
13
|
+
ADD: "mdi:plus",
|
|
14
|
+
REMOVE: "mdi:minus",
|
|
15
|
+
EDIT: "mdi:pencil",
|
|
16
|
+
SAVE: "mdi:content-save",
|
|
17
|
+
LOAD: "mdi:folder-open",
|
|
18
|
+
EXPORT: "mdi:download",
|
|
19
|
+
IMPORT: "mdi:upload",
|
|
20
|
+
SEARCH: "mdi:magnify",
|
|
21
|
+
CLOSE: "mdi:close",
|
|
22
|
+
SETTINGS: "mdi:cog",
|
|
23
|
+
HELP: "mdi:help-circle",
|
|
24
|
+
// Status icons
|
|
25
|
+
SUCCESS: "mdi:check-circle",
|
|
26
|
+
ERROR: "mdi:alert-circle",
|
|
27
|
+
WARNING: "mdi:alert",
|
|
28
|
+
INFO: "mdi:information",
|
|
29
|
+
LOADING: "mdi:loading",
|
|
30
|
+
// Navigation icons
|
|
31
|
+
HOME: "mdi:home",
|
|
32
|
+
BACK: "mdi:arrow-left",
|
|
33
|
+
FORWARD: "mdi:arrow-right",
|
|
34
|
+
UP: "mdi:arrow-up",
|
|
35
|
+
DOWN: "mdi:arrow-down",
|
|
36
|
+
// Workflow icons
|
|
37
|
+
WORKFLOW: "mdi:graph",
|
|
38
|
+
NODE_ADD: "mdi:plus-circle",
|
|
39
|
+
NODE_DELETE: "mdi:minus-circle",
|
|
40
|
+
CONNECTION: "mdi:connection",
|
|
41
|
+
// Data icons
|
|
42
|
+
DATA: "mdi:database",
|
|
43
|
+
FILE: "mdi:file",
|
|
44
|
+
TEXT: "mdi:text",
|
|
45
|
+
JSON: "mdi:code-json",
|
|
46
|
+
// Model and processing icons
|
|
47
|
+
MODEL: "mdi:chip",
|
|
48
|
+
BRAIN: "mdi:brain",
|
|
49
|
+
VECTOR: "mdi:vector-point",
|
|
50
|
+
ROBOT: "mdi:robot",
|
|
51
|
+
// Tool icons
|
|
52
|
+
TOOL: "mdi:wrench",
|
|
53
|
+
CALCULATOR: "mdi:calculator",
|
|
54
|
+
CLOCK: "mdi:clock",
|
|
55
|
+
// Communication icons
|
|
56
|
+
CHAT: "mdi:chat",
|
|
57
|
+
MESSAGE: "mdi:message",
|
|
58
|
+
EMAIL: "mdi:email",
|
|
59
|
+
WEBHOOK: "mdi:webhook",
|
|
60
|
+
// Storage icons
|
|
61
|
+
STORAGE: "mdi:database",
|
|
62
|
+
MEMORY: "mdi:memory",
|
|
63
|
+
CACHE: "mdi:cached",
|
|
64
|
+
// Processing icons
|
|
65
|
+
PROCESS: "mdi:cog",
|
|
66
|
+
FILTER: "mdi:filter",
|
|
67
|
+
SORT: "mdi:sort",
|
|
68
|
+
TRANSFORM: "mdi:transform",
|
|
69
|
+
// Logic icons
|
|
70
|
+
LOGIC: "mdi:git-branch",
|
|
71
|
+
CONDITION: "mdi:source-fork",
|
|
72
|
+
LOOP: "mdi:loop",
|
|
73
|
+
// Integration icons
|
|
74
|
+
API: "mdi:api",
|
|
75
|
+
LINK: "mdi:link",
|
|
76
|
+
PLUGIN: "mdi:puzzle",
|
|
77
|
+
BUNDLE: "mdi:package-variant"
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Category-specific icons matching Langflow's visual style
|
|
81
|
+
*/
|
|
82
|
+
export const CATEGORY_ICONS = {
|
|
83
|
+
"inputs": "mdi:arrow-down-circle",
|
|
84
|
+
"outputs": "mdi:arrow-up-circle",
|
|
85
|
+
"prompts": "mdi:message-text",
|
|
86
|
+
"models": "mdi:robot",
|
|
87
|
+
"processing": "mdi:cog",
|
|
88
|
+
"logic": "mdi:git-branch",
|
|
89
|
+
"data": "mdi:database",
|
|
90
|
+
"helpers": "mdi:help-circle",
|
|
91
|
+
"tools": "mdi:wrench",
|
|
92
|
+
"vector stores": "mdi:vector-square",
|
|
93
|
+
"embeddings": "mdi:vector-point",
|
|
94
|
+
"memories": "mdi:brain",
|
|
95
|
+
"agents": "mdi:account-cog",
|
|
96
|
+
"bundles": "mdi:package-variant"
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Get the appropriate icon for a node
|
|
100
|
+
* @param nodeIcon - The node's specific icon
|
|
101
|
+
* @param category - The node's category
|
|
102
|
+
* @returns The icon to use
|
|
103
|
+
*/
|
|
104
|
+
export function getNodeIcon(nodeIcon, category) {
|
|
105
|
+
// If node has a specific icon, use it
|
|
106
|
+
if (nodeIcon) {
|
|
107
|
+
return nodeIcon;
|
|
108
|
+
}
|
|
109
|
+
// If category is provided, use category icon
|
|
110
|
+
if (category && CATEGORY_ICONS[category]) {
|
|
111
|
+
return CATEGORY_ICONS[category];
|
|
112
|
+
}
|
|
113
|
+
// Fallback to default node icon
|
|
114
|
+
return DEFAULT_ICONS.NODE;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get the appropriate icon for a category
|
|
118
|
+
* @param category - The category
|
|
119
|
+
* @returns The icon to use
|
|
120
|
+
*/
|
|
121
|
+
export function getCategoryIcon(category) {
|
|
122
|
+
return CATEGORY_ICONS[category] || DEFAULT_ICONS.CATEGORY;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get a default icon by key
|
|
126
|
+
* @param key - The icon key from DEFAULT_ICONS
|
|
127
|
+
* @returns The icon string
|
|
128
|
+
*/
|
|
129
|
+
export function getDefaultIcon(key) {
|
|
130
|
+
return DEFAULT_ICONS[key];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validate if an icon string is properly formatted
|
|
134
|
+
* @param icon - The icon string to validate
|
|
135
|
+
* @returns True if valid, false otherwise
|
|
136
|
+
*/
|
|
137
|
+
export function isValidIcon(icon) {
|
|
138
|
+
// Check if it's a valid iconify format (e.g., "mdi:icon-name")
|
|
139
|
+
return /^[a-z-]+:[a-z-]+$/.test(icon);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get a fallback icon if the provided icon is invalid
|
|
143
|
+
* @param icon - The icon to check
|
|
144
|
+
* @param fallback - The fallback icon to use
|
|
145
|
+
* @returns The valid icon string
|
|
146
|
+
*/
|
|
147
|
+
export function getValidIcon(icon, fallback = DEFAULT_ICONS.NODE) {
|
|
148
|
+
return isValidIcon(icon) ? icon : fallback;
|
|
149
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@d34dman/flowdrop",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "vite dev",
|
|
6
|
+
"build": "vite build && npm run prepack",
|
|
7
|
+
"watch:build": "npm-watch build",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
10
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
13
|
+
"lint": "eslint . && prettier --check .",
|
|
14
|
+
"test:unit": "vitest",
|
|
15
|
+
"test": "npm run test:unit -- --run && npm run test:e2e",
|
|
16
|
+
"test:e2e": "playwright test",
|
|
17
|
+
"format": "prettier --write .",
|
|
18
|
+
"storybook": "storybook dev -p 6006",
|
|
19
|
+
"build-storybook": "storybook build"
|
|
20
|
+
},
|
|
21
|
+
"watch": {
|
|
22
|
+
"build": {
|
|
23
|
+
"ignore": "build",
|
|
24
|
+
"patterns": [
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"extensions": "js,ts,svelte,html,css,svg",
|
|
28
|
+
"quiet": true,
|
|
29
|
+
"legacyWatch": true,
|
|
30
|
+
"delay": 2500,
|
|
31
|
+
"runOnChangeOnly": false
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"!dist/**/*.test.*",
|
|
37
|
+
"!dist/**/*.spec.*"
|
|
38
|
+
],
|
|
39
|
+
"sideEffects": [
|
|
40
|
+
"**/*.css"
|
|
41
|
+
],
|
|
42
|
+
"svelte": "./dist/index.js",
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"type": "module",
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"types": "./dist/index.d.ts",
|
|
48
|
+
"svelte": "./dist/index.js"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"svelte": "^5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@chromatic-com/storybook": "^4.0.1",
|
|
56
|
+
"@eslint/compat": "^1.2.5",
|
|
57
|
+
"@eslint/js": "^9.18.0",
|
|
58
|
+
"@iconify/svelte": "^5.0.0",
|
|
59
|
+
"@playwright/test": "^1.49.1",
|
|
60
|
+
"@storybook/addon-a11y": "^9.0.15",
|
|
61
|
+
"@storybook/addon-docs": "^9.0.15",
|
|
62
|
+
"@storybook/addon-svelte-csf": "^5.0.4",
|
|
63
|
+
"@storybook/addon-vitest": "^9.0.15",
|
|
64
|
+
"@storybook/sveltekit": "^9.0.15",
|
|
65
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
66
|
+
"@sveltejs/kit": "^2.16.0",
|
|
67
|
+
"@sveltejs/package": "^2.0.0",
|
|
68
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
69
|
+
"@types/node": "^20",
|
|
70
|
+
"@vitest/browser": "^3.2.3",
|
|
71
|
+
"eslint": "^9.18.0",
|
|
72
|
+
"eslint-config-prettier": "^10.0.1",
|
|
73
|
+
"eslint-plugin-storybook": "^9.0.15",
|
|
74
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
75
|
+
"globals": "^16.0.0",
|
|
76
|
+
"playwright": "^1.53.0",
|
|
77
|
+
"prettier": "^3.4.2",
|
|
78
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
79
|
+
"publint": "^0.3.2",
|
|
80
|
+
"storybook": "^9.0.15",
|
|
81
|
+
"svelte": "^5.0.0",
|
|
82
|
+
"svelte-check": "^4.0.0",
|
|
83
|
+
"terser": "^5.43.1",
|
|
84
|
+
"typescript": "^5.0.0",
|
|
85
|
+
"typescript-eslint": "^8.20.0",
|
|
86
|
+
"vite": "^6.2.6",
|
|
87
|
+
"vite-plugin-devtools-json": "^0.2.1",
|
|
88
|
+
"vitest": "^3.2.3",
|
|
89
|
+
"vitest-browser-svelte": "^0.1.0"
|
|
90
|
+
},
|
|
91
|
+
"keywords": [
|
|
92
|
+
"svelte"
|
|
93
|
+
],
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"@types/uuid": "^10.0.0",
|
|
96
|
+
"@xyflow/svelte": "~1.2",
|
|
97
|
+
"uuid": "^11.1.0"
|
|
98
|
+
}
|
|
99
|
+
}
|