@flowdrop/flowdrop 1.8.0 → 1.9.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.
- package/dist/chat/responseParser.js +7 -0
- package/dist/commands/parser.js +12 -0
- package/dist/components/PipelineStatus.svelte +15 -8
- package/dist/components/PipelineStatus.svelte.d.ts +3 -0
- package/dist/components/chat/AIChatPanel.svelte +22 -1
- package/dist/components/playground/ChatPanel.svelte +149 -24
- package/dist/components/playground/ChatPanel.svelte.d.ts +3 -1
- package/dist/components/playground/ExecutionList.svelte +142 -0
- package/dist/components/playground/ExecutionList.svelte.d.ts +10 -0
- package/dist/components/playground/MessageBubble.svelte +221 -153
- package/dist/components/playground/PipelinePanel.svelte +372 -0
- package/dist/components/playground/PipelinePanel.svelte.d.ts +19 -0
- package/dist/components/playground/Playground.svelte +586 -103
- package/dist/components/playground/Playground.svelte.d.ts +6 -0
- package/dist/services/globalSave.d.ts +7 -0
- package/dist/services/globalSave.js +5 -1
- package/dist/stores/pipelinePanelStore.svelte.d.ts +6 -0
- package/dist/stores/pipelinePanelStore.svelte.js +24 -0
- package/dist/stores/playgroundStore.svelte.d.ts +4 -0
- package/dist/stores/playgroundStore.svelte.js +58 -0
- package/dist/svelte-app.js +25 -2
- package/dist/types/playground.d.ts +12 -0
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { onMount, onDestroy } from 'svelte';
|
|
12
12
|
import Icon from '@iconify/svelte';
|
|
13
13
|
import ChatPanel from './ChatPanel.svelte';
|
|
14
|
+
import ExecutionList from './ExecutionList.svelte';
|
|
14
15
|
import type { Workflow } from '../../types/index.js';
|
|
15
16
|
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
16
17
|
import type { PlaygroundMode, PlaygroundConfig } from '../../types/playground.js';
|
|
@@ -25,7 +26,10 @@
|
|
|
25
26
|
getError,
|
|
26
27
|
playgroundActions,
|
|
27
28
|
getInputFields,
|
|
28
|
-
createPollingCallback
|
|
29
|
+
createPollingCallback,
|
|
30
|
+
getActiveExecutionId,
|
|
31
|
+
getLatestExecutionId,
|
|
32
|
+
getPinnedExecutionId,
|
|
29
33
|
} from '../../stores/playgroundStore.svelte.js';
|
|
30
34
|
import { interruptActions } from '../../stores/interruptStore.svelte.js';
|
|
31
35
|
import { logger } from '../../utils/logger.js';
|
|
@@ -49,6 +53,12 @@
|
|
|
49
53
|
config?: PlaygroundConfig;
|
|
50
54
|
/** Callback when playground is closed (for embedded mode) */
|
|
51
55
|
onClose?: () => void;
|
|
56
|
+
/** Callback to toggle the pipeline panel (if undefined, toggle button is hidden) */
|
|
57
|
+
onTogglePanel?: () => void;
|
|
58
|
+
/** Whether the pipeline panel is currently open (for toggle button active state) */
|
|
59
|
+
isPipelinePanelOpen?: boolean;
|
|
60
|
+
/** When provided, session switches and creation navigate to a URL instead of mutating store state */
|
|
61
|
+
onSessionNavigate?: (sessionId: string) => void;
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
let {
|
|
@@ -58,7 +68,10 @@
|
|
|
58
68
|
initialSessionId,
|
|
59
69
|
endpointConfig,
|
|
60
70
|
config = {},
|
|
61
|
-
onClose
|
|
71
|
+
onClose,
|
|
72
|
+
onTogglePanel,
|
|
73
|
+
isPipelinePanelOpen = false,
|
|
74
|
+
onSessionNavigate,
|
|
62
75
|
}: Props = $props();
|
|
63
76
|
|
|
64
77
|
/** Current input values from InputCollector */
|
|
@@ -70,6 +83,9 @@
|
|
|
70
83
|
/** Track which session's dropdown menu is open */
|
|
71
84
|
let openMenuId = $state<string | null>(null);
|
|
72
85
|
|
|
86
|
+
/** Whether the runs sub-section is expanded under the active session */
|
|
87
|
+
let runsExpanded = $state(false);
|
|
88
|
+
|
|
73
89
|
/** Track if initial session has been loaded to prevent duplicate loads */
|
|
74
90
|
let initialSessionLoaded = $state(false);
|
|
75
91
|
|
|
@@ -79,6 +95,24 @@
|
|
|
79
95
|
/** Track if auto-run has already been triggered to prevent duplicate executions */
|
|
80
96
|
let autoRunTriggered = $state(false);
|
|
81
97
|
|
|
98
|
+
/** Whether log messages are visible in the chat panel */
|
|
99
|
+
let showLogs = $state(true);
|
|
100
|
+
|
|
101
|
+
/** Whether the session switcher popover is open (standalone mode) */
|
|
102
|
+
let sessionDropdownOpen = $state(false);
|
|
103
|
+
|
|
104
|
+
// Close session popover on outside click
|
|
105
|
+
$effect(() => {
|
|
106
|
+
if (!sessionDropdownOpen) return;
|
|
107
|
+
function handleOutside(e: MouseEvent) {
|
|
108
|
+
if (!(e.target as HTMLElement).closest('.playground__session-chip-wrap')) {
|
|
109
|
+
sessionDropdownOpen = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
document.addEventListener('click', handleOutside);
|
|
113
|
+
return () => document.removeEventListener('click', handleOutside);
|
|
114
|
+
});
|
|
115
|
+
|
|
82
116
|
/**
|
|
83
117
|
* Initialize the playground on mount
|
|
84
118
|
*/
|
|
@@ -262,6 +296,13 @@
|
|
|
262
296
|
try {
|
|
263
297
|
const sessionName = `Session ${getSessions().length + 1}`;
|
|
264
298
|
const session = await playgroundService.createSession(workflowId, sessionName);
|
|
299
|
+
|
|
300
|
+
if (onSessionNavigate) {
|
|
301
|
+
// URL-based routing: navigate to the new session; page remount handles store init
|
|
302
|
+
onSessionNavigate(session.id);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
265
306
|
playgroundActions.addSession(session);
|
|
266
307
|
playgroundActions.setCurrentSession(session);
|
|
267
308
|
playgroundActions.clearMessages();
|
|
@@ -278,6 +319,8 @@
|
|
|
278
319
|
* Select a session
|
|
279
320
|
*/
|
|
280
321
|
async function handleSelectSession(sessionId: string): Promise<void> {
|
|
322
|
+
playgroundActions.pinExecution(null);
|
|
323
|
+
runsExpanded = false;
|
|
281
324
|
const currentSessionId = getCurrentSession()?.id;
|
|
282
325
|
if (currentSessionId === sessionId) {
|
|
283
326
|
return;
|
|
@@ -488,8 +531,8 @@
|
|
|
488
531
|
class:playground--no-sidebar={config.showSidebar === false}
|
|
489
532
|
>
|
|
490
533
|
<div class="playground__container">
|
|
491
|
-
<!-- Sidebar (
|
|
492
|
-
{#if config.showSidebar !== false}
|
|
534
|
+
<!-- Sidebar — hidden in standalone mode (session switcher lives in the header chip instead) -->
|
|
535
|
+
{#if config.showSidebar === true || (config.showSidebar !== false && mode !== 'standalone')}
|
|
493
536
|
<aside
|
|
494
537
|
class="playground__sidebar"
|
|
495
538
|
style={config.sidebarWidth ? `--fd-playground-sidebar-width: ${config.sidebarWidth}` : ''}
|
|
@@ -499,6 +542,13 @@
|
|
|
499
542
|
<div class="playground__sidebar-title">
|
|
500
543
|
<span>Playground</span>
|
|
501
544
|
</div>
|
|
545
|
+
<a
|
|
546
|
+
href="/workflow/{workflowId}/edit"
|
|
547
|
+
class="playground__edit-link"
|
|
548
|
+
title="Edit workflow"
|
|
549
|
+
>
|
|
550
|
+
<Icon icon="mdi:pencil-outline" />
|
|
551
|
+
</a>
|
|
502
552
|
{#if (mode === 'embedded' || mode === 'modal') && onClose}
|
|
503
553
|
<button
|
|
504
554
|
type="button"
|
|
@@ -515,24 +565,24 @@
|
|
|
515
565
|
{/if}
|
|
516
566
|
</div>
|
|
517
567
|
|
|
518
|
-
<!--
|
|
568
|
+
<!-- Sessions Section -->
|
|
519
569
|
<div class="playground__section">
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
class="
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
570
|
+
<!-- Section header with inline add button -->
|
|
571
|
+
<div class="playground__section-header">
|
|
572
|
+
<span class="playground__section-label">Sessions</span>
|
|
573
|
+
<button
|
|
574
|
+
type="button"
|
|
575
|
+
class="playground__section-add"
|
|
576
|
+
onclick={handleCreateSession}
|
|
577
|
+
disabled={getIsLoading()}
|
|
578
|
+
title="New session"
|
|
579
|
+
>
|
|
580
|
+
<Icon icon="mdi:plus" />
|
|
581
|
+
</button>
|
|
582
|
+
</div>
|
|
530
583
|
|
|
531
|
-
<!-- Sessions List
|
|
584
|
+
<!-- Sessions List -->
|
|
532
585
|
<div class="playground__sessions-wrap">
|
|
533
|
-
{#if getSessions().length > 0}
|
|
534
|
-
<p class="playground__sessions-hint">Click a session to load it</p>
|
|
535
|
-
{/if}
|
|
536
586
|
<div class="playground__sessions">
|
|
537
587
|
{#if getSessions().length === 0 && !getIsLoading()}
|
|
538
588
|
<div class="playground__sessions-empty">
|
|
@@ -540,42 +590,75 @@
|
|
|
540
590
|
</div>
|
|
541
591
|
{:else}
|
|
542
592
|
{#each getSessions() as session (session.id)}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
593
|
+
{@const isActive = getCurrentSession()?.id === session.id}
|
|
594
|
+
<div class="playground__session-group">
|
|
595
|
+
<div
|
|
596
|
+
class="playground__session"
|
|
597
|
+
class:playground__session--active={isActive}
|
|
598
|
+
role="button"
|
|
599
|
+
tabindex="0"
|
|
600
|
+
title="Click to load this session"
|
|
601
|
+
aria-label={m().layout.loadSession({ name: session.name })}
|
|
602
|
+
onclick={() => handleSelectSession(session.id)}
|
|
603
|
+
onkeydown={(e) => e.key === 'Enter' && handleSelectSession(session.id)}
|
|
604
|
+
>
|
|
605
|
+
<span class="playground__session-name" title={session.name}>
|
|
606
|
+
{session.name}
|
|
607
|
+
</span>
|
|
608
|
+
<div class="playground__session-actions">
|
|
609
|
+
<button
|
|
610
|
+
type="button"
|
|
611
|
+
class="playground__session-menu"
|
|
612
|
+
class:playground__session-menu--open={openMenuId === session.id}
|
|
613
|
+
onclick={(e) => handleMenuToggle(e, session.id)}
|
|
614
|
+
title="Session options"
|
|
615
|
+
>
|
|
616
|
+
<Icon icon="mdi:dots-vertical" />
|
|
617
|
+
</button>
|
|
618
|
+
{#if openMenuId === session.id}
|
|
619
|
+
<div class="playground__session-dropdown">
|
|
620
|
+
<button
|
|
621
|
+
type="button"
|
|
622
|
+
class="playground__session-dropdown-item playground__session-dropdown-item--danger"
|
|
623
|
+
onclick={(e) => handleMenuDelete(e, session.id)}
|
|
624
|
+
>
|
|
625
|
+
<Icon icon="mdi:delete-outline" />
|
|
626
|
+
<span>Delete</span>
|
|
627
|
+
</button>
|
|
628
|
+
</div>
|
|
629
|
+
{/if}
|
|
630
|
+
</div>
|
|
578
631
|
</div>
|
|
632
|
+
<!-- Collapsible runs sub-section under active session -->
|
|
633
|
+
{#if isActive && getCurrentSession()?.executions?.length}
|
|
634
|
+
<div class="playground__runs-section">
|
|
635
|
+
<button
|
|
636
|
+
type="button"
|
|
637
|
+
class="playground__runs-toggle"
|
|
638
|
+
onclick={() => (runsExpanded = !runsExpanded)}
|
|
639
|
+
>
|
|
640
|
+
<Icon icon={runsExpanded ? 'mdi:chevron-down' : 'mdi:chevron-right'} />
|
|
641
|
+
<span>Runs</span>
|
|
642
|
+
<span class="playground__runs-count">{getCurrentSession()!.executions!.length}</span>
|
|
643
|
+
</button>
|
|
644
|
+
{#if runsExpanded}
|
|
645
|
+
<div class="playground__executions-inline">
|
|
646
|
+
<ExecutionList
|
|
647
|
+
executions={getCurrentSession()!.executions!}
|
|
648
|
+
activeExecutionId={getActiveExecutionId()}
|
|
649
|
+
latestExecutionId={getLatestExecutionId()}
|
|
650
|
+
onSelect={(id) => {
|
|
651
|
+
if (id === getLatestExecutionId()) {
|
|
652
|
+
playgroundActions.pinExecution(null);
|
|
653
|
+
} else {
|
|
654
|
+
playgroundActions.pinExecution(id);
|
|
655
|
+
}
|
|
656
|
+
}}
|
|
657
|
+
/>
|
|
658
|
+
</div>
|
|
659
|
+
{/if}
|
|
660
|
+
</div>
|
|
661
|
+
{/if}
|
|
579
662
|
</div>
|
|
580
663
|
{/each}
|
|
581
664
|
{/if}
|
|
@@ -587,18 +670,118 @@
|
|
|
587
670
|
|
|
588
671
|
<!-- Main Content -->
|
|
589
672
|
<main class="playground__main">
|
|
590
|
-
<!-- Session Header
|
|
591
|
-
{#if getCurrentSession() && config.showSessionHeader !== false}
|
|
673
|
+
<!-- Session Header -->
|
|
674
|
+
{#if mode === 'standalone' || (getCurrentSession() && config.showSessionHeader !== false)}
|
|
592
675
|
<header class="playground__header">
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
class="playground__header-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
676
|
+
{#if mode === 'standalone'}
|
|
677
|
+
<!-- Panel icon + label (mirrors PipelinePanel header) -->
|
|
678
|
+
<Icon icon="mdi:message-text-outline" class="playground__header-icon" />
|
|
679
|
+
<span class="playground__header-label">Sessions</span>
|
|
680
|
+
|
|
681
|
+
<!-- Session chip — switches sessions via popover -->
|
|
682
|
+
<div class="playground__session-chip-wrap">
|
|
683
|
+
<button
|
|
684
|
+
type="button"
|
|
685
|
+
class="playground__session-chip"
|
|
686
|
+
class:playground__session-chip--open={sessionDropdownOpen}
|
|
687
|
+
onclick={() => (sessionDropdownOpen = !sessionDropdownOpen)}
|
|
688
|
+
title="Switch session"
|
|
689
|
+
>
|
|
690
|
+
<span class="playground__session-chip-name">
|
|
691
|
+
{getCurrentSession()?.name ?? 'No session'}
|
|
692
|
+
</span>
|
|
693
|
+
<Icon icon={sessionDropdownOpen ? 'mdi:chevron-up' : 'mdi:chevron-down'} class="playground__session-chip-chevron" />
|
|
694
|
+
</button>
|
|
695
|
+
|
|
696
|
+
{#if sessionDropdownOpen}
|
|
697
|
+
<div class="playground__session-popover">
|
|
698
|
+
<button
|
|
699
|
+
type="button"
|
|
700
|
+
class="playground__session-popover-item playground__session-popover-item--new"
|
|
701
|
+
disabled={getIsLoading()}
|
|
702
|
+
onclick={() => { sessionDropdownOpen = false; void handleCreateSession(); }}
|
|
703
|
+
>
|
|
704
|
+
<Icon icon="mdi:plus" />
|
|
705
|
+
<span>New session</span>
|
|
706
|
+
</button>
|
|
707
|
+
{#if getSessions().length > 0}
|
|
708
|
+
<div class="playground__session-popover-divider"></div>
|
|
709
|
+
{#each getSessions() as session (session.id)}
|
|
710
|
+
{@const isActive = getCurrentSession()?.id === session.id}
|
|
711
|
+
<div class="playground__session-popover-row">
|
|
712
|
+
<button
|
|
713
|
+
type="button"
|
|
714
|
+
class="playground__session-popover-item"
|
|
715
|
+
class:playground__session-popover-item--active={isActive}
|
|
716
|
+
onclick={() => {
|
|
717
|
+
sessionDropdownOpen = false;
|
|
718
|
+
if (onSessionNavigate) {
|
|
719
|
+
onSessionNavigate(session.id);
|
|
720
|
+
} else {
|
|
721
|
+
void handleSelectSession(session.id);
|
|
722
|
+
}
|
|
723
|
+
}}
|
|
724
|
+
>
|
|
725
|
+
{#if isActive}
|
|
726
|
+
<Icon icon="mdi:check" class="playground__session-popover-check" />
|
|
727
|
+
{:else}
|
|
728
|
+
<Icon icon="mdi:message-outline" />
|
|
729
|
+
{/if}
|
|
730
|
+
<span>{session.name}</span>
|
|
731
|
+
</button>
|
|
732
|
+
<button
|
|
733
|
+
type="button"
|
|
734
|
+
class="playground__session-popover-delete"
|
|
735
|
+
onclick={(e) => { handleMenuDelete(e, session.id); sessionDropdownOpen = false; }}
|
|
736
|
+
title="Delete session"
|
|
737
|
+
>
|
|
738
|
+
<Icon icon="mdi:delete-outline" />
|
|
739
|
+
</button>
|
|
740
|
+
</div>
|
|
741
|
+
{/each}
|
|
742
|
+
{/if}
|
|
743
|
+
</div>
|
|
744
|
+
{/if}
|
|
745
|
+
</div>
|
|
746
|
+
{:else}
|
|
747
|
+
<!-- Embedded / modal: original title + close -->
|
|
748
|
+
<div class="playground__header-group">
|
|
749
|
+
<h2 class="playground__header-title">{getCurrentSession()?.name}</h2>
|
|
750
|
+
<button
|
|
751
|
+
type="button"
|
|
752
|
+
class="playground__header-close"
|
|
753
|
+
onclick={handleCloseSession}
|
|
754
|
+
title="Close session"
|
|
755
|
+
>
|
|
756
|
+
<Icon icon="mdi:close" />
|
|
757
|
+
</button>
|
|
758
|
+
</div>
|
|
759
|
+
{/if}
|
|
760
|
+
|
|
761
|
+
<div class="playground__header-actions">
|
|
762
|
+
{#if mode === 'standalone' && onTogglePanel}
|
|
763
|
+
<button
|
|
764
|
+
type="button"
|
|
765
|
+
class="playground__log-toggle"
|
|
766
|
+
class:playground__log-toggle--active={isPipelinePanelOpen}
|
|
767
|
+
onclick={onTogglePanel}
|
|
768
|
+
title={isPipelinePanelOpen ? 'Hide pipeline' : 'Show pipeline'}
|
|
769
|
+
>
|
|
770
|
+
<Icon icon="mdi:source-branch" />
|
|
771
|
+
Pipeline
|
|
772
|
+
</button>
|
|
773
|
+
{/if}
|
|
774
|
+
<button
|
|
775
|
+
type="button"
|
|
776
|
+
class="playground__log-toggle"
|
|
777
|
+
class:playground__log-toggle--active={showLogs}
|
|
778
|
+
onclick={() => (showLogs = !showLogs)}
|
|
779
|
+
title={showLogs ? 'Hide log messages' : 'Show log messages'}
|
|
780
|
+
>
|
|
781
|
+
<Icon icon="mdi:console" />
|
|
782
|
+
Logs
|
|
783
|
+
</button>
|
|
784
|
+
</div>
|
|
602
785
|
</header>
|
|
603
786
|
{/if}
|
|
604
787
|
|
|
@@ -636,6 +819,7 @@
|
|
|
636
819
|
onSendMessage={handleSendMessage}
|
|
637
820
|
onStopExecution={handleStopExecution}
|
|
638
821
|
onInterruptResolved={handleInterruptResolved}
|
|
822
|
+
bind:showLogs
|
|
639
823
|
/>
|
|
640
824
|
{/if}
|
|
641
825
|
</div>
|
|
@@ -691,6 +875,7 @@
|
|
|
691
875
|
width: var(--fd-playground-sidebar-width);
|
|
692
876
|
background-color: var(--fd-background);
|
|
693
877
|
border-right: 1px solid var(--fd-border);
|
|
878
|
+
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
|
|
694
879
|
display: flex;
|
|
695
880
|
flex-direction: column;
|
|
696
881
|
}
|
|
@@ -717,6 +902,24 @@
|
|
|
717
902
|
color: var(--fd-foreground);
|
|
718
903
|
}
|
|
719
904
|
|
|
905
|
+
.playground__edit-link {
|
|
906
|
+
display: flex;
|
|
907
|
+
align-items: center;
|
|
908
|
+
justify-content: center;
|
|
909
|
+
width: var(--fd-playground-icon-btn-size);
|
|
910
|
+
height: var(--fd-playground-icon-btn-size);
|
|
911
|
+
border-radius: var(--fd-radius-md);
|
|
912
|
+
color: var(--fd-muted-foreground);
|
|
913
|
+
text-decoration: none;
|
|
914
|
+
transition: all var(--fd-transition-fast);
|
|
915
|
+
flex-shrink: 0;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
.playground__edit-link:hover {
|
|
919
|
+
background-color: var(--fd-muted);
|
|
920
|
+
color: var(--fd-foreground);
|
|
921
|
+
}
|
|
922
|
+
|
|
720
923
|
.playground__sidebar-close {
|
|
721
924
|
display: flex;
|
|
722
925
|
align-items: center;
|
|
@@ -742,51 +945,119 @@
|
|
|
742
945
|
display: flex;
|
|
743
946
|
flex-direction: column;
|
|
744
947
|
min-height: 0;
|
|
745
|
-
padding: var(--fd-space-md)
|
|
948
|
+
padding: 0 var(--fd-space-md);
|
|
746
949
|
}
|
|
747
950
|
|
|
748
|
-
/*
|
|
749
|
-
.
|
|
951
|
+
/* Section header: label + add icon */
|
|
952
|
+
.playground__section-header {
|
|
953
|
+
display: flex;
|
|
954
|
+
align-items: center;
|
|
955
|
+
justify-content: space-between;
|
|
956
|
+
padding: var(--fd-space-md) var(--fd-space-xs) var(--fd-space-xs);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.playground__section-label {
|
|
960
|
+
font-size: var(--fd-text-xs);
|
|
961
|
+
font-weight: 600;
|
|
962
|
+
color: var(--fd-muted-foreground);
|
|
963
|
+
text-transform: uppercase;
|
|
964
|
+
letter-spacing: 0.06em;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.playground__section-add {
|
|
750
968
|
display: flex;
|
|
751
969
|
align-items: center;
|
|
752
970
|
justify-content: center;
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
border: 1px solid var(--fd-border);
|
|
971
|
+
width: var(--fd-playground-icon-btn-size);
|
|
972
|
+
height: var(--fd-playground-icon-btn-size);
|
|
973
|
+
border: none;
|
|
757
974
|
border-radius: var(--fd-radius-md);
|
|
758
|
-
background
|
|
975
|
+
background: transparent;
|
|
976
|
+
color: var(--fd-muted-foreground);
|
|
977
|
+
cursor: pointer;
|
|
978
|
+
transition: all var(--fd-transition-fast);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.playground__section-add:hover:not(:disabled) {
|
|
982
|
+
background-color: var(--fd-muted);
|
|
759
983
|
color: var(--fd-foreground);
|
|
760
|
-
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.playground__section-add:disabled {
|
|
987
|
+
opacity: 0.4;
|
|
988
|
+
cursor: not-allowed;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/* Session group wraps session row + its inline runs */
|
|
992
|
+
.playground__session-group {
|
|
993
|
+
margin-bottom: var(--fd-space-3xs);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/* Collapsible runs sub-section under active session */
|
|
997
|
+
.playground__runs-section {
|
|
998
|
+
margin-bottom: var(--fd-space-3xs);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.playground__runs-toggle {
|
|
1002
|
+
display: flex;
|
|
1003
|
+
align-items: center;
|
|
1004
|
+
gap: var(--fd-space-3xs);
|
|
1005
|
+
width: 100%;
|
|
1006
|
+
padding: var(--fd-space-3xs) var(--fd-space-sm);
|
|
1007
|
+
padding-left: calc(var(--fd-space-md) + var(--fd-space-3xs));
|
|
1008
|
+
border: none;
|
|
1009
|
+
border-radius: var(--fd-radius-sm);
|
|
1010
|
+
background: transparent;
|
|
1011
|
+
color: var(--fd-muted-foreground);
|
|
1012
|
+
font-size: var(--fd-text-xs);
|
|
761
1013
|
font-weight: 500;
|
|
762
1014
|
cursor: pointer;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
border-color var(--fd-transition-fast),
|
|
766
|
-
transform 0.1s ease;
|
|
767
|
-
box-sizing: border-box;
|
|
1015
|
+
text-align: left;
|
|
1016
|
+
transition: all var(--fd-transition-fast);
|
|
768
1017
|
}
|
|
769
1018
|
|
|
770
|
-
.
|
|
1019
|
+
.playground__runs-toggle:hover {
|
|
771
1020
|
background-color: var(--fd-muted);
|
|
772
|
-
|
|
773
|
-
|
|
1021
|
+
color: var(--fd-foreground);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.playground__runs-toggle :global(svg) {
|
|
1025
|
+
width: 0.875rem;
|
|
1026
|
+
height: 0.875rem;
|
|
1027
|
+
flex-shrink: 0;
|
|
774
1028
|
}
|
|
775
1029
|
|
|
776
|
-
.
|
|
777
|
-
|
|
778
|
-
|
|
1030
|
+
.playground__runs-count {
|
|
1031
|
+
margin-left: auto;
|
|
1032
|
+
font-size: var(--fd-text-2xs);
|
|
1033
|
+
font-weight: 600;
|
|
1034
|
+
color: var(--fd-muted-foreground);
|
|
1035
|
+
background: var(--fd-muted);
|
|
1036
|
+
border-radius: 999px;
|
|
1037
|
+
padding: 1px var(--fd-space-xs);
|
|
1038
|
+
min-width: 1.4em;
|
|
1039
|
+
text-align: center;
|
|
1040
|
+
line-height: 1.4;
|
|
779
1041
|
}
|
|
780
1042
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
1043
|
+
/* Inline runs tree under active session */
|
|
1044
|
+
.playground__executions-inline {
|
|
1045
|
+
margin-left: calc(var(--fd-space-md) + var(--fd-space-xs));
|
|
1046
|
+
margin-bottom: var(--fd-space-xs);
|
|
1047
|
+
border-left: 2px solid var(--fd-border);
|
|
1048
|
+
padding-left: var(--fd-space-xs);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.playground__executions-inline :global(.execution-list__item) {
|
|
1052
|
+
padding: var(--fd-space-xs) var(--fd-space-sm);
|
|
1053
|
+
font-size: var(--fd-text-xs);
|
|
1054
|
+
border-radius: var(--fd-radius-sm);
|
|
1055
|
+
border-left-width: 2px;
|
|
785
1056
|
}
|
|
786
1057
|
|
|
787
|
-
.
|
|
788
|
-
|
|
789
|
-
|
|
1058
|
+
.playground__executions-inline :global(.execution-list) {
|
|
1059
|
+
gap: 1px;
|
|
1060
|
+
padding: var(--fd-space-3xs) 0;
|
|
790
1061
|
}
|
|
791
1062
|
|
|
792
1063
|
/* Sessions */
|
|
@@ -797,17 +1068,10 @@
|
|
|
797
1068
|
min-height: 0;
|
|
798
1069
|
}
|
|
799
1070
|
|
|
800
|
-
.playground__sessions-hint {
|
|
801
|
-
font-size: var(--fd-text-2xs);
|
|
802
|
-
color: var(--fd-muted-foreground);
|
|
803
|
-
margin: var(--fd-space-md) 0 var(--fd-space-2xs) var(--fd-space-md);
|
|
804
|
-
line-height: 1.3;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
1071
|
.playground__sessions {
|
|
808
1072
|
flex: 1;
|
|
809
1073
|
overflow-y: auto;
|
|
810
|
-
padding: 0 var(--fd-space-
|
|
1074
|
+
padding: 0 var(--fd-space-3xs) var(--fd-space-xl);
|
|
811
1075
|
min-height: 0;
|
|
812
1076
|
}
|
|
813
1077
|
|
|
@@ -824,7 +1088,6 @@
|
|
|
824
1088
|
align-items: center;
|
|
825
1089
|
justify-content: space-between;
|
|
826
1090
|
padding: var(--fd-space-sm) var(--fd-space-md);
|
|
827
|
-
margin-bottom: var(--fd-space-3xs);
|
|
828
1091
|
border-radius: var(--fd-radius-md);
|
|
829
1092
|
border-left: 3px solid transparent;
|
|
830
1093
|
cursor: pointer;
|
|
@@ -956,15 +1219,34 @@
|
|
|
956
1219
|
.playground__header {
|
|
957
1220
|
display: flex;
|
|
958
1221
|
align-items: center;
|
|
959
|
-
|
|
1222
|
+
gap: var(--fd-space-xs);
|
|
960
1223
|
height: var(--fd-playground-header-height);
|
|
961
|
-
padding: 0 var(--fd-space-
|
|
1224
|
+
padding: 0 var(--fd-space-xl);
|
|
962
1225
|
border-bottom: 1px solid var(--fd-border);
|
|
963
1226
|
background-color: var(--fd-background);
|
|
964
1227
|
box-sizing: border-box;
|
|
965
1228
|
flex-shrink: 0;
|
|
966
1229
|
}
|
|
967
1230
|
|
|
1231
|
+
:global(.playground__header-icon) {
|
|
1232
|
+
font-size: var(--fd-text-base);
|
|
1233
|
+
color: var(--fd-muted-foreground);
|
|
1234
|
+
flex-shrink: 0;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.playground__header-label {
|
|
1238
|
+
font-size: var(--fd-text-sm);
|
|
1239
|
+
font-weight: 600;
|
|
1240
|
+
color: var(--fd-foreground);
|
|
1241
|
+
flex-shrink: 0;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.playground__header-group {
|
|
1245
|
+
display: flex;
|
|
1246
|
+
align-items: center;
|
|
1247
|
+
gap: var(--fd-space-xs);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
968
1250
|
.playground__header-title {
|
|
969
1251
|
font-size: var(--fd-text-md);
|
|
970
1252
|
font-weight: 600;
|
|
@@ -992,6 +1274,207 @@
|
|
|
992
1274
|
color: var(--fd-foreground);
|
|
993
1275
|
}
|
|
994
1276
|
|
|
1277
|
+
.playground__header-actions {
|
|
1278
|
+
display: flex;
|
|
1279
|
+
align-items: center;
|
|
1280
|
+
gap: var(--fd-space-xs);
|
|
1281
|
+
margin-left: auto;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
.playground__log-toggle {
|
|
1285
|
+
display: inline-flex;
|
|
1286
|
+
align-items: center;
|
|
1287
|
+
gap: var(--fd-space-3xs);
|
|
1288
|
+
padding: var(--fd-space-3xs) var(--fd-space-sm);
|
|
1289
|
+
border: 1px solid var(--fd-border);
|
|
1290
|
+
border-radius: var(--fd-radius-md);
|
|
1291
|
+
background: transparent;
|
|
1292
|
+
color: var(--fd-muted-foreground);
|
|
1293
|
+
font-size: var(--fd-text-xs);
|
|
1294
|
+
font-weight: 500;
|
|
1295
|
+
cursor: pointer;
|
|
1296
|
+
transition: all var(--fd-transition-fast);
|
|
1297
|
+
line-height: 1;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.playground__log-toggle :global(svg) {
|
|
1301
|
+
font-size: var(--fd-text-xs);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.playground__log-toggle:hover {
|
|
1305
|
+
background-color: var(--fd-muted);
|
|
1306
|
+
color: var(--fd-foreground);
|
|
1307
|
+
border-color: var(--fd-border-strong);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
.playground__log-toggle--active {
|
|
1311
|
+
background-color: var(--fd-primary-muted);
|
|
1312
|
+
border-color: var(--fd-primary);
|
|
1313
|
+
color: var(--fd-primary);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/* Session chip (standalone mode) */
|
|
1317
|
+
.playground__session-chip-wrap {
|
|
1318
|
+
position: relative;
|
|
1319
|
+
flex-shrink: 0;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
.playground__session-chip {
|
|
1323
|
+
display: inline-flex;
|
|
1324
|
+
align-items: center;
|
|
1325
|
+
gap: var(--fd-space-xs);
|
|
1326
|
+
padding: var(--fd-space-3xs) var(--fd-space-sm) var(--fd-space-3xs) var(--fd-space-xs);
|
|
1327
|
+
border: 1px solid var(--fd-border);
|
|
1328
|
+
border-radius: var(--fd-radius-md);
|
|
1329
|
+
background: var(--fd-background);
|
|
1330
|
+
color: var(--fd-foreground);
|
|
1331
|
+
font-size: var(--fd-text-sm);
|
|
1332
|
+
font-weight: 500;
|
|
1333
|
+
cursor: pointer;
|
|
1334
|
+
transition: all var(--fd-transition-fast);
|
|
1335
|
+
max-width: 220px;
|
|
1336
|
+
line-height: 1;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
.playground__session-chip :global(svg) {
|
|
1340
|
+
flex-shrink: 0;
|
|
1341
|
+
font-size: var(--fd-text-sm);
|
|
1342
|
+
color: var(--fd-muted-foreground);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
.playground__session-chip:hover {
|
|
1346
|
+
background-color: var(--fd-muted);
|
|
1347
|
+
border-color: var(--fd-border-strong);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.playground__session-chip--open {
|
|
1351
|
+
background-color: var(--fd-muted);
|
|
1352
|
+
border-color: var(--fd-border-strong);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.playground__session-chip-name {
|
|
1356
|
+
flex: 1;
|
|
1357
|
+
white-space: nowrap;
|
|
1358
|
+
overflow: hidden;
|
|
1359
|
+
text-overflow: ellipsis;
|
|
1360
|
+
min-width: 0;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
:global(.playground__session-chip-chevron) {
|
|
1364
|
+
color: var(--fd-muted-foreground);
|
|
1365
|
+
flex-shrink: 0;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/* Session switcher popover */
|
|
1369
|
+
.playground__session-popover {
|
|
1370
|
+
position: absolute;
|
|
1371
|
+
top: calc(100% + var(--fd-space-xs));
|
|
1372
|
+
left: 0;
|
|
1373
|
+
z-index: 50;
|
|
1374
|
+
min-width: 220px;
|
|
1375
|
+
max-width: 300px;
|
|
1376
|
+
padding: var(--fd-space-xs);
|
|
1377
|
+
background-color: var(--fd-background);
|
|
1378
|
+
border: 1px solid var(--fd-border);
|
|
1379
|
+
border-radius: var(--fd-radius-lg);
|
|
1380
|
+
box-shadow: var(--fd-shadow-lg);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.playground__session-popover-divider {
|
|
1384
|
+
height: 1px;
|
|
1385
|
+
background-color: var(--fd-border-muted);
|
|
1386
|
+
margin: var(--fd-space-xs) 0;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
.playground__session-popover-row {
|
|
1390
|
+
display: flex;
|
|
1391
|
+
align-items: center;
|
|
1392
|
+
gap: 2px;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.playground__session-popover-item {
|
|
1396
|
+
display: flex;
|
|
1397
|
+
align-items: center;
|
|
1398
|
+
gap: var(--fd-space-sm);
|
|
1399
|
+
flex: 1;
|
|
1400
|
+
min-width: 0;
|
|
1401
|
+
padding: var(--fd-space-sm) var(--fd-space-sm);
|
|
1402
|
+
border: none;
|
|
1403
|
+
border-radius: var(--fd-radius-sm);
|
|
1404
|
+
background: transparent;
|
|
1405
|
+
color: var(--fd-foreground);
|
|
1406
|
+
font-size: var(--fd-text-sm);
|
|
1407
|
+
text-align: left;
|
|
1408
|
+
cursor: pointer;
|
|
1409
|
+
transition: background-color var(--fd-transition-fast);
|
|
1410
|
+
white-space: nowrap;
|
|
1411
|
+
overflow: hidden;
|
|
1412
|
+
text-overflow: ellipsis;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
.playground__session-popover-item :global(svg) {
|
|
1416
|
+
flex-shrink: 0;
|
|
1417
|
+
color: var(--fd-muted-foreground);
|
|
1418
|
+
font-size: var(--fd-text-sm);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
.playground__session-popover-item span {
|
|
1422
|
+
overflow: hidden;
|
|
1423
|
+
text-overflow: ellipsis;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.playground__session-popover-item:hover {
|
|
1427
|
+
background-color: var(--fd-muted);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
.playground__session-popover-item:disabled {
|
|
1431
|
+
opacity: 0.4;
|
|
1432
|
+
cursor: not-allowed;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
.playground__session-popover-item--new {
|
|
1436
|
+
color: var(--fd-primary);
|
|
1437
|
+
font-weight: 500;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.playground__session-popover-item--new :global(svg) {
|
|
1441
|
+
color: var(--fd-primary);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
.playground__session-popover-item--active {
|
|
1445
|
+
font-weight: 500;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
:global(.playground__session-popover-check) {
|
|
1449
|
+
color: var(--fd-primary) !important;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
.playground__session-popover-delete {
|
|
1453
|
+
display: flex;
|
|
1454
|
+
align-items: center;
|
|
1455
|
+
justify-content: center;
|
|
1456
|
+
flex-shrink: 0;
|
|
1457
|
+
width: var(--fd-size-icon-btn);
|
|
1458
|
+
height: var(--fd-size-icon-btn);
|
|
1459
|
+
border: none;
|
|
1460
|
+
border-radius: var(--fd-radius-sm);
|
|
1461
|
+
background: transparent;
|
|
1462
|
+
color: var(--fd-muted-foreground);
|
|
1463
|
+
cursor: pointer;
|
|
1464
|
+
opacity: 0;
|
|
1465
|
+
transition: all var(--fd-transition-fast);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
.playground__session-popover-row:hover .playground__session-popover-delete {
|
|
1469
|
+
opacity: 1;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
.playground__session-popover-delete:hover {
|
|
1473
|
+
background-color: var(--fd-error-muted);
|
|
1474
|
+
color: var(--fd-error);
|
|
1475
|
+
opacity: 1;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
995
1478
|
/* Error */
|
|
996
1479
|
.playground__error {
|
|
997
1480
|
display: flex;
|