@d34dman/flowdrop 0.0.31 → 0.0.32

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.
@@ -383,6 +383,7 @@
383
383
  class="playground"
384
384
  class:playground--embedded={mode === 'embedded'}
385
385
  class:playground--standalone={mode === 'standalone'}
386
+ class:playground--modal={mode === 'modal'}
386
387
  >
387
388
  <div class="playground__container">
388
389
  <!-- Sidebar -->
@@ -393,14 +394,18 @@
393
394
  <Icon icon="mdi:play-circle-outline" />
394
395
  <span>Playground</span>
395
396
  </div>
396
- {#if mode === 'embedded' && onClose}
397
+ {#if (mode === 'embedded' || mode === 'modal') && onClose}
397
398
  <button
398
399
  type="button"
399
400
  class="playground__sidebar-close"
400
401
  onclick={onClose}
401
402
  title="Close playground"
402
403
  >
403
- <Icon icon="mdi:dock-right" />
404
+ {#if mode === 'modal'}
405
+ <Icon icon="mdi:close" />
406
+ {:else}
407
+ <Icon icon="mdi:dock-right" />
408
+ {/if}
404
409
  </button>
405
410
  {/if}
406
411
  </div>
@@ -536,6 +541,11 @@
536
541
  height: 100vh;
537
542
  }
538
543
 
544
+ .playground--modal {
545
+ height: 100%;
546
+ width: 100%;
547
+ }
548
+
539
549
  /* Container */
540
550
  .playground__container {
541
551
  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;
@@ -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';
@@ -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';
@@ -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 mode)
96
+ * Callback when playground is closed (required for embedded and modal modes)
96
97
  */
97
98
  onClose?: () => void;
98
99
  }
@@ -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
- // Apply container styling
106
- container.style.height = height;
107
- container.style.width = width;
108
- // Mount the Svelte Playground component
109
- const svelteApp = mount(Playground, {
110
- target: container,
111
- props: {
112
- workflowId,
113
- workflow,
114
- mode,
115
- initialSessionId,
116
- endpointConfig: finalEndpointConfig,
117
- config,
118
- onClose
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
@@ -182,7 +182,7 @@ export interface PlaygroundConfig {
182
182
  /**
183
183
  * Display mode for the Playground component
184
184
  */
185
- export type PlaygroundMode = 'embedded' | 'standalone';
185
+ export type PlaygroundMode = 'embedded' | 'standalone' | 'modal';
186
186
  /**
187
187
  * Chat input detection patterns for identifying chat nodes in workflows
188
188
  */
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@d34dman/flowdrop",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "0.0.31",
5
+ "version": "0.0.32",
6
6
  "scripts": {
7
7
  "dev": "vite dev",
8
8
  "build": "vite build && npm run prepack",