@d34dman/flowdrop 0.0.26 → 0.0.28
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/App.svelte +3 -6
- package/dist/components/ConfigForm.svelte +496 -8
- package/dist/components/ConfigForm.svelte.d.ts +2 -0
- package/dist/components/EdgeRefresher.svelte +0 -1
- package/dist/components/SchemaForm.svelte +492 -0
- package/dist/components/SchemaForm.svelte.d.ts +65 -0
- package/dist/components/WorkflowEditor.svelte +1 -4
- package/dist/components/form/FormCodeEditor.svelte +0 -1
- package/dist/components/form/FormMarkdownEditor.svelte +0 -2
- package/dist/components/form/types.d.ts +96 -0
- package/dist/components/nodes/SimpleNode.svelte +92 -142
- package/dist/components/nodes/SquareNode.svelte +75 -58
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/services/dynamicSchemaService.d.ts +108 -0
- package/dist/services/dynamicSchemaService.js +445 -0
- package/dist/types/index.d.ts +213 -0
- package/package.json +1 -1
|
@@ -669,6 +669,7 @@
|
|
|
669
669
|
>
|
|
670
670
|
<ConfigForm
|
|
671
671
|
node={currentNode}
|
|
672
|
+
workflowId={$workflowStore?.id}
|
|
672
673
|
onSave={async (updatedConfig, uiExtensions?: NodeUIExtensions) => {
|
|
673
674
|
if (selectedNodeId && currentNode) {
|
|
674
675
|
// Build the updated node data
|
|
@@ -694,12 +695,8 @@
|
|
|
694
695
|
// All nodes use 'universalNode' and UniversalNode handles internal switching
|
|
695
696
|
workflowActions.updateNode(selectedNodeId, nodeUpdates);
|
|
696
697
|
|
|
697
|
-
//
|
|
698
|
-
|
|
699
|
-
const nodeType = currentNode.data?.metadata?.type;
|
|
700
|
-
if (nodeType === "gateway" && workflowEditorRef && selectedNodeId) {
|
|
701
|
-
await workflowEditorRef.refreshEdgePositions(selectedNodeId);
|
|
702
|
-
}
|
|
698
|
+
// Refresh edge positions just in case. This is a safe bet.
|
|
699
|
+
await workflowEditorRef.refreshEdgePositions(selectedNodeId);
|
|
703
700
|
}
|
|
704
701
|
|
|
705
702
|
closeConfigSidebar();
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- Dynamic form generation from JSON Schema using modular form components
|
|
9
9
|
- UI Extensions support for display settings (e.g., hide unconnected handles)
|
|
10
10
|
- Extensible architecture for complex schema types (array, object)
|
|
11
|
+
- Admin/Edit support for external configuration links and dynamic schema fetching
|
|
11
12
|
|
|
12
13
|
Accessibility features:
|
|
13
14
|
- Proper label associations with for/id attributes
|
|
@@ -18,9 +19,21 @@
|
|
|
18
19
|
|
|
19
20
|
<script lang="ts">
|
|
20
21
|
import Icon from '@iconify/svelte';
|
|
21
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
ConfigSchema,
|
|
24
|
+
WorkflowNode,
|
|
25
|
+
NodeUIExtensions,
|
|
26
|
+
ConfigEditOptions
|
|
27
|
+
} from '../types/index.js';
|
|
22
28
|
import { FormField, FormFieldWrapper, FormToggle } from './form/index.js';
|
|
23
29
|
import type { FieldSchema } from './form/index.js';
|
|
30
|
+
import {
|
|
31
|
+
getEffectiveConfigEditOptions,
|
|
32
|
+
fetchDynamicSchema,
|
|
33
|
+
resolveExternalEditUrl,
|
|
34
|
+
invalidateSchemaCache,
|
|
35
|
+
type DynamicSchemaResult
|
|
36
|
+
} from '../services/dynamicSchemaService.js';
|
|
24
37
|
|
|
25
38
|
interface Props {
|
|
26
39
|
/** Optional workflow node (if provided, schema and values are derived from it) */
|
|
@@ -31,20 +44,81 @@
|
|
|
31
44
|
values?: Record<string, unknown>;
|
|
32
45
|
/** Whether to show UI extension settings section */
|
|
33
46
|
showUIExtensions?: boolean;
|
|
47
|
+
/** Optional workflow ID for context in external links */
|
|
48
|
+
workflowId?: string;
|
|
34
49
|
/** Callback when form is saved (includes both config and extensions if enabled) */
|
|
35
50
|
onSave: (config: Record<string, unknown>, uiExtensions?: NodeUIExtensions) => void;
|
|
36
51
|
/** Callback when form is cancelled */
|
|
37
52
|
onCancel: () => void;
|
|
38
53
|
}
|
|
39
54
|
|
|
40
|
-
let {
|
|
55
|
+
let {
|
|
56
|
+
node,
|
|
57
|
+
schema,
|
|
58
|
+
values,
|
|
59
|
+
showUIExtensions = true,
|
|
60
|
+
workflowId,
|
|
61
|
+
onSave,
|
|
62
|
+
onCancel
|
|
63
|
+
}: Props = $props();
|
|
41
64
|
|
|
42
65
|
/**
|
|
43
|
-
*
|
|
66
|
+
* State for dynamic schema loading
|
|
44
67
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
68
|
+
let dynamicSchemaLoading = $state(false);
|
|
69
|
+
let dynamicSchemaError = $state<string | null>(null);
|
|
70
|
+
let fetchedDynamicSchema = $state<ConfigSchema | null>(null);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the admin edit configuration for the node
|
|
74
|
+
*/
|
|
75
|
+
const configEditOptions = $derived.by<ConfigEditOptions | undefined>(() => {
|
|
76
|
+
if (!node) return undefined;
|
|
77
|
+
return getEffectiveConfigEditOptions(node);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Determine if we should show the external edit link
|
|
82
|
+
*/
|
|
83
|
+
const showExternalEditLink = $derived.by(() => {
|
|
84
|
+
if (!configEditOptions?.externalEditLink) return false;
|
|
85
|
+
// Show if no dynamic schema, or if both exist but preferDynamicSchema is false
|
|
86
|
+
if (!configEditOptions.dynamicSchema) return true;
|
|
87
|
+
return !configEditOptions.preferDynamicSchema;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Determine if we should use/fetch dynamic schema
|
|
92
|
+
*/
|
|
93
|
+
const useDynamicSchema = $derived.by(() => {
|
|
94
|
+
if (!configEditOptions?.dynamicSchema) return false;
|
|
95
|
+
// Use if no external link, or if both exist and preferDynamicSchema is true
|
|
96
|
+
if (!configEditOptions.externalEditLink) return true;
|
|
97
|
+
return configEditOptions.preferDynamicSchema === true;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the configuration schema from node metadata, direct prop, or fetched dynamic schema
|
|
102
|
+
* Priority: fetchedDynamicSchema > direct schema prop > node metadata configSchema
|
|
103
|
+
*/
|
|
104
|
+
const configSchema = $derived.by<ConfigSchema | undefined>(() => {
|
|
105
|
+
// If we have a fetched dynamic schema, use it
|
|
106
|
+
if (fetchedDynamicSchema) {
|
|
107
|
+
return fetchedDynamicSchema;
|
|
108
|
+
}
|
|
109
|
+
// Otherwise use the direct prop or node metadata
|
|
110
|
+
return schema ?? (node?.data.metadata?.configSchema as ConfigSchema | undefined);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if the node has no static schema and needs dynamic loading
|
|
115
|
+
*/
|
|
116
|
+
const needsDynamicSchemaLoad = $derived.by(() => {
|
|
117
|
+
if (!node) return false;
|
|
118
|
+
const staticSchema = schema ?? node.data.metadata?.configSchema;
|
|
119
|
+
// Need to load if: no static schema AND dynamic schema is configured
|
|
120
|
+
return !staticSchema && useDynamicSchema && !fetchedDynamicSchema && !dynamicSchemaLoading;
|
|
121
|
+
});
|
|
48
122
|
|
|
49
123
|
/**
|
|
50
124
|
* Get the current configuration from node or direct prop
|
|
@@ -74,6 +148,85 @@
|
|
|
74
148
|
return { ...typeDefaults, ...instanceOverrides };
|
|
75
149
|
});
|
|
76
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Fetch dynamic schema when needed
|
|
153
|
+
*/
|
|
154
|
+
async function loadDynamicSchema(): Promise<void> {
|
|
155
|
+
if (!node || !configEditOptions?.dynamicSchema) return;
|
|
156
|
+
|
|
157
|
+
dynamicSchemaLoading = true;
|
|
158
|
+
dynamicSchemaError = null;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const result: DynamicSchemaResult = await fetchDynamicSchema(
|
|
162
|
+
configEditOptions.dynamicSchema,
|
|
163
|
+
node,
|
|
164
|
+
workflowId
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (result.success && result.schema) {
|
|
168
|
+
fetchedDynamicSchema = result.schema;
|
|
169
|
+
} else {
|
|
170
|
+
dynamicSchemaError =
|
|
171
|
+
result.error ?? configEditOptions.errorMessage ?? 'Failed to load configuration schema';
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
dynamicSchemaError =
|
|
175
|
+
err instanceof Error
|
|
176
|
+
? err.message
|
|
177
|
+
: (configEditOptions.errorMessage ?? 'Failed to load configuration schema');
|
|
178
|
+
} finally {
|
|
179
|
+
dynamicSchemaLoading = false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Refresh the dynamic schema (invalidate cache and reload)
|
|
185
|
+
*/
|
|
186
|
+
async function refreshDynamicSchema(): Promise<void> {
|
|
187
|
+
if (!node || !configEditOptions?.dynamicSchema) return;
|
|
188
|
+
|
|
189
|
+
// Invalidate the cache first
|
|
190
|
+
invalidateSchemaCache(node, configEditOptions.dynamicSchema);
|
|
191
|
+
fetchedDynamicSchema = null;
|
|
192
|
+
|
|
193
|
+
// Reload the schema
|
|
194
|
+
await loadDynamicSchema();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get the resolved external edit URL
|
|
199
|
+
*/
|
|
200
|
+
function getExternalEditUrl(): string {
|
|
201
|
+
if (!node || !configEditOptions?.externalEditLink) return '#';
|
|
202
|
+
return resolveExternalEditUrl(configEditOptions.externalEditLink, node, workflowId);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Handle opening external edit link
|
|
207
|
+
*/
|
|
208
|
+
function handleExternalEditClick(): void {
|
|
209
|
+
if (!node || !configEditOptions?.externalEditLink) return;
|
|
210
|
+
|
|
211
|
+
const url = getExternalEditUrl();
|
|
212
|
+
const openInNewTab = configEditOptions.externalEditLink.openInNewTab !== false;
|
|
213
|
+
|
|
214
|
+
if (openInNewTab) {
|
|
215
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
216
|
+
} else {
|
|
217
|
+
window.location.href = url;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Auto-load dynamic schema on mount if needed
|
|
223
|
+
*/
|
|
224
|
+
$effect(() => {
|
|
225
|
+
if (needsDynamicSchemaLoad) {
|
|
226
|
+
loadDynamicSchema();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
77
230
|
/**
|
|
78
231
|
* Initialize config values when node/schema changes
|
|
79
232
|
*/
|
|
@@ -179,7 +332,74 @@
|
|
|
179
332
|
}
|
|
180
333
|
</script>
|
|
181
334
|
|
|
182
|
-
|
|
335
|
+
<!-- External Edit Link Section (shown when configured and preferred) -->
|
|
336
|
+
{#if showExternalEditLink && configEditOptions?.externalEditLink}
|
|
337
|
+
<div class="config-form__admin-edit">
|
|
338
|
+
<div class="config-form__admin-edit-header">
|
|
339
|
+
<Icon icon="heroicons:arrow-top-right-on-square" />
|
|
340
|
+
<span>External Configuration</span>
|
|
341
|
+
</div>
|
|
342
|
+
<div class="config-form__admin-edit-content">
|
|
343
|
+
<p class="config-form__admin-edit-description">
|
|
344
|
+
{configEditOptions.externalEditLink.description ??
|
|
345
|
+
'This node requires external configuration. Click the button below to open the configuration panel.'}
|
|
346
|
+
</p>
|
|
347
|
+
<button
|
|
348
|
+
type="button"
|
|
349
|
+
class="config-form__button config-form__button--external"
|
|
350
|
+
onclick={handleExternalEditClick}
|
|
351
|
+
>
|
|
352
|
+
<Icon
|
|
353
|
+
icon={configEditOptions.externalEditLink.icon ?? 'heroicons:arrow-top-right-on-square'}
|
|
354
|
+
/>
|
|
355
|
+
<span>{configEditOptions.externalEditLink.label ?? 'Configure Externally'}</span>
|
|
356
|
+
</button>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
{/if}
|
|
360
|
+
|
|
361
|
+
<!-- Dynamic Schema Loading State -->
|
|
362
|
+
{#if dynamicSchemaLoading}
|
|
363
|
+
<div class="config-form__loading">
|
|
364
|
+
<div class="config-form__loading-spinner"></div>
|
|
365
|
+
<p class="config-form__loading-text">
|
|
366
|
+
{configEditOptions?.loadingMessage ?? 'Loading configuration options...'}
|
|
367
|
+
</p>
|
|
368
|
+
</div>
|
|
369
|
+
{:else if dynamicSchemaError}
|
|
370
|
+
<div class="config-form__error">
|
|
371
|
+
<div class="config-form__error-header">
|
|
372
|
+
<Icon icon="heroicons:exclamation-triangle" />
|
|
373
|
+
<span>Configuration Error</span>
|
|
374
|
+
</div>
|
|
375
|
+
<div class="config-form__error-content">
|
|
376
|
+
<p class="config-form__error-message">{dynamicSchemaError}</p>
|
|
377
|
+
<div class="config-form__error-actions">
|
|
378
|
+
<button
|
|
379
|
+
type="button"
|
|
380
|
+
class="config-form__button config-form__button--secondary"
|
|
381
|
+
onclick={refreshDynamicSchema}
|
|
382
|
+
>
|
|
383
|
+
<Icon icon="heroicons:arrow-path" />
|
|
384
|
+
<span>Retry</span>
|
|
385
|
+
</button>
|
|
386
|
+
{#if configEditOptions?.externalEditLink}
|
|
387
|
+
<button
|
|
388
|
+
type="button"
|
|
389
|
+
class="config-form__button config-form__button--external"
|
|
390
|
+
onclick={handleExternalEditClick}
|
|
391
|
+
>
|
|
392
|
+
<Icon
|
|
393
|
+
icon={configEditOptions.externalEditLink.icon ??
|
|
394
|
+
'heroicons:arrow-top-right-on-square'}
|
|
395
|
+
/>
|
|
396
|
+
<span>{configEditOptions.externalEditLink.label ?? 'Use External Editor'}</span>
|
|
397
|
+
</button>
|
|
398
|
+
{/if}
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
{:else if configSchema}
|
|
183
403
|
<form
|
|
184
404
|
class="config-form"
|
|
185
405
|
onsubmit={(e) => {
|
|
@@ -187,6 +407,35 @@
|
|
|
187
407
|
handleSave();
|
|
188
408
|
}}
|
|
189
409
|
>
|
|
410
|
+
<!-- Dynamic Schema Refresh Button -->
|
|
411
|
+
{#if fetchedDynamicSchema && configEditOptions?.showRefreshButton !== false}
|
|
412
|
+
<div class="config-form__schema-actions">
|
|
413
|
+
<button
|
|
414
|
+
type="button"
|
|
415
|
+
class="config-form__schema-refresh"
|
|
416
|
+
onclick={refreshDynamicSchema}
|
|
417
|
+
title="Refresh configuration schema"
|
|
418
|
+
>
|
|
419
|
+
<Icon icon="heroicons:arrow-path" />
|
|
420
|
+
<span>Refresh Schema</span>
|
|
421
|
+
</button>
|
|
422
|
+
{#if configEditOptions?.externalEditLink}
|
|
423
|
+
<button
|
|
424
|
+
type="button"
|
|
425
|
+
class="config-form__schema-external"
|
|
426
|
+
onclick={handleExternalEditClick}
|
|
427
|
+
title={configEditOptions.externalEditLink.description ?? 'Open external editor'}
|
|
428
|
+
>
|
|
429
|
+
<Icon
|
|
430
|
+
icon={configEditOptions.externalEditLink.icon ??
|
|
431
|
+
'heroicons:arrow-top-right-on-square'}
|
|
432
|
+
/>
|
|
433
|
+
<span>{configEditOptions.externalEditLink.label ?? 'External Editor'}</span>
|
|
434
|
+
</button>
|
|
435
|
+
{/if}
|
|
436
|
+
</div>
|
|
437
|
+
{/if}
|
|
438
|
+
|
|
190
439
|
{#if configSchema.properties}
|
|
191
440
|
<div class="config-form__fields">
|
|
192
441
|
{#each Object.entries(configSchema.properties) as [key, field], index (key)}
|
|
@@ -259,12 +508,24 @@
|
|
|
259
508
|
</button>
|
|
260
509
|
</div>
|
|
261
510
|
</form>
|
|
262
|
-
{:else}
|
|
511
|
+
{:else if !dynamicSchemaLoading && !showExternalEditLink}
|
|
263
512
|
<div class="config-form__empty">
|
|
264
513
|
<div class="config-form__empty-icon">
|
|
265
514
|
<Icon icon="heroicons:cog-6-tooth" />
|
|
266
515
|
</div>
|
|
267
516
|
<p class="config-form__empty-text">No configuration options available for this node.</p>
|
|
517
|
+
{#if configEditOptions?.externalEditLink}
|
|
518
|
+
<button
|
|
519
|
+
type="button"
|
|
520
|
+
class="config-form__button config-form__button--external config-form__empty-button"
|
|
521
|
+
onclick={handleExternalEditClick}
|
|
522
|
+
>
|
|
523
|
+
<Icon
|
|
524
|
+
icon={configEditOptions.externalEditLink.icon ?? 'heroicons:arrow-top-right-on-square'}
|
|
525
|
+
/>
|
|
526
|
+
<span>{configEditOptions.externalEditLink.label ?? 'Configure Externally'}</span>
|
|
527
|
+
</button>
|
|
528
|
+
{/if}
|
|
268
529
|
</div>
|
|
269
530
|
{/if}
|
|
270
531
|
|
|
@@ -482,4 +743,231 @@
|
|
|
482
743
|
font-style: italic;
|
|
483
744
|
line-height: 1.5;
|
|
484
745
|
}
|
|
746
|
+
|
|
747
|
+
.config-form__empty-button {
|
|
748
|
+
margin-top: 1rem;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/* ============================================
|
|
752
|
+
ADMIN/EDIT SECTION - External Configuration
|
|
753
|
+
============================================ */
|
|
754
|
+
|
|
755
|
+
.config-form__admin-edit {
|
|
756
|
+
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
|
757
|
+
border: 1px solid var(--color-ref-blue-200, #bfdbfe);
|
|
758
|
+
border-radius: 0.625rem;
|
|
759
|
+
overflow: hidden;
|
|
760
|
+
margin-bottom: 1rem;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.config-form__admin-edit-header {
|
|
764
|
+
display: flex;
|
|
765
|
+
align-items: center;
|
|
766
|
+
gap: 0.5rem;
|
|
767
|
+
padding: 0.75rem 1rem;
|
|
768
|
+
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
|
|
769
|
+
border-bottom: 1px solid var(--color-ref-blue-200, #bfdbfe);
|
|
770
|
+
font-size: 0.8125rem;
|
|
771
|
+
font-weight: 600;
|
|
772
|
+
color: var(--color-ref-blue-800, #1e40af);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.config-form__admin-edit-header :global(svg) {
|
|
776
|
+
width: 1rem;
|
|
777
|
+
height: 1rem;
|
|
778
|
+
color: var(--color-ref-blue-600, #2563eb);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.config-form__admin-edit-content {
|
|
782
|
+
padding: 1rem;
|
|
783
|
+
display: flex;
|
|
784
|
+
flex-direction: column;
|
|
785
|
+
gap: 0.75rem;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.config-form__admin-edit-description {
|
|
789
|
+
margin: 0;
|
|
790
|
+
font-size: 0.8125rem;
|
|
791
|
+
color: var(--color-ref-blue-700, #1d4ed8);
|
|
792
|
+
line-height: 1.5;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/* ============================================
|
|
796
|
+
LOADING STATE
|
|
797
|
+
============================================ */
|
|
798
|
+
|
|
799
|
+
.config-form__loading {
|
|
800
|
+
display: flex;
|
|
801
|
+
flex-direction: column;
|
|
802
|
+
align-items: center;
|
|
803
|
+
justify-content: center;
|
|
804
|
+
padding: 3rem 1.5rem;
|
|
805
|
+
gap: 1rem;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.config-form__loading-spinner {
|
|
809
|
+
width: 2.5rem;
|
|
810
|
+
height: 2.5rem;
|
|
811
|
+
border: 3px solid var(--color-ref-blue-100, #dbeafe);
|
|
812
|
+
border-top-color: var(--color-ref-blue-500, #3b82f6);
|
|
813
|
+
border-radius: 50%;
|
|
814
|
+
animation: config-form-spin 0.8s linear infinite;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
@keyframes config-form-spin {
|
|
818
|
+
to {
|
|
819
|
+
transform: rotate(360deg);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.config-form__loading-text {
|
|
824
|
+
margin: 0;
|
|
825
|
+
font-size: 0.875rem;
|
|
826
|
+
color: var(--color-ref-gray-600, #4b5563);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/* ============================================
|
|
830
|
+
ERROR STATE
|
|
831
|
+
============================================ */
|
|
832
|
+
|
|
833
|
+
.config-form__error {
|
|
834
|
+
background-color: var(--color-ref-red-50, #fef2f2);
|
|
835
|
+
border: 1px solid var(--color-ref-red-200, #fecaca);
|
|
836
|
+
border-radius: 0.5rem;
|
|
837
|
+
overflow: hidden;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.config-form__error-header {
|
|
841
|
+
display: flex;
|
|
842
|
+
align-items: center;
|
|
843
|
+
gap: 0.5rem;
|
|
844
|
+
padding: 0.75rem 1rem;
|
|
845
|
+
background-color: var(--color-ref-red-100, #fee2e2);
|
|
846
|
+
border-bottom: 1px solid var(--color-ref-red-200, #fecaca);
|
|
847
|
+
font-size: 0.8125rem;
|
|
848
|
+
font-weight: 600;
|
|
849
|
+
color: var(--color-ref-red-800, #991b1b);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.config-form__error-header :global(svg) {
|
|
853
|
+
width: 1rem;
|
|
854
|
+
height: 1rem;
|
|
855
|
+
color: var(--color-ref-red-600, #dc2626);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.config-form__error-content {
|
|
859
|
+
padding: 1rem;
|
|
860
|
+
display: flex;
|
|
861
|
+
flex-direction: column;
|
|
862
|
+
gap: 0.75rem;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.config-form__error-message {
|
|
866
|
+
margin: 0;
|
|
867
|
+
font-size: 0.8125rem;
|
|
868
|
+
color: var(--color-ref-red-700, #b91c1c);
|
|
869
|
+
line-height: 1.5;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.config-form__error-actions {
|
|
873
|
+
display: flex;
|
|
874
|
+
gap: 0.5rem;
|
|
875
|
+
flex-wrap: wrap;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* ============================================
|
|
879
|
+
SCHEMA ACTIONS (Refresh, External Editor)
|
|
880
|
+
============================================ */
|
|
881
|
+
|
|
882
|
+
.config-form__schema-actions {
|
|
883
|
+
display: flex;
|
|
884
|
+
gap: 0.5rem;
|
|
885
|
+
margin-bottom: 1rem;
|
|
886
|
+
padding-bottom: 0.75rem;
|
|
887
|
+
border-bottom: 1px solid var(--color-ref-gray-100, #f3f4f6);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.config-form__schema-refresh,
|
|
891
|
+
.config-form__schema-external {
|
|
892
|
+
display: inline-flex;
|
|
893
|
+
align-items: center;
|
|
894
|
+
gap: 0.375rem;
|
|
895
|
+
padding: 0.375rem 0.625rem;
|
|
896
|
+
font-size: 0.75rem;
|
|
897
|
+
font-weight: 500;
|
|
898
|
+
font-family: inherit;
|
|
899
|
+
border-radius: 0.375rem;
|
|
900
|
+
cursor: pointer;
|
|
901
|
+
transition: all 0.15s ease;
|
|
902
|
+
border: 1px solid transparent;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.config-form__schema-refresh {
|
|
906
|
+
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
907
|
+
border-color: var(--color-ref-gray-200, #e5e7eb);
|
|
908
|
+
color: var(--color-ref-gray-600, #4b5563);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.config-form__schema-refresh:hover {
|
|
912
|
+
background-color: var(--color-ref-gray-100, #f3f4f6);
|
|
913
|
+
border-color: var(--color-ref-gray-300, #d1d5db);
|
|
914
|
+
color: var(--color-ref-gray-700, #374151);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.config-form__schema-refresh :global(svg),
|
|
918
|
+
.config-form__schema-external :global(svg) {
|
|
919
|
+
width: 0.875rem;
|
|
920
|
+
height: 0.875rem;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.config-form__schema-external {
|
|
924
|
+
background-color: var(--color-ref-blue-50, #eff6ff);
|
|
925
|
+
border-color: var(--color-ref-blue-200, #bfdbfe);
|
|
926
|
+
color: var(--color-ref-blue-700, #1d4ed8);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.config-form__schema-external:hover {
|
|
930
|
+
background-color: var(--color-ref-blue-100, #dbeafe);
|
|
931
|
+
border-color: var(--color-ref-blue-300, #93c5fd);
|
|
932
|
+
color: var(--color-ref-blue-800, #1e40af);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/* ============================================
|
|
936
|
+
EXTERNAL BUTTON STYLE
|
|
937
|
+
============================================ */
|
|
938
|
+
|
|
939
|
+
.config-form__button--external {
|
|
940
|
+
background: linear-gradient(
|
|
941
|
+
135deg,
|
|
942
|
+
var(--color-ref-indigo-500, #6366f1) 0%,
|
|
943
|
+
var(--color-ref-blue-600, #2563eb) 100%
|
|
944
|
+
);
|
|
945
|
+
color: #ffffff;
|
|
946
|
+
box-shadow:
|
|
947
|
+
0 1px 3px rgba(99, 102, 241, 0.3),
|
|
948
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
.config-form__button--external:hover {
|
|
952
|
+
background: linear-gradient(
|
|
953
|
+
135deg,
|
|
954
|
+
var(--color-ref-indigo-600, #4f46e5) 0%,
|
|
955
|
+
var(--color-ref-blue-700, #1d4ed8) 100%
|
|
956
|
+
);
|
|
957
|
+
box-shadow:
|
|
958
|
+
0 4px 12px rgba(99, 102, 241, 0.35),
|
|
959
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
960
|
+
transform: translateY(-1px);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.config-form__button--external:active {
|
|
964
|
+
transform: translateY(0);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.config-form__button--external:focus-visible {
|
|
968
|
+
outline: none;
|
|
969
|
+
box-shadow:
|
|
970
|
+
0 0 0 3px rgba(99, 102, 241, 0.4),
|
|
971
|
+
0 4px 12px rgba(99, 102, 241, 0.35);
|
|
972
|
+
}
|
|
485
973
|
</style>
|
|
@@ -8,6 +8,8 @@ interface Props {
|
|
|
8
8
|
values?: Record<string, unknown>;
|
|
9
9
|
/** Whether to show UI extension settings section */
|
|
10
10
|
showUIExtensions?: boolean;
|
|
11
|
+
/** Optional workflow ID for context in external links */
|
|
12
|
+
workflowId?: string;
|
|
11
13
|
/** Callback when form is saved (includes both config and extensions if enabled) */
|
|
12
14
|
onSave: (config: Record<string, unknown>, uiExtensions?: NodeUIExtensions) => void;
|
|
13
15
|
/** Callback when form is cancelled */
|