@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.
Files changed (46) hide show
  1. package/README.md +293 -0
  2. package/dist/adapters/WorkflowAdapter.d.ts +166 -0
  3. package/dist/adapters/WorkflowAdapter.js +337 -0
  4. package/dist/api/client.d.ts +79 -0
  5. package/dist/api/client.js +208 -0
  6. package/dist/app.css +0 -0
  7. package/dist/clients/ApiClient.d.ts +203 -0
  8. package/dist/clients/ApiClient.js +212 -0
  9. package/dist/components/App.svelte +237 -0
  10. package/dist/components/App.svelte.d.ts +3 -0
  11. package/dist/components/CanvasBanner.svelte +51 -0
  12. package/dist/components/CanvasBanner.svelte.d.ts +22 -0
  13. package/dist/components/LoadingSpinner.svelte +36 -0
  14. package/dist/components/LoadingSpinner.svelte.d.ts +8 -0
  15. package/dist/components/Node.svelte +38 -0
  16. package/dist/components/Node.svelte.d.ts +4 -0
  17. package/dist/components/NodeSidebar.svelte +500 -0
  18. package/dist/components/NodeSidebar.svelte.d.ts +9 -0
  19. package/dist/components/WorkflowEditor.svelte +542 -0
  20. package/dist/components/WorkflowEditor.svelte.d.ts +10 -0
  21. package/dist/components/WorkflowNode.svelte +558 -0
  22. package/dist/components/WorkflowNode.svelte.d.ts +11 -0
  23. package/dist/data/samples.d.ts +17 -0
  24. package/dist/data/samples.js +1193 -0
  25. package/dist/examples/adapter-usage.d.ts +66 -0
  26. package/dist/examples/adapter-usage.js +138 -0
  27. package/dist/examples/api-client-usage.d.ts +31 -0
  28. package/dist/examples/api-client-usage.js +241 -0
  29. package/dist/index.d.ts +19 -0
  30. package/dist/index.js +27 -0
  31. package/dist/services/api.d.ts +110 -0
  32. package/dist/services/api.js +149 -0
  33. package/dist/services/workflowStorage.d.ts +37 -0
  34. package/dist/services/workflowStorage.js +116 -0
  35. package/dist/styles/base.css +858 -0
  36. package/dist/svelte-app.d.ts +17 -0
  37. package/dist/svelte-app.js +30 -0
  38. package/dist/types/index.d.ts +179 -0
  39. package/dist/types/index.js +4 -0
  40. package/dist/utils/colors.d.ts +121 -0
  41. package/dist/utils/colors.js +240 -0
  42. package/dist/utils/connections.d.ts +47 -0
  43. package/dist/utils/connections.js +240 -0
  44. package/dist/utils/icons.d.ts +102 -0
  45. package/dist/utils/icons.js +149 -0
  46. package/package.json +99 -0
@@ -0,0 +1,22 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: Props & {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const CanvasBanner: $$__sveltets_2_IsomorphicComponent<{
15
+ title: string;
16
+ description: string;
17
+ iconName: string | undefined;
18
+ }, {
19
+ [evt: string]: CustomEvent<any>;
20
+ }, {}, {}, string>;
21
+ type CanvasBanner = InstanceType<typeof CanvasBanner>;
22
+ export default CanvasBanner;
@@ -0,0 +1,36 @@
1
+ <!--
2
+ Loading Spinner Component
3
+ Reusable loading indicator with customizable size and text
4
+ -->
5
+
6
+ <script lang="ts">
7
+ interface Props {
8
+ size?: "xs" | "sm" | "md" | "lg";
9
+ text?: string;
10
+ showText?: boolean;
11
+ }
12
+
13
+ let props: Props = $props();
14
+
15
+ // Default values
16
+ let size = $derived(props.size || "md");
17
+ let text = $derived(props.text || "Loading...");
18
+ let showText = $derived(props.showText !== false);
19
+ </script>
20
+
21
+ <div class="flowdrop-loading">
22
+ <div class="flowdrop-spinner flowdrop-spinner--{size}"></div>
23
+ {#if showText}
24
+ <p class="flowdrop-text--xs flowdrop-text--gray">{text}</p>
25
+ {/if}
26
+ </div>
27
+
28
+ <style>
29
+ .flowdrop-loading {
30
+ display: flex;
31
+ flex-direction: column;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 0.5rem;
35
+ }
36
+ </style>
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ size?: "xs" | "sm" | "md" | "lg";
3
+ text?: string;
4
+ showText?: boolean;
5
+ }
6
+ declare const LoadingSpinner: import("svelte").Component<Props, {}, "">;
7
+ type LoadingSpinner = ReturnType<typeof LoadingSpinner>;
8
+ export default LoadingSpinner;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ // @ts-ignore
3
+ import { Handle, Position, type NodeProps } from '@xyflow/svelte';
4
+
5
+ let { positionAbsoluteX, positionAbsoluteY }: NodeProps = $props();
6
+ </script>
7
+
8
+ <div class="card">
9
+ <Handle type="target" position={Position.Right} />
10
+ <div>
11
+ Custom Node
12
+ <div>
13
+ <strong>position: {~~positionAbsoluteX},{~~positionAbsoluteY}</strong>
14
+ </div>
15
+ </div>
16
+ <Handle type="source" position={Position.Left} id="a" style="left: 10px;" />
17
+ <Handle type="source" position={Position.Left} id="b" style="left: auto; right: 10px;" />
18
+ </div>
19
+
20
+
21
+ <div class="card bg-base-100 w-96 shadow-sm">
22
+ <figure>
23
+ <img
24
+ src="https://img.daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.webp"
25
+ alt="Shoes" />
26
+ </figure>
27
+ <div class="card-body">
28
+ <h2 class="card-title">
29
+ Card Title
30
+ <div class="badge badge-secondary">NEW</div>
31
+ </h2>
32
+ <p>A card component has a figure, a body part, and inside body there are title and actions parts</p>
33
+ <div class="card-actions justify-end">
34
+ <div class="badge badge-outline">Fashion</div>
35
+ <div class="badge badge-outline">Products</div>
36
+ </div>
37
+ </div>
38
+ </div>
@@ -0,0 +1,4 @@
1
+ import { type NodeProps } from '@xyflow/svelte';
2
+ declare const Node: import("svelte").Component<NodeProps, {}, "">;
3
+ type Node = ReturnType<typeof Node>;
4
+ export default Node;
@@ -0,0 +1,500 @@
1
+ <!--
2
+ Node Sidebar Component
3
+ Displays available node types organized by category with accordion-style groups
4
+ Styled with BEM syntax
5
+ -->
6
+
7
+ <script lang="ts">
8
+ import type { NodeMetadata, NodeCategory } from "../types/index.js";
9
+ import LoadingSpinner from "./LoadingSpinner.svelte";
10
+ import Icon from "@iconify/svelte";
11
+ import { getNodeIcon, getCategoryIcon, getDefaultIcon } from "../utils/icons.js";
12
+ import { getCategoryColorToken } from "../utils/colors.js";
13
+
14
+ interface Props {
15
+ nodes: NodeMetadata[];
16
+ selectedCategory?: NodeCategory;
17
+ searchQuery?: string;
18
+ }
19
+
20
+ let props: Props = $props();
21
+ let searchInput = $state("");
22
+ let selectedCategory = $state(props.selectedCategory || "all");
23
+
24
+ let filteredNodes = $derived(getFilteredNodes());
25
+ let categories = $derived(getCategories());
26
+
27
+ // Debug logging
28
+ $effect(() => {
29
+ console.log('NodeSidebar: props.nodes received:', props.nodes?.length || 0, 'nodes');
30
+ console.log('NodeSidebar: props.nodes content:', props.nodes);
31
+ });
32
+
33
+ /**
34
+ * Get all unique categories from node types
35
+ */
36
+ function getCategories(): NodeCategory[] {
37
+ const nodes = props.nodes || [];
38
+ if (nodes.length === 0) return [];
39
+ const categories = new Set<NodeCategory>();
40
+ nodes.forEach(node => categories.add(node.category));
41
+ return Array.from(categories).sort();
42
+ }
43
+
44
+ /**
45
+ * Filter node types based on search query and selected category
46
+ */
47
+ function getFilteredNodes(): NodeMetadata[] {
48
+ // Use actual node types from props
49
+ let filtered = props.nodes || [];
50
+
51
+ // Filter by category
52
+ if (selectedCategory !== "all") {
53
+ filtered = filtered.filter(node => node.category === selectedCategory);
54
+ }
55
+
56
+ // Filter by search query
57
+ if (searchInput.trim()) {
58
+ const query = searchInput.toLowerCase();
59
+ filtered = filtered.filter(node =>
60
+ node.name.toLowerCase().includes(query) ||
61
+ node.description.toLowerCase().includes(query) ||
62
+ node.tags?.some(tag => tag.toLowerCase().includes(query))
63
+ );
64
+ }
65
+
66
+ // Create a new array and sort it to avoid mutating the original
67
+ return [...filtered].sort((a, b) => a.name.localeCompare(b.name));
68
+ }
69
+
70
+ /**
71
+ * Handle node type drag start - creates a new node instance
72
+ */
73
+ function handleNodeDragStart(event: DragEvent, nodeType: NodeMetadata): void {
74
+ if (!event.dataTransfer) return;
75
+
76
+ // Create a new node instance from the node type
77
+ const newNodeData = {
78
+ type: "node",
79
+ nodeType: nodeType.id,
80
+ nodeData: {
81
+ label: nodeType.name,
82
+ config: { ...nodeType.configSchema },
83
+ metadata: nodeType
84
+ }
85
+ };
86
+
87
+ const jsonData = JSON.stringify(newNodeData);
88
+
89
+ // Set the data that SvelteFlow will receive
90
+ event.dataTransfer.setData("application/json", jsonData);
91
+ event.dataTransfer.setData("text/plain", nodeType.name);
92
+ event.dataTransfer.effectAllowed = "copy";
93
+
94
+ // Set drag image
95
+ if (event.target) {
96
+ const rect = (event.target as HTMLElement).getBoundingClientRect();
97
+ event.dataTransfer.setDragImage(event.target as HTMLElement, rect.width / 2, rect.height / 2);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Handle category selection
103
+ */
104
+ function handleCategorySelect(category: NodeCategory | "all"): void {
105
+ selectedCategory = category;
106
+ }
107
+
108
+ /**
109
+ * Handle search input change
110
+ */
111
+ function handleSearchChange(): void {
112
+ // Search is handled reactively through the derived filteredNodes
113
+ }
114
+
115
+ /**
116
+ * Handle node type click
117
+ */
118
+ function handleNodeClick(nodeType: NodeMetadata): void {
119
+ // Node type selection is handled through events
120
+ }
121
+
122
+ /**
123
+ * Get category display name
124
+ */
125
+ function getCategoryDisplayName(category: NodeCategory): string {
126
+ const names: Record<NodeCategory, string> = {
127
+ "inputs": "Inputs",
128
+ "outputs": "Outputs",
129
+ "prompts": "Prompts",
130
+ "models": "Models",
131
+ "processing": "Processing",
132
+ "logic": "Logic",
133
+ "data": "Data",
134
+ "helpers": "Helpers",
135
+ "tools": "Tools",
136
+ "vector stores": "Vector Stores",
137
+ "embeddings": "Embeddings",
138
+ "memories": "Memories",
139
+ "agents": "Agents",
140
+ "bundles": "Bundles"
141
+ };
142
+ return names[category] || category;
143
+ }
144
+
145
+ /**
146
+ * Get node type count for category
147
+ */
148
+ function getNodeCount(category: NodeCategory): number {
149
+ const nodes = props.nodes || [];
150
+ return nodes.filter(node => node.category === category).length;
151
+ }
152
+
153
+ /**
154
+ * Get node types for category
155
+ */
156
+ function getNodesForCategory(category: NodeCategory): NodeMetadata[] {
157
+ const nodes = props.nodes || [];
158
+ return [...nodes]
159
+ .filter(node => node.category === category)
160
+ .sort((a, b) => a.name.localeCompare(b.name));
161
+ }
162
+
163
+ /**
164
+ * Get filtered nodes for category
165
+ */
166
+ function getFilteredNodesForCategory(category: NodeCategory): NodeMetadata[] {
167
+ let nodes = getNodesForCategory(category);
168
+
169
+ // Filter by search query
170
+ if (searchInput.trim()) {
171
+ const query = searchInput.toLowerCase();
172
+ nodes = nodes.filter(node =>
173
+ node.name.toLowerCase().includes(query) ||
174
+ node.description.toLowerCase().includes(query) ||
175
+ node.tags?.some(tag => tag.toLowerCase().includes(query))
176
+ );
177
+ }
178
+
179
+ return nodes;
180
+ }
181
+ </script>
182
+
183
+ <div class="flowdrop-sidebar">
184
+ <!-- Header -->
185
+ <div class="flowdrop-sidebar__header">
186
+ <div class="flowdrop-sidebar__title">
187
+ <h2 class="flowdrop-text--lg flowdrop-font--bold">Components</h2>
188
+ </div>
189
+ </div>
190
+
191
+ <!-- Search Section -->
192
+ <div class="flowdrop-sidebar__search">
193
+ <div class="flowdrop-join flowdrop-w--full">
194
+ <div class="flowdrop-join__item flowdrop-flex--1">
195
+ <input
196
+ type="text"
197
+ placeholder="Search components..."
198
+ class="flowdrop-input flowdrop-join__item flowdrop-w--full"
199
+ bind:value={searchInput}
200
+ oninput={handleSearchChange}
201
+ />
202
+ </div>
203
+ <button class="flowdrop-btn flowdrop-join__item" aria-label="Search components">
204
+ <svg class="flowdrop-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
205
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
206
+ </svg>
207
+ </button>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- Node Types List -->
212
+ <div class="flowdrop-sidebar__content">
213
+ {#if props.nodes?.length === 0}
214
+ <!-- No node types available -->
215
+ <div class="flowdrop-hero">
216
+ <div class="flowdrop-hero__content">
217
+ <div class="flowdrop-hero__icon">📦</div>
218
+ <h3 class="flowdrop-hero__title">No node types available</h3>
219
+ <p class="flowdrop-hero__description">Node type definitions will appear here</p>
220
+ <div class="flowdrop-mb--4">
221
+ <LoadingSpinner size="md" text="Loading from server..." />
222
+ </div>
223
+ </div>
224
+ </div>
225
+ {:else if searchInput.trim()}
226
+ <!-- Search Results -->
227
+ <div class="flowdrop-p--4">
228
+ <div class="flowdrop-divider">
229
+ <h3 class="flowdrop-divider__text">Search Results</h3>
230
+ </div>
231
+ {#if filteredNodes.length === 0}
232
+ <div class="flowdrop-hero">
233
+ <div class="flowdrop-hero__content">
234
+ <div class="flowdrop-hero__icon">🔍</div>
235
+ <h3 class="flowdrop-hero__title">No components found</h3>
236
+ <p class="flowdrop-hero__description">Try adjusting your search</p>
237
+ {#if props.nodes?.length === 0}
238
+ <div class="flowdrop-mb--4">
239
+ <LoadingSpinner size="sm" text="Loading components..." />
240
+ </div>
241
+ {/if}
242
+ </div>
243
+ </div>
244
+ {:else}
245
+ <div class="flowdrop-node-list">
246
+ {#each filteredNodes as nodeType (nodeType.id)}
247
+ <div
248
+ class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
249
+ draggable="true"
250
+ ondragstart={(e) => handleNodeDragStart(e, nodeType)}
251
+ onclick={() => handleNodeClick(nodeType)}
252
+ role="button"
253
+ tabindex="0"
254
+ onkeydown={(e) => {
255
+ if (e.key === "Enter" || e.key === " ") {
256
+ e.preventDefault();
257
+ handleNodeClick(nodeType);
258
+ }
259
+ }}
260
+ >
261
+ <div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
262
+ <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
263
+ <!-- Node Type Icon -->
264
+ <div class="flowdrop-node-icon" style="background-color: {getCategoryColorToken(nodeType.category)}">
265
+ <Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
266
+ </div>
267
+
268
+ <!-- Node Type Info - Icon and Title only -->
269
+ <h4 class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1">
270
+ {nodeType.name}
271
+ </h4>
272
+ </div>
273
+ <p class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0">
274
+ {nodeType.description}
275
+ </p>
276
+ </div>
277
+ </div>
278
+ {/each}
279
+ </div>
280
+ {/if}
281
+ </div>
282
+ {:else}
283
+ <!-- Show categories with details when no search is active -->
284
+ <div class="flowdrop-p--4">
285
+
286
+ <!-- Category-specific details -->
287
+ <div class="flowdrop-category-list">
288
+ {#each categories as category (category)}
289
+ {@const categoryNodes = getFilteredNodesForCategory(category)}
290
+ {#if categoryNodes.length > 0}
291
+ <details class="flowdrop-details">
292
+ <summary class="flowdrop-details__summary">
293
+ <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
294
+ <span class="flowdrop-node-icon" style="background-color: {getCategoryColorToken(category)}">
295
+ <Icon icon={getCategoryIcon(category)} />
296
+ </span>
297
+ <span>{getCategoryDisplayName(category)}</span>
298
+ </div>
299
+ <div class="flowdrop-badge flowdrop-badge--secondary">{categoryNodes.length}</div>
300
+ </summary>
301
+ <div class="flowdrop-details__content">
302
+ <div class="flowdrop-node-list">
303
+ {#each categoryNodes as nodeType (nodeType.id)}
304
+ <div
305
+ class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
306
+ draggable="true"
307
+ ondragstart={(e) => handleNodeDragStart(e, nodeType)}
308
+ onclick={() => handleNodeClick(nodeType)}
309
+ role="button"
310
+ tabindex="0"
311
+ onkeydown={(e) => {
312
+ if (e.key === "Enter" || e.key === " ") {
313
+ e.preventDefault();
314
+ handleNodeClick(nodeType);
315
+ }
316
+ }}
317
+ >
318
+ <div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
319
+ <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
320
+ <!-- Node Type Icon -->
321
+ <div class="flowdrop-node-icon" style="background-color: {getCategoryColorToken(nodeType.category)}">
322
+ <Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
323
+ </div>
324
+
325
+ <!-- Node Type Info - Icon and Title only -->
326
+ <h4 class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1">
327
+ {nodeType.name}
328
+ </h4>
329
+ </div>
330
+ <p class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0">
331
+ {nodeType.description}
332
+ </p>
333
+ </div>
334
+ </div>
335
+ {/each}
336
+ </div>
337
+ </div>
338
+ </details>
339
+ {/if}
340
+ {/each}
341
+ </div>
342
+ </div>
343
+ {/if}
344
+ </div>
345
+
346
+ <!-- Footer -->
347
+ <div class="flowdrop-sidebar__footer">
348
+ <div class="flowdrop-flex flowdrop-gap--4">
349
+ <div class="flowdrop-flex flowdrop-gap--4">
350
+ {#if props.nodes?.length === 0}
351
+ <span class="flowdrop-text--xs flowdrop-text--gray">Loading components...</span>
352
+ {:else}
353
+ <span class="flowdrop-text--xs flowdrop-text--gray">Total: {props.nodes?.length || 0} components</span>
354
+ <span class="flowdrop-text--xs flowdrop-text--gray">Showing: {filteredNodes.length}</span>
355
+ {/if}
356
+ </div>
357
+ </div>
358
+ </div>
359
+ </div>
360
+
361
+ <style>
362
+ .flowdrop-sidebar {
363
+ background-color: #f3f4f6;
364
+ border-right: 1px solid #e5e7eb;
365
+ width: 320px;
366
+ height: 100%;
367
+ display: flex;
368
+ flex-direction: column;
369
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
370
+ }
371
+
372
+ .flowdrop-sidebar__header {
373
+ background-color: #ffffff;
374
+ border-bottom: 1px solid #e5e7eb;
375
+ padding: 0.5rem 1rem;
376
+ }
377
+
378
+ .flowdrop-sidebar__title {
379
+ display: flex;
380
+ align-items: center;
381
+ }
382
+
383
+ .flowdrop-sidebar__title h2 {
384
+ font-size: 1rem;
385
+ font-weight: 600;
386
+ margin: 0;
387
+ }
388
+
389
+ .flowdrop-sidebar__search {
390
+ padding: 0.75rem 1rem;
391
+ background-color: #ffffff;
392
+ border-bottom: 1px solid #e5e7eb;
393
+ }
394
+
395
+ .flowdrop-sidebar__content {
396
+ flex: 1;
397
+ overflow-y: auto;
398
+ scrollbar-width: thin;
399
+ scrollbar-color: #d1d5db #f3f4f6;
400
+ }
401
+
402
+ .flowdrop-sidebar__content::-webkit-scrollbar {
403
+ width: 8px;
404
+ }
405
+
406
+ .flowdrop-sidebar__content::-webkit-scrollbar-track {
407
+ background: #f3f4f6;
408
+ }
409
+
410
+ .flowdrop-sidebar__content::-webkit-scrollbar-thumb {
411
+ background: #d1d5db;
412
+ border-radius: 4px;
413
+ }
414
+
415
+ .flowdrop-sidebar__content::-webkit-scrollbar-thumb:hover {
416
+ background: #9ca3af;
417
+ }
418
+
419
+ .flowdrop-sidebar__footer {
420
+ background-color: rgba(255, 255, 255, 0.8);
421
+ backdrop-filter: blur(8px);
422
+ border-top: 1px solid #e5e7eb;
423
+ padding: 0.5rem 0.75rem;
424
+ }
425
+
426
+ .flowdrop-node-list {
427
+ display: flex;
428
+ flex-direction: column;
429
+ gap: 0.375rem;
430
+ }
431
+
432
+ .flowdrop-node-item {
433
+ cursor: grab;
434
+ transition: all 0.2s ease-in-out;
435
+ border-radius: 0.375rem;
436
+ border: 1px solid transparent;
437
+ }
438
+
439
+ .flowdrop-node-item:hover {
440
+ border-color: #d1d5db;
441
+ background-color: #f9fafb;
442
+ }
443
+
444
+ .flowdrop-node-item:active {
445
+ cursor: grabbing;
446
+ }
447
+
448
+ .flowdrop-node-icon {
449
+ width: 2rem;
450
+ height: 2rem;
451
+ border-radius: 0.375rem;
452
+ color: #ffffff;
453
+ font-size: 0.875rem;
454
+ display: flex;
455
+ align-items: center;
456
+ justify-content: center;
457
+ flex-shrink: 0;
458
+ }
459
+
460
+ .flowdrop-category-list {
461
+ display: flex;
462
+ flex-direction: column;
463
+ gap: 0.375rem;
464
+ }
465
+
466
+ .flowdrop-icon {
467
+ width: 1rem;
468
+ height: 1rem;
469
+ }
470
+
471
+ .flowdrop-items--center {
472
+ align-items: center;
473
+ }
474
+
475
+
476
+ .flowdrop-truncate {
477
+ overflow: hidden;
478
+ text-overflow: ellipsis;
479
+ white-space: nowrap;
480
+ }
481
+
482
+ .flowdrop-card__body h4 {
483
+ margin: 0;
484
+ line-height: 1;
485
+ }
486
+
487
+ .flowdrop-p--4 {
488
+ padding: 1rem;
489
+ }
490
+
491
+ .flowdrop-py--1 {
492
+ padding-top: 0.25rem;
493
+ padding-bottom: 0.25rem;
494
+ }
495
+
496
+ .flowdrop-ml--0 {
497
+ margin-left: 0;
498
+ }
499
+
500
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { NodeMetadata, NodeCategory } from "../types/index.js";
2
+ interface Props {
3
+ nodes: NodeMetadata[];
4
+ selectedCategory?: NodeCategory;
5
+ searchQuery?: string;
6
+ }
7
+ declare const NodeSidebar: import("svelte").Component<Props, {}, "">;
8
+ type NodeSidebar = ReturnType<typeof NodeSidebar>;
9
+ export default NodeSidebar;