@flowdrop/flowdrop 1.4.0 → 1.5.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 (100) hide show
  1. package/README.md +68 -24
  2. package/dist/adapters/WorkflowAdapter.js +2 -22
  3. package/dist/adapters/agentspec/autoLayout.d.ts +51 -5
  4. package/dist/adapters/agentspec/autoLayout.js +120 -23
  5. package/dist/chat/commandClassifier.d.ts +19 -0
  6. package/dist/chat/commandClassifier.js +30 -0
  7. package/dist/chat/index.d.ts +27 -0
  8. package/dist/chat/index.js +32 -0
  9. package/dist/chat/responseParser.d.ts +21 -0
  10. package/dist/chat/responseParser.js +87 -0
  11. package/dist/commands/batch.d.ts +18 -0
  12. package/dist/commands/batch.js +56 -0
  13. package/dist/commands/executor.d.ts +37 -0
  14. package/dist/commands/executor.js +1044 -0
  15. package/dist/commands/index.d.ts +14 -0
  16. package/dist/commands/index.js +17 -0
  17. package/dist/commands/parser.d.ts +16 -0
  18. package/dist/commands/parser.js +278 -0
  19. package/dist/commands/positioner.d.ts +19 -0
  20. package/dist/commands/positioner.js +33 -0
  21. package/dist/commands/storeIntegration.svelte.d.ts +16 -0
  22. package/dist/commands/storeIntegration.svelte.js +67 -0
  23. package/dist/commands/types.d.ts +343 -0
  24. package/dist/commands/types.js +45 -0
  25. package/dist/components/App.svelte +351 -12
  26. package/dist/components/App.svelte.d.ts +3 -0
  27. package/dist/components/CanvasController.svelte +38 -0
  28. package/dist/components/CanvasController.svelte.d.ts +32 -0
  29. package/dist/components/ConfigMappingRow.svelte +130 -0
  30. package/dist/components/ConfigMappingRow.svelte.d.ts +8 -0
  31. package/dist/components/ConfigPanel.svelte +56 -7
  32. package/dist/components/ConfigPanel.svelte.d.ts +2 -0
  33. package/dist/components/FlowDropEdge.svelte +2 -10
  34. package/dist/components/LogsSidebar.svelte +5 -5
  35. package/dist/components/NodeSidebar.svelte +15 -49
  36. package/dist/components/NodeSwapPicker.svelte +537 -0
  37. package/dist/components/NodeSwapPicker.svelte.d.ts +16 -0
  38. package/dist/components/PortMappingRow.svelte +209 -0
  39. package/dist/components/PortMappingRow.svelte.d.ts +12 -0
  40. package/dist/components/SwapMappingEditor.svelte +550 -0
  41. package/dist/components/SwapMappingEditor.svelte.d.ts +12 -0
  42. package/dist/components/WorkflowEditor.svelte +99 -4
  43. package/dist/components/WorkflowEditor.svelte.d.ts +8 -0
  44. package/dist/components/chat/AIChatPanel.svelte +658 -0
  45. package/dist/components/chat/AIChatPanel.svelte.d.ts +13 -0
  46. package/dist/components/chat/CommandPreview.svelte +184 -0
  47. package/dist/components/chat/CommandPreview.svelte.d.ts +9 -0
  48. package/dist/components/console/CommandConsole.stories.svelte +93 -0
  49. package/dist/components/console/CommandConsole.stories.svelte.d.ts +27 -0
  50. package/dist/components/console/CommandConsole.svelte +259 -0
  51. package/dist/components/console/CommandConsole.svelte.d.ts +11 -0
  52. package/dist/components/console/ConsoleAutocomplete.svelte +139 -0
  53. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +21 -0
  54. package/dist/components/console/ConsoleInput.svelte +712 -0
  55. package/dist/components/console/ConsoleInput.svelte.d.ts +16 -0
  56. package/dist/components/console/ConsoleOutput.svelte +121 -0
  57. package/dist/components/console/ConsoleOutput.svelte.d.ts +11 -0
  58. package/dist/components/console/formatters.d.ts +26 -0
  59. package/dist/components/console/formatters.js +118 -0
  60. package/dist/components/interrupt/index.d.ts +1 -0
  61. package/dist/components/interrupt/index.js +1 -0
  62. package/dist/config/endpoints.d.ts +8 -0
  63. package/dist/config/endpoints.js +5 -0
  64. package/dist/core/index.d.ts +5 -0
  65. package/dist/core/index.js +9 -0
  66. package/dist/editor/index.d.ts +3 -1
  67. package/dist/editor/index.js +4 -2
  68. package/dist/helpers/proximityConnect.js +8 -1
  69. package/dist/helpers/workflowEditorHelper.d.ts +3 -53
  70. package/dist/helpers/workflowEditorHelper.js +13 -228
  71. package/dist/playground/index.d.ts +1 -1
  72. package/dist/playground/index.js +1 -1
  73. package/dist/schemas/v1/workflow.schema.json +107 -22
  74. package/dist/services/chatService.d.ts +65 -0
  75. package/dist/services/chatService.js +131 -0
  76. package/dist/services/historyService.d.ts +6 -4
  77. package/dist/services/historyService.js +21 -6
  78. package/dist/stores/interruptStore.svelte.js +6 -1
  79. package/dist/stores/playgroundStore.svelte.d.ts +1 -1
  80. package/dist/stores/playgroundStore.svelte.js +11 -2
  81. package/dist/stores/portCoordinateStore.svelte.d.ts +4 -0
  82. package/dist/stores/portCoordinateStore.svelte.js +20 -26
  83. package/dist/stores/workflowStore.svelte.d.ts +31 -2
  84. package/dist/stores/workflowStore.svelte.js +84 -64
  85. package/dist/types/chat.d.ts +63 -0
  86. package/dist/types/chat.js +9 -0
  87. package/dist/types/events.d.ts +28 -2
  88. package/dist/types/events.js +1 -0
  89. package/dist/types/index.d.ts +8 -0
  90. package/dist/types/settings.d.ts +6 -0
  91. package/dist/types/settings.js +3 -0
  92. package/dist/utils/edgeStyling.d.ts +42 -0
  93. package/dist/utils/edgeStyling.js +176 -0
  94. package/dist/utils/nodeIds.d.ts +31 -0
  95. package/dist/utils/nodeIds.js +42 -0
  96. package/dist/utils/nodeSwap.d.ts +221 -0
  97. package/dist/utils/nodeSwap.js +686 -0
  98. package/package.json +6 -1
  99. package/dist/helpers/nodeLayoutHelper.d.ts +0 -14
  100. package/dist/helpers/nodeLayoutHelper.js +0 -19
@@ -0,0 +1,658 @@
1
+ <script lang="ts">
2
+ import type {
3
+ ChatHistoryMessage,
4
+ ChatRequest,
5
+ CommandPreviewItem,
6
+ } from "../../types/chat.js";
7
+ import type { NodeMetadata } from "../../types/index.js";
8
+ import type { UIAction } from "../../commands/types.js";
9
+ import type { EndpointConfig } from "../../config/endpoints.js";
10
+ import { chatService } from "../../services/chatService.js";
11
+ import { getWorkflowStore } from "../../stores/workflowStore.svelte.js";
12
+ import { extractCommands } from "../../chat/responseParser.js";
13
+ import { isMutatingCommand } from "../../chat/commandClassifier.js";
14
+ import { parseCommand } from "../../commands/parser.js";
15
+ import { executeCommand, executeBatch } from "../../commands/index.js";
16
+ import { createStoreCommandContext } from "../../commands/storeIntegration.svelte.js";
17
+ import CommandPreview from "./CommandPreview.svelte";
18
+ import { tick } from "svelte";
19
+ import Icon from "@iconify/svelte";
20
+
21
+ // =========================================================================
22
+ // Internal Display Message Type
23
+ // =========================================================================
24
+
25
+ interface DisplayMessage {
26
+ role: "user" | "assistant";
27
+ content: string;
28
+ /** Mutating commands awaiting approval */
29
+ commandPreview?: CommandPreviewItem[];
30
+ /** Results from auto-executed read-only commands */
31
+ readOnlyResults?: string[];
32
+ }
33
+
34
+ interface Props {
35
+ nodeTypes: NodeMetadata[];
36
+ workflowId?: string;
37
+ onUIAction?: (action: UIAction) => void;
38
+ placeholder?: string;
39
+ endpointConfig?: EndpointConfig | null;
40
+ }
41
+
42
+ let { nodeTypes, workflowId, onUIAction, placeholder, endpointConfig }: Props = $props();
43
+
44
+ // =========================================================================
45
+ // State
46
+ // =========================================================================
47
+
48
+ let displayMessages: DisplayMessage[] = $state([]);
49
+ let inputValue: string = $state("");
50
+ let isLoading: boolean = $state(false);
51
+ let inputElement: HTMLTextAreaElement | undefined = $state();
52
+ let messagesElement: HTMLDivElement | undefined = $state();
53
+
54
+ // =========================================================================
55
+ // Derived State
56
+ // =========================================================================
57
+
58
+ const isDisabled = $derived(!workflowId);
59
+ const isChatConfigured = $derived(
60
+ endpointConfig?.endpoints?.chat !== undefined,
61
+ );
62
+ const canSend = $derived(
63
+ inputValue.trim().length > 0 &&
64
+ !isLoading &&
65
+ !isDisabled &&
66
+ isChatConfigured,
67
+ );
68
+
69
+ // =========================================================================
70
+ // Auto-scroll
71
+ // =========================================================================
72
+
73
+ $effect(() => {
74
+ const _count = displayMessages.length;
75
+ const _loading = isLoading;
76
+ tick().then(() => {
77
+ if (messagesElement) {
78
+ messagesElement.scrollTop = messagesElement.scrollHeight;
79
+ }
80
+ });
81
+ });
82
+
83
+ // =========================================================================
84
+ // Helpers
85
+ // =========================================================================
86
+
87
+ /** Build conversation history from display messages for API requests */
88
+ function getHistory(): ChatHistoryMessage[] {
89
+ return displayMessages.map((m) => ({ role: m.role, content: m.content }));
90
+ }
91
+
92
+ function getWorkflowState(): unknown {
93
+ const workflow = getWorkflowStore();
94
+ if (!workflow) return null;
95
+ return {
96
+ nodes: workflow.nodes.map((n) => ({
97
+ id: n.id,
98
+ type: n.type,
99
+ data: n.data,
100
+ position: n.position,
101
+ })),
102
+ edges: workflow.edges.map((e) => ({
103
+ id: e.id,
104
+ source: e.source,
105
+ sourceHandle: e.sourceHandle,
106
+ target: e.target,
107
+ targetHandle: e.targetHandle,
108
+ })),
109
+ };
110
+ }
111
+
112
+ function getCommandContext() {
113
+ return createStoreCommandContext(nodeTypes, onUIAction);
114
+ }
115
+
116
+ // =========================================================================
117
+ // Command Execution
118
+ // =========================================================================
119
+
120
+ /**
121
+ * Process an LLM response: extract commands, auto-execute read-only ones,
122
+ * and queue mutating ones for approval via CommandPreview.
123
+ */
124
+ function processResponse(responseContent: string): DisplayMessage {
125
+ const { explanation, commands } = extractCommands(responseContent);
126
+
127
+ // No commands — pure chat message
128
+ if (commands.length === 0) {
129
+ return { role: "assistant", content: explanation || responseContent };
130
+ }
131
+
132
+ const context = getCommandContext();
133
+ const readOnlyResults: string[] = [];
134
+ const mutatingCommands: CommandPreviewItem[] = [];
135
+
136
+ for (const raw of commands) {
137
+ const parsed = parseCommand(raw);
138
+ if (!parsed.ok) {
139
+ // Treat parse errors as mutating (show in preview as error)
140
+ mutatingCommands.push({ raw, status: "error", result: parsed.error });
141
+ continue;
142
+ }
143
+
144
+ if (!isMutatingCommand(parsed.command.type)) {
145
+ // Auto-execute read-only commands
146
+ if (context) {
147
+ const result = executeCommand(parsed.command, context);
148
+ if (result.ok) {
149
+ readOnlyResults.push(`> ${raw}\n${result.message}`);
150
+ } else {
151
+ readOnlyResults.push(`> ${raw}\nError: ${result.error}`);
152
+ }
153
+ } else {
154
+ readOnlyResults.push(`> ${raw}\nError: No workflow loaded`);
155
+ }
156
+ } else {
157
+ mutatingCommands.push({ raw, status: "pending" });
158
+ }
159
+ }
160
+
161
+ const msg: DisplayMessage = {
162
+ role: "assistant",
163
+ content: explanation || responseContent,
164
+ };
165
+
166
+ if (readOnlyResults.length > 0) {
167
+ msg.readOnlyResults = readOnlyResults;
168
+ }
169
+ if (mutatingCommands.length > 0) {
170
+ msg.commandPreview = mutatingCommands;
171
+ }
172
+
173
+ return msg;
174
+ }
175
+
176
+ /** Execute all pending mutating commands in a CommandPreview via executeBatch */
177
+ function handleApproveCommands(messageIndex: number) {
178
+ const msg = displayMessages[messageIndex];
179
+ if (!msg?.commandPreview) return;
180
+
181
+ const context = getCommandContext();
182
+ if (!context) {
183
+ // Mark all as error
184
+ for (const cmd of msg.commandPreview) {
185
+ if (cmd.status === "pending") {
186
+ cmd.status = "error";
187
+ cmd.result = "No workflow loaded";
188
+ }
189
+ }
190
+ appendErrorToHistory("Command execution failed: No workflow loaded");
191
+ return;
192
+ }
193
+
194
+ // Parse all pending commands
195
+ const pendingItems = msg.commandPreview.filter(
196
+ (c) => c.status === "pending",
197
+ );
198
+ const parsedCommands: { item: CommandPreviewItem; command: unknown }[] = [];
199
+
200
+ for (const item of pendingItems) {
201
+ item.status = "executing";
202
+ const parsed = parseCommand(item.raw);
203
+ if (!parsed.ok) {
204
+ item.status = "error";
205
+ item.result = parsed.error;
206
+ // Revert other executing items back to pending
207
+ for (const other of pendingItems) {
208
+ if (other.status === "executing") other.status = "pending";
209
+ }
210
+ appendErrorToHistory(
211
+ `Command parse error for "${item.raw}": ${parsed.error}`,
212
+ );
213
+ return;
214
+ }
215
+ parsedCommands.push({ item, command: parsed.command });
216
+ }
217
+
218
+ // Execute atomically via executeBatch
219
+ const commands = parsedCommands.map(
220
+ (p) => p.command,
221
+ ) as import("../../commands/types.js").Command[];
222
+
223
+ let batchResult: ReturnType<typeof executeBatch>;
224
+ try {
225
+ batchResult = executeBatch(commands, context);
226
+ } catch (err) {
227
+ // Unexpected error — reset all items to pending so the UI isn't stuck
228
+ for (const { item } of parsedCommands) {
229
+ item.status = "pending";
230
+ }
231
+ appendErrorToHistory(
232
+ `Unexpected execution error: ${err instanceof Error ? err.message : String(err)}`,
233
+ );
234
+ return;
235
+ }
236
+
237
+ // Update status for each command
238
+ for (let i = 0; i < parsedCommands.length; i++) {
239
+ const { item } = parsedCommands[i];
240
+ const result = batchResult.results[i];
241
+ if (result) {
242
+ if (result.ok) {
243
+ item.status = "success";
244
+ item.result = result.message;
245
+ } else {
246
+ item.status = "error";
247
+ item.result = result.error;
248
+ }
249
+ } else {
250
+ // Commands after the failed one weren't executed
251
+ item.status = "pending";
252
+ }
253
+ }
254
+
255
+ if (!batchResult.ok) {
256
+ appendErrorToHistory(
257
+ `Command execution failed at command ${batchResult.completedCount + 1}/${batchResult.totalCount}: ${batchResult.error}`,
258
+ );
259
+ }
260
+ }
261
+
262
+ /** Dismiss a command preview without executing */
263
+ function handleCancelCommands(messageIndex: number) {
264
+ const msg = displayMessages[messageIndex];
265
+ if (!msg?.commandPreview) return;
266
+ // Remove the command preview entirely
267
+ msg.commandPreview = undefined;
268
+ }
269
+
270
+ /** Append an error message to conversation history so the LLM can self-correct */
271
+ function appendErrorToHistory(errorMessage: string) {
272
+ displayMessages.push({
273
+ role: "assistant",
274
+ content: `Error: ${errorMessage}`,
275
+ });
276
+ }
277
+
278
+ // =========================================================================
279
+ // Message Handling
280
+ // =========================================================================
281
+
282
+ async function sendMessage() {
283
+ const text = inputValue.trim();
284
+ if (!text || isLoading || !workflowId) return;
285
+
286
+ // Add user message
287
+ displayMessages.push({ role: "user", content: text });
288
+ inputValue = "";
289
+ isLoading = true;
290
+
291
+ try {
292
+ const history = getHistory();
293
+ const request: ChatRequest = {
294
+ message: text,
295
+ workflowState: getWorkflowState(),
296
+ history: history.slice(0, -1), // all except current message (already sent as `message`)
297
+ };
298
+
299
+ const response = await chatService.sendMessage(workflowId, request);
300
+
301
+ // Process response: extract commands, auto-execute read-only, queue mutating
302
+ const displayMsg = processResponse(response.content);
303
+ displayMessages.push(displayMsg);
304
+ } catch (err) {
305
+ const errorMessage =
306
+ err instanceof Error ? err.message : "Failed to send message";
307
+ displayMessages.push({
308
+ role: "assistant",
309
+ content: `Error: ${errorMessage}`,
310
+ });
311
+ } finally {
312
+ isLoading = false;
313
+ tick().then(() => inputElement?.focus());
314
+ }
315
+ }
316
+
317
+ function handleKeydown(event: KeyboardEvent) {
318
+ if (event.key === "Enter" && !event.shiftKey) {
319
+ event.preventDefault();
320
+ sendMessage();
321
+ }
322
+ }
323
+ </script>
324
+
325
+ <div class="ai-chat-panel" role="region" aria-label="AI Chat">
326
+ {#if !isChatConfigured}
327
+ <!-- No backend configured -->
328
+ <div class="ai-chat-panel__notice">
329
+ <Icon icon="mdi:robot-off-outline" />
330
+ <span>AI Chat requires backend configuration</span>
331
+ </div>
332
+ {:else if isDisabled}
333
+ <!-- No workflow loaded -->
334
+ <div class="ai-chat-panel__notice">
335
+ <Icon icon="mdi:chat-sleep-outline" />
336
+ <span>Load a workflow to start chatting</span>
337
+ </div>
338
+ {:else}
339
+ <!-- Messages area -->
340
+ <div
341
+ class="ai-chat-panel__messages"
342
+ bind:this={messagesElement}
343
+ role="log"
344
+ aria-live="polite"
345
+ >
346
+ {#if displayMessages.length === 0}
347
+ <div class="ai-chat-panel__empty">
348
+ <Icon icon="mdi:chat-outline" />
349
+ <span>Ask the AI to help build your workflow</span>
350
+ </div>
351
+ {/if}
352
+ {#each displayMessages as message, msgIndex}
353
+ <div class="ai-chat-panel__bubble ai-chat-panel__bubble--{message.role}">
354
+ <div class="ai-chat-panel__bubble-content">{message.content}</div>
355
+ {#if message.readOnlyResults && message.readOnlyResults.length > 0}
356
+ <div class="ai-chat-panel__readonly-results">
357
+ {#each message.readOnlyResults as result}
358
+ <pre class="ai-chat-panel__readonly-result">{result}</pre>
359
+ {/each}
360
+ </div>
361
+ {/if}
362
+ {#if message.commandPreview && message.commandPreview.length > 0}
363
+ <div class="ai-chat-panel__command-preview">
364
+ <CommandPreview
365
+ commands={message.commandPreview}
366
+ onApprove={() => handleApproveCommands(msgIndex)}
367
+ onCancel={() => handleCancelCommands(msgIndex)}
368
+ />
369
+ </div>
370
+ {/if}
371
+ </div>
372
+ {/each}
373
+ {#if isLoading}
374
+ <div class="ai-chat-panel__bubble ai-chat-panel__bubble--assistant">
375
+ <div class="ai-chat-panel__thinking">
376
+ <span class="ai-chat-panel__dot"></span>
377
+ <span class="ai-chat-panel__dot"></span>
378
+ <span class="ai-chat-panel__dot"></span>
379
+ </div>
380
+ </div>
381
+ {/if}
382
+ </div>
383
+
384
+ <!-- Input area -->
385
+ <div class="ai-chat-panel__input-area">
386
+ <textarea
387
+ bind:this={inputElement}
388
+ bind:value={inputValue}
389
+ onkeydown={handleKeydown}
390
+ class="ai-chat-panel__input"
391
+ placeholder={placeholder ?? "Describe what you want to build..."}
392
+ rows="1"
393
+ disabled={isLoading}
394
+ ></textarea>
395
+ <button
396
+ class="ai-chat-panel__send"
397
+ onclick={sendMessage}
398
+ disabled={!canSend}
399
+ aria-label="Send message"
400
+ >
401
+ <Icon icon="mdi:send" />
402
+ </button>
403
+ </div>
404
+ {/if}
405
+ </div>
406
+
407
+ <style>
408
+ .ai-chat-panel {
409
+ display: flex;
410
+ flex-direction: column;
411
+ height: 100%;
412
+ width: 100%;
413
+ background: var(--fd-background);
414
+ color: var(--fd-foreground);
415
+ overflow: hidden;
416
+ }
417
+
418
+ /* Notice / disabled states */
419
+ .ai-chat-panel__notice {
420
+ display: flex;
421
+ flex-direction: column;
422
+ align-items: center;
423
+ justify-content: center;
424
+ gap: var(--fd-space-sm);
425
+ height: 100%;
426
+ color: var(--fd-muted-foreground);
427
+ font-size: var(--fd-text-sm);
428
+ text-align: center;
429
+ padding: var(--fd-space-md);
430
+ }
431
+
432
+ .ai-chat-panel__notice :global(svg) {
433
+ font-size: 2rem;
434
+ opacity: 0.5;
435
+ }
436
+
437
+ /* Messages area */
438
+ .ai-chat-panel__messages {
439
+ flex: 1;
440
+ overflow-y: auto;
441
+ padding: var(--fd-space-sm);
442
+ display: flex;
443
+ flex-direction: column;
444
+ gap: var(--fd-space-xs);
445
+ scrollbar-width: thin;
446
+ scrollbar-color: var(--fd-scrollbar-thumb) var(--fd-scrollbar-track);
447
+ }
448
+
449
+ .ai-chat-panel__messages::-webkit-scrollbar {
450
+ width: 8px;
451
+ }
452
+
453
+ .ai-chat-panel__messages::-webkit-scrollbar-track {
454
+ background: var(--fd-scrollbar-track);
455
+ }
456
+
457
+ .ai-chat-panel__messages::-webkit-scrollbar-thumb {
458
+ background: var(--fd-scrollbar-thumb);
459
+ border-radius: 4px;
460
+ }
461
+
462
+ .ai-chat-panel__messages::-webkit-scrollbar-thumb:hover {
463
+ background: var(--fd-scrollbar-thumb-hover);
464
+ }
465
+
466
+ /* Empty state */
467
+ .ai-chat-panel__empty {
468
+ display: flex;
469
+ flex-direction: column;
470
+ align-items: center;
471
+ justify-content: center;
472
+ gap: var(--fd-space-xs);
473
+ height: 100%;
474
+ color: var(--fd-muted-foreground);
475
+ font-size: var(--fd-text-sm);
476
+ opacity: 0.6;
477
+ }
478
+
479
+ .ai-chat-panel__empty :global(svg) {
480
+ font-size: 1.5rem;
481
+ }
482
+
483
+ /* Message bubbles */
484
+ .ai-chat-panel__bubble {
485
+ max-width: 80%;
486
+ animation: fadeIn 0.15s ease-out;
487
+ }
488
+
489
+ .ai-chat-panel__bubble--user {
490
+ align-self: flex-end;
491
+ }
492
+
493
+ .ai-chat-panel__bubble--assistant {
494
+ align-self: flex-start;
495
+ }
496
+
497
+ .ai-chat-panel__bubble-content {
498
+ padding: var(--fd-space-xs) var(--fd-space-sm);
499
+ border-radius: var(--fd-radius-md);
500
+ font-size: var(--fd-text-sm);
501
+ line-height: 1.5;
502
+ white-space: pre-wrap;
503
+ word-break: break-word;
504
+ }
505
+
506
+ .ai-chat-panel__bubble--user .ai-chat-panel__bubble-content {
507
+ background: var(--fd-primary);
508
+ color: var(--fd-primary-foreground);
509
+ border-bottom-right-radius: var(--fd-radius-xs);
510
+ }
511
+
512
+ .ai-chat-panel__bubble--assistant .ai-chat-panel__bubble-content {
513
+ background: var(--fd-muted);
514
+ color: var(--fd-foreground);
515
+ border-bottom-left-radius: var(--fd-radius-xs);
516
+ }
517
+
518
+ /* Read-only command results */
519
+ .ai-chat-panel__readonly-results {
520
+ margin-top: var(--fd-space-xs);
521
+ }
522
+
523
+ .ai-chat-panel__readonly-result {
524
+ margin: 0;
525
+ padding: var(--fd-space-xs) var(--fd-space-sm);
526
+ background: var(--fd-card);
527
+ border: 1px solid var(--fd-border);
528
+ border-radius: var(--fd-radius-sm);
529
+ font-family: var(--fd-font-mono);
530
+ font-size: var(--fd-text-xs);
531
+ line-height: 1.4;
532
+ white-space: pre-wrap;
533
+ word-break: break-word;
534
+ color: var(--fd-foreground);
535
+ }
536
+
537
+ .ai-chat-panel__readonly-result + .ai-chat-panel__readonly-result {
538
+ margin-top: var(--fd-space-3xs);
539
+ }
540
+
541
+ /* Command preview block */
542
+ .ai-chat-panel__command-preview {
543
+ margin-top: var(--fd-space-xs);
544
+ }
545
+
546
+ @keyframes fadeIn {
547
+ from {
548
+ opacity: 0;
549
+ transform: translateY(4px);
550
+ }
551
+ to {
552
+ opacity: 1;
553
+ transform: translateY(0);
554
+ }
555
+ }
556
+
557
+ /* Thinking indicator */
558
+ .ai-chat-panel__thinking {
559
+ display: flex;
560
+ gap: 4px;
561
+ padding: var(--fd-space-xs) var(--fd-space-sm);
562
+ background: var(--fd-muted);
563
+ border-radius: var(--fd-radius-md);
564
+ border-bottom-left-radius: var(--fd-radius-xs);
565
+ }
566
+
567
+ .ai-chat-panel__dot {
568
+ width: 6px;
569
+ height: 6px;
570
+ border-radius: 50%;
571
+ background: var(--fd-muted-foreground);
572
+ animation: bounce 1.4s ease-in-out infinite;
573
+ }
574
+
575
+ .ai-chat-panel__dot:nth-child(2) {
576
+ animation-delay: 0.2s;
577
+ }
578
+
579
+ .ai-chat-panel__dot:nth-child(3) {
580
+ animation-delay: 0.4s;
581
+ }
582
+
583
+ @keyframes bounce {
584
+ 0%,
585
+ 60%,
586
+ 100% {
587
+ transform: translateY(0);
588
+ }
589
+ 30% {
590
+ transform: translateY(-4px);
591
+ }
592
+ }
593
+
594
+ /* Input area */
595
+ .ai-chat-panel__input-area {
596
+ display: flex;
597
+ align-items: flex-end;
598
+ gap: var(--fd-space-xs);
599
+ padding: var(--fd-space-xs) var(--fd-space-sm);
600
+ border-top: 1px solid var(--fd-border);
601
+ background: var(--fd-background);
602
+ }
603
+
604
+ .ai-chat-panel__input {
605
+ flex: 1;
606
+ resize: none;
607
+ border: 1px solid var(--fd-border);
608
+ border-radius: var(--fd-radius-md);
609
+ padding: var(--fd-space-xs) var(--fd-space-sm);
610
+ font-family: var(--fd-font-sans, inherit);
611
+ font-size: var(--fd-text-sm);
612
+ line-height: 1.4;
613
+ color: var(--fd-foreground);
614
+ background: var(--fd-card);
615
+ outline: none;
616
+ max-height: 80px;
617
+ overflow-y: auto;
618
+ }
619
+
620
+ .ai-chat-panel__input:focus {
621
+ border-color: var(--fd-primary);
622
+ }
623
+
624
+ .ai-chat-panel__input:disabled {
625
+ opacity: 0.5;
626
+ cursor: not-allowed;
627
+ }
628
+
629
+ .ai-chat-panel__input::placeholder {
630
+ color: var(--fd-muted-foreground);
631
+ }
632
+
633
+ .ai-chat-panel__send {
634
+ display: flex;
635
+ align-items: center;
636
+ justify-content: center;
637
+ width: 32px;
638
+ height: 32px;
639
+ border: none;
640
+ border-radius: var(--fd-radius-md);
641
+ background: var(--fd-primary);
642
+ color: var(--fd-primary-foreground);
643
+ cursor: pointer;
644
+ flex-shrink: 0;
645
+ transition:
646
+ background-color var(--fd-transition-fast),
647
+ opacity var(--fd-transition-fast);
648
+ }
649
+
650
+ .ai-chat-panel__send:hover:not(:disabled) {
651
+ background: var(--fd-primary-hover);
652
+ }
653
+
654
+ .ai-chat-panel__send:disabled {
655
+ opacity: 0.4;
656
+ cursor: not-allowed;
657
+ }
658
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { NodeMetadata } from "../../types/index.js";
2
+ import type { UIAction } from "../../commands/types.js";
3
+ import type { EndpointConfig } from "../../config/endpoints.js";
4
+ interface Props {
5
+ nodeTypes: NodeMetadata[];
6
+ workflowId?: string;
7
+ onUIAction?: (action: UIAction) => void;
8
+ placeholder?: string;
9
+ endpointConfig?: EndpointConfig | null;
10
+ }
11
+ declare const AIChatPanel: import("svelte").Component<Props, {}, "">;
12
+ type AIChatPanel = ReturnType<typeof AIChatPanel>;
13
+ export default AIChatPanel;