@d34dman/flowdrop 0.0.31 → 0.0.33
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/dist/components/playground/ChatPanel.svelte +5 -1
- package/dist/components/playground/ChatPanel.svelte.d.ts +2 -0
- package/dist/components/playground/MessageBubble.svelte +177 -28
- package/dist/components/playground/MessageBubble.svelte.d.ts +2 -0
- package/dist/components/playground/Playground.svelte +26 -10
- package/dist/components/playground/PlaygroundModal.svelte +220 -0
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +25 -0
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +1 -0
- package/dist/playground/index.d.ts +33 -0
- package/dist/playground/index.js +33 -0
- package/dist/playground/mount.d.ts +2 -1
- package/dist/playground/mount.js +47 -16
- package/dist/services/playgroundService.js +5 -2
- package/dist/types/playground.d.ts +3 -1
- package/package.json +1 -1
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
onStopExecution?: () => void;
|
|
36
36
|
/** Whether to show log messages inline (false = hide them) */
|
|
37
37
|
showLogsInline?: boolean;
|
|
38
|
+
/** Whether to enable markdown rendering in messages */
|
|
39
|
+
enableMarkdown?: boolean;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
let {
|
|
@@ -43,7 +45,8 @@
|
|
|
43
45
|
placeholder = 'Type your message...',
|
|
44
46
|
onSendMessage,
|
|
45
47
|
onStopExecution,
|
|
46
|
-
showLogsInline = false
|
|
48
|
+
showLogsInline = false,
|
|
49
|
+
enableMarkdown = true
|
|
47
50
|
}: Props = $props();
|
|
48
51
|
|
|
49
52
|
/** Input field value */
|
|
@@ -206,6 +209,7 @@
|
|
|
206
209
|
{message}
|
|
207
210
|
showTimestamp={showTimestamps}
|
|
208
211
|
isLast={index === displayMessages.length - 1}
|
|
212
|
+
{enableMarkdown}
|
|
209
213
|
/>
|
|
210
214
|
{/each}
|
|
211
215
|
|
|
@@ -14,6 +14,8 @@ interface Props {
|
|
|
14
14
|
onStopExecution?: () => void;
|
|
15
15
|
/** Whether to show log messages inline (false = hide them) */
|
|
16
16
|
showLogsInline?: boolean;
|
|
17
|
+
/** Whether to enable markdown rendering in messages */
|
|
18
|
+
enableMarkdown?: boolean;
|
|
17
19
|
}
|
|
18
20
|
declare const ChatPanel: import("svelte").Component<Props, {}, "">;
|
|
19
21
|
type ChatPanel = ReturnType<typeof ChatPanel>;
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
Renders individual messages in the playground chat interface.
|
|
5
5
|
Supports different message roles with distinct styling.
|
|
6
|
+
Supports markdown rendering for message content.
|
|
6
7
|
Styled with BEM syntax.
|
|
7
8
|
-->
|
|
8
9
|
|
|
9
10
|
<script lang="ts">
|
|
10
11
|
import Icon from '@iconify/svelte';
|
|
12
|
+
import { marked } from 'marked';
|
|
11
13
|
import type { PlaygroundMessage, PlaygroundMessageRole } from '../../types/playground.js';
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -20,9 +22,20 @@
|
|
|
20
22
|
showTimestamp?: boolean;
|
|
21
23
|
/** Whether this is the last message (affects styling) */
|
|
22
24
|
isLast?: boolean;
|
|
25
|
+
/** Whether to render markdown content */
|
|
26
|
+
enableMarkdown?: boolean;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
let { message, showTimestamp = true, isLast = false }: Props = $props();
|
|
29
|
+
let { message, showTimestamp = true, isLast = false, enableMarkdown = true }: Props = $props();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Render content as markdown or plain text
|
|
33
|
+
*/
|
|
34
|
+
const renderedContent = $derived(
|
|
35
|
+
enableMarkdown && message.role !== 'log'
|
|
36
|
+
? marked.parse(message.content || '')
|
|
37
|
+
: message.content
|
|
38
|
+
);
|
|
26
39
|
|
|
27
40
|
/**
|
|
28
41
|
* Get the icon for the message role
|
|
@@ -144,7 +157,13 @@
|
|
|
144
157
|
|
|
145
158
|
<!-- Message Text -->
|
|
146
159
|
<div class="message-bubble__text">
|
|
147
|
-
{message.
|
|
160
|
+
{#if enableMarkdown && message.role !== 'log'}
|
|
161
|
+
<!-- Markdown content - marked.js sanitizes content by default -->
|
|
162
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
163
|
+
{@html renderedContent}
|
|
164
|
+
{:else}
|
|
165
|
+
{message.content}
|
|
166
|
+
{/if}
|
|
148
167
|
</div>
|
|
149
168
|
|
|
150
169
|
<!-- Metadata Footer -->
|
|
@@ -188,32 +207,33 @@
|
|
|
188
207
|
}
|
|
189
208
|
}
|
|
190
209
|
|
|
191
|
-
/* Role-specific styling */
|
|
210
|
+
/* Role-specific styling - Neutral theme */
|
|
192
211
|
.message-bubble--user {
|
|
193
|
-
background:
|
|
194
|
-
|
|
212
|
+
background-color: #f1f5f9;
|
|
213
|
+
border: 1px solid #e2e8f0;
|
|
214
|
+
color: #1e293b;
|
|
195
215
|
margin-left: 2rem;
|
|
196
216
|
flex-direction: row-reverse;
|
|
197
217
|
}
|
|
198
218
|
|
|
199
219
|
.message-bubble--assistant {
|
|
200
|
-
background-color: #
|
|
201
|
-
border: 1px solid #
|
|
202
|
-
color: #
|
|
220
|
+
background-color: #ffffff;
|
|
221
|
+
border: 1px solid #e5e7eb;
|
|
222
|
+
color: #1f2937;
|
|
203
223
|
margin-right: 2rem;
|
|
204
224
|
}
|
|
205
225
|
|
|
206
226
|
.message-bubble--system {
|
|
207
|
-
background-color: #
|
|
208
|
-
border: 1px solid #
|
|
209
|
-
color: #
|
|
227
|
+
background-color: #f9fafb;
|
|
228
|
+
border: 1px solid #e5e7eb;
|
|
229
|
+
color: #6b7280;
|
|
210
230
|
margin: 0 1rem;
|
|
211
231
|
font-size: 0.875rem;
|
|
212
232
|
}
|
|
213
233
|
|
|
214
234
|
.message-bubble--log {
|
|
215
|
-
background-color: #
|
|
216
|
-
border: 1px solid #
|
|
235
|
+
background-color: #f8fafc;
|
|
236
|
+
border: 1px solid #e2e8f0;
|
|
217
237
|
color: #475569;
|
|
218
238
|
margin: 0 1rem;
|
|
219
239
|
font-size: 0.8125rem;
|
|
@@ -249,18 +269,18 @@
|
|
|
249
269
|
}
|
|
250
270
|
|
|
251
271
|
.message-bubble--user .message-bubble__avatar {
|
|
252
|
-
background-color:
|
|
253
|
-
color: #
|
|
272
|
+
background-color: #e2e8f0;
|
|
273
|
+
color: #475569;
|
|
254
274
|
}
|
|
255
275
|
|
|
256
276
|
.message-bubble--assistant .message-bubble__avatar {
|
|
257
|
-
background-color: #
|
|
258
|
-
color: #
|
|
277
|
+
background-color: #e5e7eb;
|
|
278
|
+
color: #374151;
|
|
259
279
|
}
|
|
260
280
|
|
|
261
281
|
.message-bubble--system .message-bubble__avatar {
|
|
262
|
-
background-color: #
|
|
263
|
-
color: #
|
|
282
|
+
background-color: #f3f4f6;
|
|
283
|
+
color: #6b7280;
|
|
264
284
|
}
|
|
265
285
|
|
|
266
286
|
.message-bubble--log .message-bubble__avatar {
|
|
@@ -292,14 +312,15 @@
|
|
|
292
312
|
.message-bubble__role {
|
|
293
313
|
font-weight: 600;
|
|
294
314
|
font-size: 0.8125rem;
|
|
315
|
+
color: #374151;
|
|
295
316
|
}
|
|
296
317
|
|
|
297
318
|
.message-bubble--user .message-bubble__role {
|
|
298
|
-
color:
|
|
319
|
+
color: #475569;
|
|
299
320
|
}
|
|
300
321
|
|
|
301
322
|
.message-bubble--assistant .message-bubble__role {
|
|
302
|
-
color: #
|
|
323
|
+
color: #374151;
|
|
303
324
|
}
|
|
304
325
|
|
|
305
326
|
.message-bubble--log .message-bubble__role {
|
|
@@ -319,8 +340,8 @@
|
|
|
319
340
|
}
|
|
320
341
|
|
|
321
342
|
.message-bubble__log-level--info {
|
|
322
|
-
background-color: #
|
|
323
|
-
color: #
|
|
343
|
+
background-color: #e0f2fe;
|
|
344
|
+
color: #0369a1;
|
|
324
345
|
}
|
|
325
346
|
|
|
326
347
|
.message-bubble__log-level--warning {
|
|
@@ -340,24 +361,152 @@
|
|
|
340
361
|
|
|
341
362
|
.message-bubble__timestamp {
|
|
342
363
|
font-size: 0.6875rem;
|
|
343
|
-
|
|
364
|
+
color: #9ca3af;
|
|
344
365
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
345
366
|
}
|
|
346
367
|
|
|
347
368
|
.message-bubble--user .message-bubble__timestamp {
|
|
348
|
-
color:
|
|
369
|
+
color: #9ca3af;
|
|
349
370
|
}
|
|
350
371
|
|
|
351
372
|
/* Message text */
|
|
352
373
|
.message-bubble__text {
|
|
353
|
-
line-height: 1.
|
|
354
|
-
white-space: pre-wrap;
|
|
374
|
+
line-height: 1.6;
|
|
355
375
|
word-break: break-word;
|
|
356
376
|
}
|
|
357
377
|
|
|
358
378
|
.message-bubble--log .message-bubble__text {
|
|
359
379
|
font-size: 0.8125rem;
|
|
360
380
|
line-height: 1.4;
|
|
381
|
+
white-space: pre-wrap;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Markdown styling for message content */
|
|
385
|
+
.message-bubble__text :global(p) {
|
|
386
|
+
margin: 0 0 0.75rem 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.message-bubble__text :global(p:last-child) {
|
|
390
|
+
margin-bottom: 0;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.message-bubble__text :global(h1),
|
|
394
|
+
.message-bubble__text :global(h2),
|
|
395
|
+
.message-bubble__text :global(h3),
|
|
396
|
+
.message-bubble__text :global(h4),
|
|
397
|
+
.message-bubble__text :global(h5),
|
|
398
|
+
.message-bubble__text :global(h6) {
|
|
399
|
+
margin: 1rem 0 0.5rem 0;
|
|
400
|
+
font-weight: 600;
|
|
401
|
+
line-height: 1.3;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.message-bubble__text :global(h1:first-child),
|
|
405
|
+
.message-bubble__text :global(h2:first-child),
|
|
406
|
+
.message-bubble__text :global(h3:first-child),
|
|
407
|
+
.message-bubble__text :global(h4:first-child),
|
|
408
|
+
.message-bubble__text :global(h5:first-child),
|
|
409
|
+
.message-bubble__text :global(h6:first-child) {
|
|
410
|
+
margin-top: 0;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.message-bubble__text :global(h1) {
|
|
414
|
+
font-size: 1.25rem;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.message-bubble__text :global(h2) {
|
|
418
|
+
font-size: 1.125rem;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.message-bubble__text :global(h3) {
|
|
422
|
+
font-size: 1rem;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.message-bubble__text :global(ul),
|
|
426
|
+
.message-bubble__text :global(ol) {
|
|
427
|
+
margin: 0.5rem 0;
|
|
428
|
+
padding-left: 1.5rem;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.message-bubble__text :global(li) {
|
|
432
|
+
margin: 0.25rem 0;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.message-bubble__text :global(code) {
|
|
436
|
+
background-color: rgba(0, 0, 0, 0.06);
|
|
437
|
+
padding: 0.125rem 0.375rem;
|
|
438
|
+
border-radius: 0.25rem;
|
|
439
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
440
|
+
font-size: 0.875em;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.message-bubble__text :global(pre) {
|
|
444
|
+
background-color: #1e293b;
|
|
445
|
+
color: #e2e8f0;
|
|
446
|
+
padding: 0.75rem 1rem;
|
|
447
|
+
border-radius: 0.5rem;
|
|
448
|
+
overflow-x: auto;
|
|
449
|
+
margin: 0.75rem 0;
|
|
450
|
+
font-size: 0.8125rem;
|
|
451
|
+
line-height: 1.5;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.message-bubble__text :global(pre code) {
|
|
455
|
+
background-color: transparent;
|
|
456
|
+
padding: 0;
|
|
457
|
+
border-radius: 0;
|
|
458
|
+
color: inherit;
|
|
459
|
+
font-size: inherit;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.message-bubble__text :global(blockquote) {
|
|
463
|
+
border-left: 3px solid #d1d5db;
|
|
464
|
+
padding-left: 1rem;
|
|
465
|
+
margin: 0.75rem 0;
|
|
466
|
+
color: #6b7280;
|
|
467
|
+
font-style: italic;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.message-bubble__text :global(a) {
|
|
471
|
+
color: #2563eb;
|
|
472
|
+
text-decoration: none;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.message-bubble__text :global(a:hover) {
|
|
476
|
+
text-decoration: underline;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.message-bubble__text :global(hr) {
|
|
480
|
+
border: none;
|
|
481
|
+
border-top: 1px solid #e5e7eb;
|
|
482
|
+
margin: 1rem 0;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.message-bubble__text :global(table) {
|
|
486
|
+
border-collapse: collapse;
|
|
487
|
+
width: 100%;
|
|
488
|
+
margin: 0.75rem 0;
|
|
489
|
+
font-size: 0.875rem;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.message-bubble__text :global(th),
|
|
493
|
+
.message-bubble__text :global(td) {
|
|
494
|
+
border: 1px solid #e5e7eb;
|
|
495
|
+
padding: 0.5rem 0.75rem;
|
|
496
|
+
text-align: left;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.message-bubble__text :global(th) {
|
|
500
|
+
background-color: #f9fafb;
|
|
501
|
+
font-weight: 600;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.message-bubble__text :global(strong) {
|
|
505
|
+
font-weight: 600;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.message-bubble__text :global(em) {
|
|
509
|
+
font-style: italic;
|
|
361
510
|
}
|
|
362
511
|
|
|
363
512
|
/* Footer */
|
|
@@ -367,7 +516,7 @@
|
|
|
367
516
|
gap: 0.75rem;
|
|
368
517
|
margin-top: 0.5rem;
|
|
369
518
|
font-size: 0.6875rem;
|
|
370
|
-
|
|
519
|
+
color: #9ca3af;
|
|
371
520
|
}
|
|
372
521
|
|
|
373
522
|
.message-bubble--user .message-bubble__footer {
|
|
@@ -9,6 +9,8 @@ interface Props {
|
|
|
9
9
|
showTimestamp?: boolean;
|
|
10
10
|
/** Whether this is the last message (affects styling) */
|
|
11
11
|
isLast?: boolean;
|
|
12
|
+
/** Whether to render markdown content */
|
|
13
|
+
enableMarkdown?: boolean;
|
|
12
14
|
}
|
|
13
15
|
declare const MessageBubble: import("svelte").Component<Props, {}, "">;
|
|
14
16
|
type MessageBubble = ReturnType<typeof MessageBubble>;
|
|
@@ -335,15 +335,20 @@
|
|
|
335
335
|
playgroundActions.addMessages(response.data);
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
338
|
+
// Update session status
|
|
339
|
+
if (response.sessionStatus) {
|
|
340
|
+
playgroundActions.updateSessionStatus(response.sessionStatus);
|
|
341
|
+
|
|
342
|
+
// Stop executing if idle, completed, or failed
|
|
343
|
+
// "idle" means no processing is happening (execution finished)
|
|
344
|
+
if (
|
|
345
|
+
response.sessionStatus === 'idle' ||
|
|
346
|
+
response.sessionStatus === 'completed' ||
|
|
347
|
+
response.sessionStatus === 'failed'
|
|
348
|
+
) {
|
|
349
|
+
playgroundActions.setExecuting(false);
|
|
346
350
|
}
|
|
351
|
+
}
|
|
347
352
|
},
|
|
348
353
|
pollingInterval
|
|
349
354
|
);
|
|
@@ -383,6 +388,7 @@
|
|
|
383
388
|
class="playground"
|
|
384
389
|
class:playground--embedded={mode === 'embedded'}
|
|
385
390
|
class:playground--standalone={mode === 'standalone'}
|
|
391
|
+
class:playground--modal={mode === 'modal'}
|
|
386
392
|
>
|
|
387
393
|
<div class="playground__container">
|
|
388
394
|
<!-- Sidebar -->
|
|
@@ -393,14 +399,18 @@
|
|
|
393
399
|
<Icon icon="mdi:play-circle-outline" />
|
|
394
400
|
<span>Playground</span>
|
|
395
401
|
</div>
|
|
396
|
-
{#if mode === 'embedded' && onClose}
|
|
402
|
+
{#if (mode === 'embedded' || mode === 'modal') && onClose}
|
|
397
403
|
<button
|
|
398
404
|
type="button"
|
|
399
405
|
class="playground__sidebar-close"
|
|
400
406
|
onclick={onClose}
|
|
401
407
|
title="Close playground"
|
|
402
408
|
>
|
|
403
|
-
|
|
409
|
+
{#if mode === 'modal'}
|
|
410
|
+
<Icon icon="mdi:close" />
|
|
411
|
+
{:else}
|
|
412
|
+
<Icon icon="mdi:dock-right" />
|
|
413
|
+
{/if}
|
|
404
414
|
</button>
|
|
405
415
|
{/if}
|
|
406
416
|
</div>
|
|
@@ -508,6 +518,7 @@
|
|
|
508
518
|
showTimestamps={config.showTimestamps ?? true}
|
|
509
519
|
autoScroll={config.autoScroll ?? true}
|
|
510
520
|
showLogsInline={config.logDisplayMode === 'inline'}
|
|
521
|
+
enableMarkdown={config.enableMarkdown ?? true}
|
|
511
522
|
onSendMessage={handleSendMessage}
|
|
512
523
|
onStopExecution={handleStopExecution}
|
|
513
524
|
/>
|
|
@@ -536,6 +547,11 @@
|
|
|
536
547
|
height: 100vh;
|
|
537
548
|
}
|
|
538
549
|
|
|
550
|
+
.playground--modal {
|
|
551
|
+
height: 100%;
|
|
552
|
+
width: 100%;
|
|
553
|
+
}
|
|
554
|
+
|
|
539
555
|
/* Container */
|
|
540
556
|
.playground__container {
|
|
541
557
|
display: flex;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
PlaygroundModal Component
|
|
3
|
+
|
|
4
|
+
Modal wrapper for the Playground component.
|
|
5
|
+
Provides a centered modal dialog with backdrop, similar to Langflow's implementation.
|
|
6
|
+
Supports closing via backdrop click, Escape key, or close button.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import Icon from "@iconify/svelte";
|
|
11
|
+
import Playground from "./Playground.svelte";
|
|
12
|
+
import type { Workflow } from "../../types/index.js";
|
|
13
|
+
import type { EndpointConfig } from "../../config/endpoints.js";
|
|
14
|
+
import type { PlaygroundConfig } from "../../types/playground.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Component props
|
|
18
|
+
*/
|
|
19
|
+
interface Props {
|
|
20
|
+
/** Whether the modal is open */
|
|
21
|
+
isOpen: boolean;
|
|
22
|
+
/** Target workflow ID */
|
|
23
|
+
workflowId: string;
|
|
24
|
+
/** Pre-loaded workflow (optional, will be fetched if not provided) */
|
|
25
|
+
workflow?: Workflow;
|
|
26
|
+
/** Resume a specific session */
|
|
27
|
+
initialSessionId?: string;
|
|
28
|
+
/** API endpoint configuration */
|
|
29
|
+
endpointConfig?: EndpointConfig;
|
|
30
|
+
/** Playground configuration options */
|
|
31
|
+
config?: PlaygroundConfig;
|
|
32
|
+
/** Callback when modal is closed */
|
|
33
|
+
onClose: () => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
isOpen,
|
|
38
|
+
workflowId,
|
|
39
|
+
workflow,
|
|
40
|
+
initialSessionId,
|
|
41
|
+
endpointConfig,
|
|
42
|
+
config = {},
|
|
43
|
+
onClose
|
|
44
|
+
}: Props = $props();
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Close modal on Escape key
|
|
48
|
+
*/
|
|
49
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
50
|
+
if (event.key === "Escape") {
|
|
51
|
+
onClose();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Close modal when clicking outside (on backdrop)
|
|
57
|
+
*/
|
|
58
|
+
function handleBackdropClick(event: MouseEvent): void {
|
|
59
|
+
if (event.target === event.currentTarget) {
|
|
60
|
+
onClose();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
{#if isOpen}
|
|
66
|
+
<!-- Modal Backdrop -->
|
|
67
|
+
<div
|
|
68
|
+
class="playground-modal-backdrop"
|
|
69
|
+
onclick={handleBackdropClick}
|
|
70
|
+
onkeydown={handleKeydown}
|
|
71
|
+
role="dialog"
|
|
72
|
+
aria-modal="true"
|
|
73
|
+
aria-labelledby="playground-modal-title"
|
|
74
|
+
tabindex="-1"
|
|
75
|
+
>
|
|
76
|
+
<!-- Modal Container -->
|
|
77
|
+
<div class="playground-modal" onclick={(e) => e.stopPropagation()}>
|
|
78
|
+
<!-- Modal Header -->
|
|
79
|
+
<div class="playground-modal__header">
|
|
80
|
+
<div class="playground-modal__title" id="playground-modal-title">
|
|
81
|
+
<Icon icon="mdi:play-circle-outline" />
|
|
82
|
+
<span>Playground</span>
|
|
83
|
+
</div>
|
|
84
|
+
<button
|
|
85
|
+
type="button"
|
|
86
|
+
class="playground-modal__close-btn"
|
|
87
|
+
onclick={onClose}
|
|
88
|
+
aria-label="Close playground modal"
|
|
89
|
+
>
|
|
90
|
+
<Icon icon="mdi:close" />
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<!-- Modal Content -->
|
|
95
|
+
<div class="playground-modal__content">
|
|
96
|
+
<Playground
|
|
97
|
+
{workflowId}
|
|
98
|
+
{workflow}
|
|
99
|
+
mode="modal"
|
|
100
|
+
{initialSessionId}
|
|
101
|
+
{endpointConfig}
|
|
102
|
+
{config}
|
|
103
|
+
{onClose}
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
{/if}
|
|
109
|
+
|
|
110
|
+
<style>
|
|
111
|
+
.playground-modal-backdrop {
|
|
112
|
+
position: fixed;
|
|
113
|
+
top: 0;
|
|
114
|
+
left: 0;
|
|
115
|
+
right: 0;
|
|
116
|
+
bottom: 0;
|
|
117
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
justify-content: center;
|
|
121
|
+
z-index: 1100;
|
|
122
|
+
padding: 1rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.playground-modal {
|
|
126
|
+
background: white;
|
|
127
|
+
border-radius: 0.75rem;
|
|
128
|
+
box-shadow:
|
|
129
|
+
0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
|
130
|
+
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
131
|
+
width: 100%;
|
|
132
|
+
max-width: 90vw;
|
|
133
|
+
min-width: 800px;
|
|
134
|
+
max-height: 90vh;
|
|
135
|
+
display: flex;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
overflow: hidden;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.playground-modal__header {
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
justify-content: space-between;
|
|
144
|
+
padding: 1rem 1.25rem;
|
|
145
|
+
border-bottom: 1px solid #e5e7eb;
|
|
146
|
+
background-color: #fafbfc;
|
|
147
|
+
flex-shrink: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.playground-modal__title {
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: 0.5rem;
|
|
154
|
+
font-size: 1rem;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
color: #1f2937;
|
|
157
|
+
margin: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.playground-modal__close-btn {
|
|
161
|
+
display: flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
width: 2rem;
|
|
165
|
+
height: 2rem;
|
|
166
|
+
border: none;
|
|
167
|
+
background: transparent;
|
|
168
|
+
border-radius: 0.375rem;
|
|
169
|
+
color: #6b7280;
|
|
170
|
+
cursor: pointer;
|
|
171
|
+
transition: all 0.2s;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.playground-modal__close-btn:hover {
|
|
175
|
+
background-color: #f3f4f6;
|
|
176
|
+
color: #374151;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.playground-modal__content {
|
|
180
|
+
flex: 1;
|
|
181
|
+
min-height: 0;
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
padding: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Responsive adjustments */
|
|
189
|
+
@media (max-width: 1024px) {
|
|
190
|
+
.playground-modal {
|
|
191
|
+
max-width: 95vw;
|
|
192
|
+
min-width: 600px;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@media (max-width: 768px) {
|
|
197
|
+
.playground-modal {
|
|
198
|
+
max-width: 100%;
|
|
199
|
+
min-width: auto;
|
|
200
|
+
max-height: 100vh;
|
|
201
|
+
border-radius: 0;
|
|
202
|
+
margin: 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.playground-modal-backdrop {
|
|
206
|
+
padding: 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.playground-modal__header {
|
|
210
|
+
padding: 0.875rem 1rem;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@media (max-width: 640px) {
|
|
215
|
+
.playground-modal {
|
|
216
|
+
max-width: 100%;
|
|
217
|
+
max-height: 100vh;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Workflow } from "../../types/index.js";
|
|
2
|
+
import type { EndpointConfig } from "../../config/endpoints.js";
|
|
3
|
+
import type { PlaygroundConfig } from "../../types/playground.js";
|
|
4
|
+
/**
|
|
5
|
+
* Component props
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
/** Whether the modal is open */
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
/** Target workflow ID */
|
|
11
|
+
workflowId: string;
|
|
12
|
+
/** Pre-loaded workflow (optional, will be fetched if not provided) */
|
|
13
|
+
workflow?: Workflow;
|
|
14
|
+
/** Resume a specific session */
|
|
15
|
+
initialSessionId?: string;
|
|
16
|
+
/** API endpoint configuration */
|
|
17
|
+
endpointConfig?: EndpointConfig;
|
|
18
|
+
/** Playground configuration options */
|
|
19
|
+
config?: PlaygroundConfig;
|
|
20
|
+
/** Callback when modal is closed */
|
|
21
|
+
onClose: () => void;
|
|
22
|
+
}
|
|
23
|
+
declare const PlaygroundModal: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type PlaygroundModal = ReturnType<typeof PlaygroundModal>;
|
|
25
|
+
export default PlaygroundModal;
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export { default as PipelineStatus } from '../components/PipelineStatus.svelte';
|
|
|
54
54
|
export { default as Navbar } from '../components/Navbar.svelte';
|
|
55
55
|
export { default as Logo } from '../components/Logo.svelte';
|
|
56
56
|
export { default as Playground } from '../components/playground/Playground.svelte';
|
|
57
|
+
export { default as PlaygroundModal } from '../components/playground/PlaygroundModal.svelte';
|
|
57
58
|
export { default as ChatPanel } from '../components/playground/ChatPanel.svelte';
|
|
58
59
|
export { default as SessionManager } from '../components/playground/SessionManager.svelte';
|
|
59
60
|
export { default as InputCollector } from '../components/playground/InputCollector.svelte';
|
package/dist/editor/index.js
CHANGED
|
@@ -69,6 +69,7 @@ export { default as Navbar } from '../components/Navbar.svelte';
|
|
|
69
69
|
export { default as Logo } from '../components/Logo.svelte';
|
|
70
70
|
// Playground Components
|
|
71
71
|
export { default as Playground } from '../components/playground/Playground.svelte';
|
|
72
|
+
export { default as PlaygroundModal } from '../components/playground/PlaygroundModal.svelte';
|
|
72
73
|
export { default as ChatPanel } from '../components/playground/ChatPanel.svelte';
|
|
73
74
|
export { default as SessionManager } from '../components/playground/SessionManager.svelte';
|
|
74
75
|
export { default as InputCollector } from '../components/playground/InputCollector.svelte';
|
|
@@ -77,8 +77,41 @@
|
|
|
77
77
|
* />
|
|
78
78
|
* {/if}
|
|
79
79
|
* ```
|
|
80
|
+
*
|
|
81
|
+
* @example In Svelte (Modal mode):
|
|
82
|
+
* ```svelte
|
|
83
|
+
* <script>
|
|
84
|
+
* import { PlaygroundModal } from "@d34dman/flowdrop/playground";
|
|
85
|
+
* let showPlayground = false;
|
|
86
|
+
* </script>
|
|
87
|
+
*
|
|
88
|
+
* <PlaygroundModal
|
|
89
|
+
* isOpen={showPlayground}
|
|
90
|
+
* workflowId="wf-123"
|
|
91
|
+
* workflow={myWorkflow}
|
|
92
|
+
* onClose={() => showPlayground = false}
|
|
93
|
+
* />
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example Using mountPlayground with modal mode:
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import { mountPlayground, createEndpointConfig } from "@d34dman/flowdrop/playground";
|
|
99
|
+
*
|
|
100
|
+
* const app = await mountPlayground(
|
|
101
|
+
* document.getElementById("playground-container"),
|
|
102
|
+
* {
|
|
103
|
+
* workflowId: "wf-123",
|
|
104
|
+
* endpointConfig: createEndpointConfig("/api/flowdrop"),
|
|
105
|
+
* mode: "modal",
|
|
106
|
+
* onClose: () => {
|
|
107
|
+
* app.destroy();
|
|
108
|
+
* }
|
|
109
|
+
* }
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
80
112
|
*/
|
|
81
113
|
export { default as Playground } from '../components/playground/Playground.svelte';
|
|
114
|
+
export { default as PlaygroundModal } from '../components/playground/PlaygroundModal.svelte';
|
|
82
115
|
export { default as ChatPanel } from '../components/playground/ChatPanel.svelte';
|
|
83
116
|
export { default as SessionManager } from '../components/playground/SessionManager.svelte';
|
|
84
117
|
export { default as InputCollector } from '../components/playground/InputCollector.svelte';
|
package/dist/playground/index.js
CHANGED
|
@@ -77,11 +77,44 @@
|
|
|
77
77
|
* />
|
|
78
78
|
* {/if}
|
|
79
79
|
* ```
|
|
80
|
+
*
|
|
81
|
+
* @example In Svelte (Modal mode):
|
|
82
|
+
* ```svelte
|
|
83
|
+
* <script>
|
|
84
|
+
* import { PlaygroundModal } from "@d34dman/flowdrop/playground";
|
|
85
|
+
* let showPlayground = false;
|
|
86
|
+
* </script>
|
|
87
|
+
*
|
|
88
|
+
* <PlaygroundModal
|
|
89
|
+
* isOpen={showPlayground}
|
|
90
|
+
* workflowId="wf-123"
|
|
91
|
+
* workflow={myWorkflow}
|
|
92
|
+
* onClose={() => showPlayground = false}
|
|
93
|
+
* />
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example Using mountPlayground with modal mode:
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import { mountPlayground, createEndpointConfig } from "@d34dman/flowdrop/playground";
|
|
99
|
+
*
|
|
100
|
+
* const app = await mountPlayground(
|
|
101
|
+
* document.getElementById("playground-container"),
|
|
102
|
+
* {
|
|
103
|
+
* workflowId: "wf-123",
|
|
104
|
+
* endpointConfig: createEndpointConfig("/api/flowdrop"),
|
|
105
|
+
* mode: "modal",
|
|
106
|
+
* onClose: () => {
|
|
107
|
+
* app.destroy();
|
|
108
|
+
* }
|
|
109
|
+
* }
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
80
112
|
*/
|
|
81
113
|
// ============================================================================
|
|
82
114
|
// Playground Components
|
|
83
115
|
// ============================================================================
|
|
84
116
|
export { default as Playground } from '../components/playground/Playground.svelte';
|
|
117
|
+
export { default as PlaygroundModal } from '../components/playground/PlaygroundModal.svelte';
|
|
85
118
|
export { default as ChatPanel } from '../components/playground/ChatPanel.svelte';
|
|
86
119
|
export { default as SessionManager } from '../components/playground/SessionManager.svelte';
|
|
87
120
|
export { default as InputCollector } from '../components/playground/InputCollector.svelte';
|
|
@@ -64,6 +64,7 @@ export interface PlaygroundMountOptions {
|
|
|
64
64
|
* Display mode
|
|
65
65
|
* - "standalone": Full-page playground experience
|
|
66
66
|
* - "embedded": Panel mode for embedding alongside other content
|
|
67
|
+
* - "modal": Modal dialog mode with backdrop
|
|
67
68
|
* @default "standalone"
|
|
68
69
|
*/
|
|
69
70
|
mode?: PlaygroundMode;
|
|
@@ -92,7 +93,7 @@ export interface PlaygroundMountOptions {
|
|
|
92
93
|
*/
|
|
93
94
|
width?: string;
|
|
94
95
|
/**
|
|
95
|
-
* Callback when playground is closed (for embedded
|
|
96
|
+
* Callback when playground is closed (required for embedded and modal modes)
|
|
96
97
|
*/
|
|
97
98
|
onClose?: () => void;
|
|
98
99
|
}
|
package/dist/playground/mount.js
CHANGED
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
*/
|
|
46
46
|
import { mount, unmount } from "svelte";
|
|
47
47
|
import Playground from "../components/playground/Playground.svelte";
|
|
48
|
+
import PlaygroundModal from "../components/playground/PlaygroundModal.svelte";
|
|
48
49
|
import { setEndpointConfig } from "../services/api.js";
|
|
49
50
|
import { playgroundService } from "../services/playgroundService.js";
|
|
50
51
|
import { currentSession, sessions, messages, playgroundActions } from "../stores/playgroundStore.js";
|
|
@@ -87,6 +88,10 @@ export async function mountPlayground(container, options) {
|
|
|
87
88
|
if (!container) {
|
|
88
89
|
throw new Error("container element is required for mountPlayground()");
|
|
89
90
|
}
|
|
91
|
+
// Validate onClose for modal mode
|
|
92
|
+
if (mode === "modal" && !onClose) {
|
|
93
|
+
throw new Error("onClose callback is required for modal mode");
|
|
94
|
+
}
|
|
90
95
|
// Set endpoint configuration if provided
|
|
91
96
|
let finalEndpointConfig;
|
|
92
97
|
if (endpointConfig) {
|
|
@@ -102,26 +107,52 @@ export async function mountPlayground(container, options) {
|
|
|
102
107
|
};
|
|
103
108
|
setEndpointConfig(finalEndpointConfig);
|
|
104
109
|
}
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
// Handle modal mode differently
|
|
111
|
+
// For modal mode, PlaygroundModal creates its own backdrop, so we mount directly to body
|
|
112
|
+
// For other modes, use the provided container
|
|
113
|
+
let targetContainer = container;
|
|
114
|
+
if (mode === "modal") {
|
|
115
|
+
// For modal mode, create a container in the body
|
|
116
|
+
// PlaygroundModal will handle the backdrop itself
|
|
117
|
+
targetContainer = document.body;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Apply container styling for non-modal modes
|
|
121
|
+
container.style.height = height;
|
|
122
|
+
container.style.width = width;
|
|
123
|
+
}
|
|
124
|
+
// Mount the appropriate component
|
|
125
|
+
const svelteApp = mount(mode === "modal" ? PlaygroundModal : Playground, {
|
|
126
|
+
target: targetContainer,
|
|
127
|
+
props: mode === "modal"
|
|
128
|
+
? {
|
|
129
|
+
isOpen: true,
|
|
130
|
+
workflowId,
|
|
131
|
+
workflow,
|
|
132
|
+
initialSessionId,
|
|
133
|
+
endpointConfig: finalEndpointConfig,
|
|
134
|
+
config,
|
|
135
|
+
onClose: () => {
|
|
136
|
+
if (onClose) {
|
|
137
|
+
onClose();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
: {
|
|
142
|
+
workflowId,
|
|
143
|
+
workflow,
|
|
144
|
+
mode,
|
|
145
|
+
initialSessionId,
|
|
146
|
+
endpointConfig: finalEndpointConfig,
|
|
147
|
+
config,
|
|
148
|
+
onClose
|
|
149
|
+
}
|
|
120
150
|
});
|
|
121
151
|
// Store state for cleanup
|
|
122
152
|
const state = {
|
|
123
153
|
svelteApp,
|
|
124
|
-
container,
|
|
154
|
+
container: targetContainer,
|
|
155
|
+
originalContainer: mode === "modal" ? container : undefined,
|
|
125
156
|
workflowId
|
|
126
157
|
};
|
|
127
158
|
// Create the mounted playground interface
|
|
@@ -263,8 +263,11 @@ export class PlaygroundService {
|
|
|
263
263
|
this.currentBackoff = interval;
|
|
264
264
|
// Call the callback with new messages
|
|
265
265
|
callback(response);
|
|
266
|
-
// Stop polling if session is completed or failed
|
|
267
|
-
|
|
266
|
+
// Stop polling if session is idle, completed, or failed
|
|
267
|
+
// "idle" means no processing is happening (execution finished)
|
|
268
|
+
if (response.sessionStatus === 'idle' ||
|
|
269
|
+
response.sessionStatus === 'completed' ||
|
|
270
|
+
response.sessionStatus === 'failed') {
|
|
268
271
|
this.stopPolling();
|
|
269
272
|
return;
|
|
270
273
|
}
|
|
@@ -178,11 +178,13 @@ export interface PlaygroundConfig {
|
|
|
178
178
|
showTimestamps?: boolean;
|
|
179
179
|
/** Show log messages inline or in collapsible section (default: "collapsible") */
|
|
180
180
|
logDisplayMode?: 'inline' | 'collapsible';
|
|
181
|
+
/** Enable markdown rendering in messages (default: true) */
|
|
182
|
+
enableMarkdown?: boolean;
|
|
181
183
|
}
|
|
182
184
|
/**
|
|
183
185
|
* Display mode for the Playground component
|
|
184
186
|
*/
|
|
185
|
-
export type PlaygroundMode = 'embedded' | 'standalone';
|
|
187
|
+
export type PlaygroundMode = 'embedded' | 'standalone' | 'modal';
|
|
186
188
|
/**
|
|
187
189
|
* Chat input detection patterns for identifying chat nodes in workflows
|
|
188
190
|
*/
|