@d34dman/flowdrop 0.0.30 → 0.0.31
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 +54 -6
- package/dist/components/NodeSidebar.svelte +1 -1
- package/dist/components/SchemaForm.svelte +14 -14
- package/dist/components/SchemaForm.svelte.d.ts +1 -1
- package/dist/components/form/FormFieldLight.svelte +66 -66
- package/dist/components/form/FormFieldLight.svelte.d.ts +1 -1
- package/dist/components/form/types.d.ts +1 -1
- package/dist/components/playground/ChatPanel.svelte +523 -0
- package/dist/components/playground/ChatPanel.svelte.d.ts +20 -0
- package/dist/components/playground/ExecutionLogs.svelte +486 -0
- package/dist/components/playground/ExecutionLogs.svelte.d.ts +14 -0
- package/dist/components/playground/InputCollector.svelte +444 -0
- package/dist/components/playground/InputCollector.svelte.d.ts +16 -0
- package/dist/components/playground/MessageBubble.svelte +398 -0
- package/dist/components/playground/MessageBubble.svelte.d.ts +15 -0
- package/dist/components/playground/Playground.svelte +851 -0
- package/dist/components/playground/Playground.svelte.d.ts +25 -0
- package/dist/components/playground/SessionManager.svelte +537 -0
- package/dist/components/playground/SessionManager.svelte.d.ts +20 -0
- package/dist/config/endpoints.d.ts +16 -0
- package/dist/config/endpoints.js +9 -0
- package/dist/core/index.d.ts +25 -23
- package/dist/core/index.js +13 -12
- package/dist/display/index.d.ts +2 -2
- package/dist/display/index.js +2 -2
- package/dist/editor/index.d.ts +57 -49
- package/dist/editor/index.js +52 -42
- package/dist/form/code.d.ts +4 -4
- package/dist/form/code.js +11 -11
- package/dist/form/fieldRegistry.d.ts +2 -2
- package/dist/form/fieldRegistry.js +8 -10
- package/dist/form/full.d.ts +5 -5
- package/dist/form/full.js +7 -7
- package/dist/form/index.d.ts +16 -16
- package/dist/form/index.js +14 -14
- package/dist/form/markdown.d.ts +3 -3
- package/dist/form/markdown.js +6 -6
- package/dist/index.d.ts +6 -4
- package/dist/index.js +9 -4
- package/dist/playground/index.d.ts +92 -0
- package/dist/playground/index.js +114 -0
- package/dist/playground/mount.d.ts +183 -0
- package/dist/playground/mount.js +178 -0
- package/dist/services/playgroundService.d.ts +129 -0
- package/dist/services/playgroundService.js +317 -0
- package/dist/stores/playgroundStore.d.ts +199 -0
- package/dist/stores/playgroundStore.js +350 -0
- package/dist/types/playground.d.ts +230 -0
- package/dist/types/playground.js +28 -0
- package/dist/utils/colors.js +4 -4
- package/package.json +6 -1
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import ConfigPanel from './ConfigPanel.svelte';
|
|
15
15
|
import Navbar from './Navbar.svelte';
|
|
16
16
|
import { api, setEndpointConfig } from '../services/api.js';
|
|
17
|
+
import { EnhancedFlowDropApiClient } from '../api/enhanced-client.js';
|
|
17
18
|
import type {
|
|
18
19
|
NodeMetadata,
|
|
19
20
|
Workflow,
|
|
@@ -122,6 +123,12 @@
|
|
|
122
123
|
let error = $state<string | null>(null);
|
|
123
124
|
let endpointConfig = $state<EndpointConfig | null>(null);
|
|
124
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Enhanced API client with authProvider support
|
|
128
|
+
* Used when authProvider is provided; otherwise falls back to legacy api service
|
|
129
|
+
*/
|
|
130
|
+
let apiClient = $state<EnhancedFlowDropApiClient | null>(null);
|
|
131
|
+
|
|
125
132
|
// ConfigSidebar state
|
|
126
133
|
let isConfigSidebarOpen = $state(false);
|
|
127
134
|
let selectedNodeId = $state<string | null>(null);
|
|
@@ -172,7 +179,7 @@
|
|
|
172
179
|
* Fetch node types from the server
|
|
173
180
|
*
|
|
174
181
|
* If propNodes is provided, uses those instead of fetching from API.
|
|
175
|
-
*
|
|
182
|
+
* Uses enhanced API client with authProvider support when available.
|
|
176
183
|
*/
|
|
177
184
|
async function fetchNodeTypes(): Promise<void> {
|
|
178
185
|
// If nodes were provided as props, use them directly (skip API fetch)
|
|
@@ -186,7 +193,13 @@
|
|
|
186
193
|
try {
|
|
187
194
|
error = null;
|
|
188
195
|
|
|
189
|
-
|
|
196
|
+
// Use enhanced client with authProvider if available, otherwise fall back to legacy api
|
|
197
|
+
let fetchedNodes: NodeMetadata[];
|
|
198
|
+
if (apiClient) {
|
|
199
|
+
fetchedNodes = await apiClient.getAvailableNodes();
|
|
200
|
+
} else {
|
|
201
|
+
fetchedNodes = await api.nodes.getNodes();
|
|
202
|
+
}
|
|
190
203
|
|
|
191
204
|
nodes = fetchedNodes;
|
|
192
205
|
error = null;
|
|
@@ -256,7 +269,7 @@
|
|
|
256
269
|
}
|
|
257
270
|
|
|
258
271
|
/**
|
|
259
|
-
* Initialize API endpoints
|
|
272
|
+
* Initialize API endpoints and create enhanced client if authProvider is available
|
|
260
273
|
* Priority: propEndpointConfig > existingConfig > apiBaseUrl > default
|
|
261
274
|
*/
|
|
262
275
|
async function initializeApiEndpoints(): Promise<void> {
|
|
@@ -264,6 +277,11 @@
|
|
|
264
277
|
if (propEndpointConfig) {
|
|
265
278
|
setEndpointConfig(propEndpointConfig);
|
|
266
279
|
endpointConfig = propEndpointConfig;
|
|
280
|
+
|
|
281
|
+
// Create enhanced API client with authProvider support if provided
|
|
282
|
+
if (authProvider) {
|
|
283
|
+
apiClient = new EnhancedFlowDropApiClient(propEndpointConfig, authProvider);
|
|
284
|
+
}
|
|
267
285
|
return;
|
|
268
286
|
}
|
|
269
287
|
|
|
@@ -274,6 +292,11 @@
|
|
|
274
292
|
// If config already exists and no override provided, use existing
|
|
275
293
|
if (existingConfig && !apiBaseUrl) {
|
|
276
294
|
endpointConfig = existingConfig;
|
|
295
|
+
|
|
296
|
+
// Create enhanced API client with authProvider support if provided
|
|
297
|
+
if (authProvider) {
|
|
298
|
+
apiClient = new EnhancedFlowDropApiClient(existingConfig, authProvider);
|
|
299
|
+
}
|
|
277
300
|
return;
|
|
278
301
|
}
|
|
279
302
|
|
|
@@ -296,6 +319,11 @@
|
|
|
296
319
|
setEndpointConfig(config);
|
|
297
320
|
// Store the configuration for passing to WorkflowEditor
|
|
298
321
|
endpointConfig = config;
|
|
322
|
+
|
|
323
|
+
// Create enhanced API client with authProvider support if provided
|
|
324
|
+
if (authProvider) {
|
|
325
|
+
apiClient = new EnhancedFlowDropApiClient(config, authProvider);
|
|
326
|
+
}
|
|
299
327
|
}
|
|
300
328
|
|
|
301
329
|
/**
|
|
@@ -357,6 +385,7 @@
|
|
|
357
385
|
* Save workflow - exposed API function
|
|
358
386
|
*
|
|
359
387
|
* Integrates with event handlers for enterprise customization.
|
|
388
|
+
* Uses enhanced API client with authProvider support when available.
|
|
360
389
|
*/
|
|
361
390
|
async function saveWorkflow(): Promise<void> {
|
|
362
391
|
// Wait for any pending DOM updates before saving
|
|
@@ -382,8 +411,7 @@
|
|
|
382
411
|
const loadingToast = features.showToasts ? apiToasts.loading('Saving workflow') : null;
|
|
383
412
|
|
|
384
413
|
try {
|
|
385
|
-
// Import
|
|
386
|
-
const { workflowApi } = await import('../services/api.js');
|
|
414
|
+
// Import uuid for new workflow ID generation
|
|
387
415
|
const { v4: uuidv4 } = await import('uuid');
|
|
388
416
|
|
|
389
417
|
// Determine the workflow ID
|
|
@@ -408,7 +436,27 @@
|
|
|
408
436
|
}
|
|
409
437
|
};
|
|
410
438
|
|
|
411
|
-
|
|
439
|
+
// Use enhanced client with authProvider if available, otherwise fall back to legacy api
|
|
440
|
+
let savedWorkflow: Workflow;
|
|
441
|
+
if (apiClient) {
|
|
442
|
+
// Check if this is an existing workflow (non-UUID ID indicates existing)
|
|
443
|
+
const isExistingWorkflow =
|
|
444
|
+
finalWorkflow.id &&
|
|
445
|
+
finalWorkflow.id.length > 0 &&
|
|
446
|
+
!finalWorkflow.id.match(
|
|
447
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
if (isExistingWorkflow) {
|
|
451
|
+
savedWorkflow = await apiClient.updateWorkflow(finalWorkflow.id, finalWorkflow);
|
|
452
|
+
} else {
|
|
453
|
+
savedWorkflow = await apiClient.saveWorkflow(finalWorkflow);
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
// Fall back to legacy workflowApi
|
|
457
|
+
const { workflowApi } = await import('../services/api.js');
|
|
458
|
+
savedWorkflow = await workflowApi.saveWorkflow(finalWorkflow);
|
|
459
|
+
}
|
|
412
460
|
|
|
413
461
|
// Update the workflow ID if it changed (new workflow)
|
|
414
462
|
// Keep our current workflow state, only update ID and metadata from backend
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
-->
|
|
51
51
|
|
|
52
52
|
<script lang="ts">
|
|
53
|
-
import Icon from
|
|
54
|
-
import type { ConfigSchema } from
|
|
55
|
-
import { FormField } from
|
|
56
|
-
import type { FieldSchema } from
|
|
53
|
+
import Icon from '@iconify/svelte';
|
|
54
|
+
import type { ConfigSchema } from '../types/index.js';
|
|
55
|
+
import { FormField } from './form/index.js';
|
|
56
|
+
import type { FieldSchema } from './form/index.js';
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Props interface for SchemaForm component
|
|
@@ -132,13 +132,13 @@
|
|
|
132
132
|
values = {},
|
|
133
133
|
onChange,
|
|
134
134
|
showActions = false,
|
|
135
|
-
saveLabel =
|
|
136
|
-
cancelLabel =
|
|
135
|
+
saveLabel = 'Save',
|
|
136
|
+
cancelLabel = 'Cancel',
|
|
137
137
|
onSave,
|
|
138
138
|
onCancel,
|
|
139
139
|
loading = false,
|
|
140
140
|
disabled = false,
|
|
141
|
-
class: className =
|
|
141
|
+
class: className = ''
|
|
142
142
|
}: Props = $props();
|
|
143
143
|
|
|
144
144
|
/**
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
*/
|
|
183
183
|
function handleFieldChange(key: string, value: unknown): void {
|
|
184
184
|
formValues[key] = value;
|
|
185
|
-
|
|
185
|
+
|
|
186
186
|
// Notify parent of the change
|
|
187
187
|
if (onChange) {
|
|
188
188
|
onChange({ ...formValues });
|
|
@@ -199,22 +199,22 @@
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
// Collect all form values including hidden fields
|
|
202
|
-
const form = document.querySelector(
|
|
202
|
+
const form = document.querySelector('.schema-form');
|
|
203
203
|
const updatedValues: Record<string, unknown> = { ...formValues };
|
|
204
204
|
|
|
205
205
|
if (form) {
|
|
206
|
-
const inputs = form.querySelectorAll(
|
|
206
|
+
const inputs = form.querySelectorAll('input, select, textarea');
|
|
207
207
|
inputs.forEach((input: Element) => {
|
|
208
208
|
const inputEl = input as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
209
209
|
if (inputEl.id) {
|
|
210
|
-
if (inputEl instanceof HTMLInputElement && inputEl.type ===
|
|
210
|
+
if (inputEl instanceof HTMLInputElement && inputEl.type === 'checkbox') {
|
|
211
211
|
updatedValues[inputEl.id] = inputEl.checked;
|
|
212
212
|
} else if (
|
|
213
213
|
inputEl instanceof HTMLInputElement &&
|
|
214
|
-
(inputEl.type ===
|
|
214
|
+
(inputEl.type === 'number' || inputEl.type === 'range')
|
|
215
215
|
) {
|
|
216
216
|
updatedValues[inputEl.id] = inputEl.value ? Number(inputEl.value) : inputEl.value;
|
|
217
|
-
} else if (inputEl instanceof HTMLInputElement && inputEl.type ===
|
|
217
|
+
} else if (inputEl instanceof HTMLInputElement && inputEl.type === 'hidden') {
|
|
218
218
|
// Parse hidden field values that might be JSON
|
|
219
219
|
try {
|
|
220
220
|
const parsed = JSON.parse(inputEl.value);
|
|
@@ -234,7 +234,7 @@
|
|
|
234
234
|
if (values && schema?.properties) {
|
|
235
235
|
Object.entries(schema.properties).forEach(
|
|
236
236
|
([key, property]: [string, Record<string, unknown>]) => {
|
|
237
|
-
if (property.format ===
|
|
237
|
+
if (property.format === 'hidden' && !(key in updatedValues) && key in values) {
|
|
238
238
|
updatedValues[key] = values[key];
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
-->
|
|
33
33
|
|
|
34
34
|
<script lang="ts">
|
|
35
|
-
import FormFieldWrapper from
|
|
36
|
-
import FormTextField from
|
|
37
|
-
import FormTextarea from
|
|
38
|
-
import FormNumberField from
|
|
39
|
-
import FormRangeField from
|
|
40
|
-
import FormToggle from
|
|
41
|
-
import FormSelect from
|
|
42
|
-
import FormCheckboxGroup from
|
|
43
|
-
import FormArray from
|
|
44
|
-
import { resolveFieldComponent } from
|
|
45
|
-
import type { FieldSchema, FieldOption } from
|
|
35
|
+
import FormFieldWrapper from './FormFieldWrapper.svelte';
|
|
36
|
+
import FormTextField from './FormTextField.svelte';
|
|
37
|
+
import FormTextarea from './FormTextarea.svelte';
|
|
38
|
+
import FormNumberField from './FormNumberField.svelte';
|
|
39
|
+
import FormRangeField from './FormRangeField.svelte';
|
|
40
|
+
import FormToggle from './FormToggle.svelte';
|
|
41
|
+
import FormSelect from './FormSelect.svelte';
|
|
42
|
+
import FormCheckboxGroup from './FormCheckboxGroup.svelte';
|
|
43
|
+
import FormArray from './FormArray.svelte';
|
|
44
|
+
import { resolveFieldComponent } from '../../form/fieldRegistry.js';
|
|
45
|
+
import type { FieldSchema, FieldOption } from './types.js';
|
|
46
46
|
|
|
47
47
|
interface Props {
|
|
48
48
|
/** Unique key/id for the field */
|
|
@@ -89,79 +89,79 @@
|
|
|
89
89
|
const fieldType = $derived.by(() => {
|
|
90
90
|
// If a custom component is registered, use it
|
|
91
91
|
if (registeredComponent) {
|
|
92
|
-
return
|
|
92
|
+
return 'registered';
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// Hidden fields should not be rendered
|
|
96
|
-
if (schema.format ===
|
|
97
|
-
return
|
|
96
|
+
if (schema.format === 'hidden') {
|
|
97
|
+
return 'hidden';
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// Check for heavy editor formats that need registration
|
|
101
|
-
if (schema.format ===
|
|
102
|
-
return
|
|
101
|
+
if (schema.format === 'json' || schema.format === 'code') {
|
|
102
|
+
return 'code-editor-fallback';
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
if (schema.format ===
|
|
106
|
-
return
|
|
105
|
+
if (schema.format === 'markdown') {
|
|
106
|
+
return 'markdown-editor-fallback';
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
if (schema.format ===
|
|
110
|
-
return
|
|
109
|
+
if (schema.format === 'template') {
|
|
110
|
+
return 'template-editor-fallback';
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// Object type without specific format would use code editor
|
|
114
|
-
if (schema.type ===
|
|
115
|
-
return
|
|
114
|
+
if (schema.type === 'object' && !schema.format) {
|
|
115
|
+
return 'code-editor-fallback';
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
// Enum with multiple selection -> checkbox group
|
|
119
119
|
if (schema.enum && schema.multiple) {
|
|
120
|
-
return
|
|
120
|
+
return 'checkbox-group';
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// Enum with single selection -> select
|
|
124
124
|
if (schema.enum) {
|
|
125
|
-
return
|
|
125
|
+
return 'select-enum';
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// Multiline string -> textarea
|
|
129
|
-
if (schema.type ===
|
|
130
|
-
return
|
|
129
|
+
if (schema.type === 'string' && schema.format === 'multiline') {
|
|
130
|
+
return 'textarea';
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// Range slider for number/integer with format: "range"
|
|
134
|
-
if ((schema.type ===
|
|
135
|
-
return
|
|
134
|
+
if ((schema.type === 'number' || schema.type === 'integer') && schema.format === 'range') {
|
|
135
|
+
return 'range';
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// String -> text field
|
|
139
|
-
if (schema.type ===
|
|
140
|
-
return
|
|
139
|
+
if (schema.type === 'string') {
|
|
140
|
+
return 'text';
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// Number or integer -> number field
|
|
144
|
-
if (schema.type ===
|
|
145
|
-
return
|
|
144
|
+
if (schema.type === 'number' || schema.type === 'integer') {
|
|
145
|
+
return 'number';
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// Boolean -> toggle
|
|
149
|
-
if (schema.type ===
|
|
150
|
-
return
|
|
149
|
+
if (schema.type === 'boolean') {
|
|
150
|
+
return 'toggle';
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// Select type or has options -> select
|
|
154
|
-
if (schema.type ===
|
|
155
|
-
return
|
|
154
|
+
if (schema.type === 'select' || schema.options) {
|
|
155
|
+
return 'select-options';
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// Array type
|
|
159
|
-
if (schema.type ===
|
|
160
|
-
return
|
|
159
|
+
if (schema.type === 'array') {
|
|
160
|
+
return 'array';
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// Fallback to text
|
|
164
|
-
return
|
|
164
|
+
return 'text';
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
/**
|
|
@@ -183,7 +183,7 @@
|
|
|
183
183
|
/**
|
|
184
184
|
* Get current value as the appropriate type
|
|
185
185
|
*/
|
|
186
|
-
const stringValue = $derived(String(value ??
|
|
186
|
+
const stringValue = $derived(String(value ?? ''));
|
|
187
187
|
const numberValue = $derived(value as number | string);
|
|
188
188
|
const booleanValue = $derived(Boolean(value ?? schema.default ?? false));
|
|
189
189
|
const arrayValue = $derived.by((): string[] => {
|
|
@@ -204,19 +204,19 @@
|
|
|
204
204
|
*/
|
|
205
205
|
function getEditorHint(editorType: string): string {
|
|
206
206
|
switch (editorType) {
|
|
207
|
-
case
|
|
207
|
+
case 'code-editor-fallback':
|
|
208
208
|
return "Code editor requires: import { registerCodeEditorField } from '@d34dman/flowdrop/form/code'; registerCodeEditorField();";
|
|
209
|
-
case
|
|
209
|
+
case 'markdown-editor-fallback':
|
|
210
210
|
return "Markdown editor requires: import { registerMarkdownEditorField } from '@d34dman/flowdrop/form/markdown'; registerMarkdownEditorField();";
|
|
211
|
-
case
|
|
211
|
+
case 'template-editor-fallback':
|
|
212
212
|
return "Template editor requires: import { registerTemplateEditorField } from '@d34dman/flowdrop/form/code'; registerTemplateEditorField();";
|
|
213
213
|
default:
|
|
214
|
-
return
|
|
214
|
+
return 'This field type requires additional registration.';
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
</script>
|
|
218
218
|
|
|
219
|
-
{#if fieldType !==
|
|
219
|
+
{#if fieldType !== 'hidden'}
|
|
220
220
|
<FormFieldWrapper
|
|
221
221
|
id={fieldKey}
|
|
222
222
|
label={fieldLabel}
|
|
@@ -224,12 +224,12 @@
|
|
|
224
224
|
description={schema.title ? schema.description : undefined}
|
|
225
225
|
{animationDelay}
|
|
226
226
|
>
|
|
227
|
-
{#if fieldType ===
|
|
227
|
+
{#if fieldType === 'registered' && registeredComponent}
|
|
228
228
|
<!-- Render registered custom component -->
|
|
229
229
|
<registeredComponent.component
|
|
230
230
|
id={fieldKey}
|
|
231
231
|
{value}
|
|
232
|
-
placeholder={schema.placeholder ??
|
|
232
|
+
placeholder={schema.placeholder ?? ''}
|
|
233
233
|
{required}
|
|
234
234
|
ariaDescribedBy={descriptionId}
|
|
235
235
|
height={schema.height as string | undefined}
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
placeholderExample={schema.placeholderExample as string | undefined}
|
|
243
243
|
onChange={(val: unknown) => onChange(val)}
|
|
244
244
|
/>
|
|
245
|
-
{:else if fieldType ===
|
|
245
|
+
{:else if fieldType === 'checkbox-group'}
|
|
246
246
|
<FormCheckboxGroup
|
|
247
247
|
id={fieldKey}
|
|
248
248
|
value={arrayValue}
|
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
ariaDescribedBy={descriptionId}
|
|
251
251
|
onChange={(val) => onChange(val)}
|
|
252
252
|
/>
|
|
253
|
-
{:else if fieldType ===
|
|
253
|
+
{:else if fieldType === 'select-enum'}
|
|
254
254
|
<FormSelect
|
|
255
255
|
id={fieldKey}
|
|
256
256
|
value={stringValue}
|
|
@@ -259,36 +259,36 @@
|
|
|
259
259
|
ariaDescribedBy={descriptionId}
|
|
260
260
|
onChange={(val) => onChange(val)}
|
|
261
261
|
/>
|
|
262
|
-
{:else if fieldType ===
|
|
262
|
+
{:else if fieldType === 'textarea'}
|
|
263
263
|
<FormTextarea
|
|
264
264
|
id={fieldKey}
|
|
265
265
|
value={stringValue}
|
|
266
|
-
placeholder={schema.placeholder ??
|
|
266
|
+
placeholder={schema.placeholder ?? ''}
|
|
267
267
|
{required}
|
|
268
268
|
ariaDescribedBy={descriptionId}
|
|
269
269
|
onChange={(val) => onChange(val)}
|
|
270
270
|
/>
|
|
271
|
-
{:else if fieldType ===
|
|
271
|
+
{:else if fieldType === 'text'}
|
|
272
272
|
<FormTextField
|
|
273
273
|
id={fieldKey}
|
|
274
274
|
value={stringValue}
|
|
275
|
-
placeholder={schema.placeholder ??
|
|
275
|
+
placeholder={schema.placeholder ?? ''}
|
|
276
276
|
{required}
|
|
277
277
|
ariaDescribedBy={descriptionId}
|
|
278
278
|
onChange={(val) => onChange(val)}
|
|
279
279
|
/>
|
|
280
|
-
{:else if fieldType ===
|
|
280
|
+
{:else if fieldType === 'number'}
|
|
281
281
|
<FormNumberField
|
|
282
282
|
id={fieldKey}
|
|
283
283
|
value={numberValue}
|
|
284
|
-
placeholder={schema.placeholder ??
|
|
284
|
+
placeholder={schema.placeholder ?? ''}
|
|
285
285
|
min={schema.minimum}
|
|
286
286
|
max={schema.maximum}
|
|
287
287
|
{required}
|
|
288
288
|
ariaDescribedBy={descriptionId}
|
|
289
289
|
onChange={(val) => onChange(val)}
|
|
290
290
|
/>
|
|
291
|
-
{:else if fieldType ===
|
|
291
|
+
{:else if fieldType === 'range'}
|
|
292
292
|
<FormRangeField
|
|
293
293
|
id={fieldKey}
|
|
294
294
|
value={numberValue}
|
|
@@ -299,14 +299,14 @@
|
|
|
299
299
|
ariaDescribedBy={descriptionId}
|
|
300
300
|
onChange={(val) => onChange(val)}
|
|
301
301
|
/>
|
|
302
|
-
{:else if fieldType ===
|
|
302
|
+
{:else if fieldType === 'toggle'}
|
|
303
303
|
<FormToggle
|
|
304
304
|
id={fieldKey}
|
|
305
305
|
value={booleanValue}
|
|
306
306
|
ariaDescribedBy={descriptionId}
|
|
307
307
|
onChange={(val) => onChange(val)}
|
|
308
308
|
/>
|
|
309
|
-
{:else if fieldType ===
|
|
309
|
+
{:else if fieldType === 'select-options'}
|
|
310
310
|
<FormSelect
|
|
311
311
|
id={fieldKey}
|
|
312
312
|
value={stringValue}
|
|
@@ -315,17 +315,17 @@
|
|
|
315
315
|
ariaDescribedBy={descriptionId}
|
|
316
316
|
onChange={(val) => onChange(val)}
|
|
317
317
|
/>
|
|
318
|
-
{:else if fieldType ===
|
|
318
|
+
{:else if fieldType === 'array' && schema.items}
|
|
319
319
|
<FormArray
|
|
320
320
|
id={fieldKey}
|
|
321
321
|
value={arrayItems}
|
|
322
322
|
itemSchema={schema.items}
|
|
323
323
|
minItems={schema.minItems}
|
|
324
324
|
maxItems={schema.maxItems}
|
|
325
|
-
addLabel={`Add ${schema.items.title ??
|
|
325
|
+
addLabel={`Add ${schema.items.title ?? 'Item'}`}
|
|
326
326
|
onChange={(val) => onChange(val)}
|
|
327
327
|
/>
|
|
328
|
-
{:else if fieldType.endsWith(
|
|
328
|
+
{:else if fieldType.endsWith('-fallback')}
|
|
329
329
|
<!-- Fallback for unregistered heavy editors -->
|
|
330
330
|
<div class="form-field-fallback">
|
|
331
331
|
<div class="form-field-fallback__message">
|
|
@@ -349,13 +349,13 @@
|
|
|
349
349
|
<!-- Provide a basic textarea fallback for editing -->
|
|
350
350
|
<FormTextarea
|
|
351
351
|
id={fieldKey}
|
|
352
|
-
value={typeof value ===
|
|
353
|
-
placeholder={schema.placeholder ??
|
|
352
|
+
value={typeof value === 'string' ? value : JSON.stringify(value, null, 2)}
|
|
353
|
+
placeholder={schema.placeholder ?? 'Enter value...'}
|
|
354
354
|
{required}
|
|
355
355
|
ariaDescribedBy={descriptionId}
|
|
356
356
|
onChange={(val) => {
|
|
357
357
|
// Try to parse as JSON for object types
|
|
358
|
-
if (schema.type ===
|
|
358
|
+
if (schema.type === 'object' || schema.format === 'json') {
|
|
359
359
|
try {
|
|
360
360
|
onChange(JSON.parse(val));
|
|
361
361
|
} catch {
|
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
<FormTextField
|
|
373
373
|
id={fieldKey}
|
|
374
374
|
value={stringValue}
|
|
375
|
-
placeholder={schema.placeholder ??
|
|
375
|
+
placeholder={schema.placeholder ?? ''}
|
|
376
376
|
ariaDescribedBy={descriptionId}
|
|
377
377
|
onChange={(val) => onChange(val)}
|
|
378
378
|
/>
|
|
@@ -413,7 +413,7 @@
|
|
|
413
413
|
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
414
414
|
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
415
415
|
border-radius: 0.375rem;
|
|
416
|
-
font-family:
|
|
416
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
|
|
417
417
|
font-size: 0.6875rem;
|
|
418
418
|
line-height: 1.5;
|
|
419
419
|
color: var(--color-ref-gray-600, #4b5563);
|
|
@@ -306,7 +306,7 @@ export interface SchemaFormProps {
|
|
|
306
306
|
* Properties define the form fields to render.
|
|
307
307
|
*/
|
|
308
308
|
schema: {
|
|
309
|
-
type:
|
|
309
|
+
type: 'object';
|
|
310
310
|
properties: Record<string, FieldSchema>;
|
|
311
311
|
required?: string[];
|
|
312
312
|
additionalProperties?: boolean;
|