@d34dman/flowdrop 0.0.29 → 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.
Files changed (55) hide show
  1. package/dist/components/App.svelte +54 -6
  2. package/dist/components/NodeSidebar.svelte +17 -9
  3. package/dist/components/SchemaForm.svelte +14 -14
  4. package/dist/components/SchemaForm.svelte.d.ts +1 -1
  5. package/dist/components/WorkflowEditor.svelte +4 -0
  6. package/dist/components/form/FormFieldLight.svelte +66 -66
  7. package/dist/components/form/FormFieldLight.svelte.d.ts +1 -1
  8. package/dist/components/form/types.d.ts +1 -1
  9. package/dist/components/nodes/ToolNode.svelte +23 -8
  10. package/dist/components/playground/ChatPanel.svelte +523 -0
  11. package/dist/components/playground/ChatPanel.svelte.d.ts +20 -0
  12. package/dist/components/playground/ExecutionLogs.svelte +486 -0
  13. package/dist/components/playground/ExecutionLogs.svelte.d.ts +14 -0
  14. package/dist/components/playground/InputCollector.svelte +444 -0
  15. package/dist/components/playground/InputCollector.svelte.d.ts +16 -0
  16. package/dist/components/playground/MessageBubble.svelte +398 -0
  17. package/dist/components/playground/MessageBubble.svelte.d.ts +15 -0
  18. package/dist/components/playground/Playground.svelte +851 -0
  19. package/dist/components/playground/Playground.svelte.d.ts +25 -0
  20. package/dist/components/playground/SessionManager.svelte +537 -0
  21. package/dist/components/playground/SessionManager.svelte.d.ts +20 -0
  22. package/dist/config/endpoints.d.ts +16 -0
  23. package/dist/config/endpoints.js +9 -0
  24. package/dist/core/index.d.ts +25 -23
  25. package/dist/core/index.js +13 -12
  26. package/dist/display/index.d.ts +2 -2
  27. package/dist/display/index.js +2 -2
  28. package/dist/editor/index.d.ts +57 -49
  29. package/dist/editor/index.js +52 -42
  30. package/dist/form/code.d.ts +4 -4
  31. package/dist/form/code.js +11 -11
  32. package/dist/form/fieldRegistry.d.ts +2 -2
  33. package/dist/form/fieldRegistry.js +8 -10
  34. package/dist/form/full.d.ts +5 -5
  35. package/dist/form/full.js +7 -7
  36. package/dist/form/index.d.ts +16 -16
  37. package/dist/form/index.js +14 -14
  38. package/dist/form/markdown.d.ts +3 -3
  39. package/dist/form/markdown.js +6 -6
  40. package/dist/index.d.ts +6 -4
  41. package/dist/index.js +9 -4
  42. package/dist/playground/index.d.ts +92 -0
  43. package/dist/playground/index.js +114 -0
  44. package/dist/playground/mount.d.ts +183 -0
  45. package/dist/playground/mount.js +178 -0
  46. package/dist/services/playgroundService.d.ts +129 -0
  47. package/dist/services/playgroundService.js +317 -0
  48. package/dist/stores/playgroundStore.d.ts +199 -0
  49. package/dist/stores/playgroundStore.js +350 -0
  50. package/dist/styles/base.css +5 -0
  51. package/dist/types/playground.d.ts +230 -0
  52. package/dist/types/playground.js +28 -0
  53. package/dist/utils/colors.d.ts +42 -0
  54. package/dist/utils/colors.js +77 -0
  55. 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
- * This fixes the bug where propNodes was ignored.
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
- const fetchedNodes = await api.nodes.getNodes();
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 necessary modules
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
- const savedWorkflow = await workflowApi.saveWorkflow(finalWorkflow);
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
@@ -25,18 +25,27 @@
25
25
  let categories = $derived(getCategories());
26
26
 
27
27
  /**
28
- * Get all unique categories from node types
28
+ * Get all unique categories from node types, preserving API order
29
+ * Categories appear in the order their first node appears in the API response
29
30
  */
30
31
  function getCategories(): NodeCategory[] {
31
32
  const nodes = props.nodes || [];
32
33
  if (nodes.length === 0) return [];
33
- const categories = new SvelteSet<NodeCategory>();
34
- nodes.forEach((node) => categories.add(node.category));
35
- return Array.from(categories).sort();
34
+ // Use a Set to track uniqueness while preserving insertion order
35
+ const seen = new SvelteSet<NodeCategory>();
36
+ const orderedCategories: NodeCategory[] = [];
37
+ for (const node of nodes) {
38
+ if (!seen.has(node.category)) {
39
+ seen.add(node.category);
40
+ orderedCategories.push(node.category);
41
+ }
42
+ }
43
+ return orderedCategories;
36
44
  }
37
45
 
38
46
  /**
39
47
  * Filter node types based on search query and selected category
48
+ * Preserves the API order - no client-side sorting applied
40
49
  */
41
50
  function getFilteredNodes(): NodeMetadata[] {
42
51
  // Use actual node types from props
@@ -58,8 +67,8 @@
58
67
  );
59
68
  }
60
69
 
61
- // Create a new array and sort it to avoid mutating the original
62
- return [...filtered].sort((a, b) => a.name.localeCompare(b.name));
70
+ // Return filtered results preserving API order
71
+ return filtered;
63
72
  }
64
73
 
65
74
  /**
@@ -149,12 +158,11 @@
149
158
 
150
159
  /**
151
160
  * Get node types for category
161
+ * Preserves the API order - no client-side sorting applied
152
162
  */
153
163
  function getNodesForCategory(category: NodeCategory): NodeMetadata[] {
154
164
  const nodes = props.nodes || [];
155
- return [...nodes]
156
- .filter((node) => node.category === category)
157
- .sort((a, b) => a.name.localeCompare(b.name));
165
+ return nodes.filter((node) => node.category === category);
158
166
  }
159
167
 
160
168
  /**
@@ -50,10 +50,10 @@
50
50
  -->
51
51
 
52
52
  <script lang="ts">
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";
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 = "Save",
136
- cancelLabel = "Cancel",
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(".schema-form");
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("input, select, textarea");
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 === "checkbox") {
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 === "number" || inputEl.type === "range")
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 === "hidden") {
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 === "hidden" && !(key in updatedValues) && key in values) {
237
+ if (property.format === 'hidden' && !(key in updatedValues) && key in values) {
238
238
  updatedValues[key] = values[key];
239
239
  }
240
240
  }
@@ -1,4 +1,4 @@
1
- import type { ConfigSchema } from "../types/index.js";
1
+ import type { ConfigSchema } from '../types/index.js';
2
2
  /**
3
3
  * Props interface for SchemaForm component
4
4
  */
@@ -37,6 +37,7 @@
37
37
  } from '../helpers/workflowEditorHelper.js';
38
38
  import type { NodeExecutionInfo } from '../types/index.js';
39
39
  import { areNodeArraysEqual, areEdgeArraysEqual, throttle } from '../utils/performanceUtils.js';
40
+ import { Toaster } from 'svelte-5-french-toast';
40
41
 
41
42
  interface Props {
42
43
  nodes?: NodeMetadata[];
@@ -457,6 +458,9 @@
457
458
  </div>
458
459
  </SvelteFlowProvider>
459
460
 
461
+ <!-- Toast notifications container -->
462
+ <Toaster position="bottom-center" />
463
+
460
464
  <style>
461
465
  .flowdrop-workflow-editor {
462
466
  display: flex;
@@ -32,17 +32,17 @@
32
32
  -->
33
33
 
34
34
  <script lang="ts">
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";
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 "registered";
92
+ return 'registered';
93
93
  }
94
94
 
95
95
  // Hidden fields should not be rendered
96
- if (schema.format === "hidden") {
97
- return "hidden";
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 === "json" || schema.format === "code") {
102
- return "code-editor-fallback";
101
+ if (schema.format === 'json' || schema.format === 'code') {
102
+ return 'code-editor-fallback';
103
103
  }
104
104
 
105
- if (schema.format === "markdown") {
106
- return "markdown-editor-fallback";
105
+ if (schema.format === 'markdown') {
106
+ return 'markdown-editor-fallback';
107
107
  }
108
108
 
109
- if (schema.format === "template") {
110
- return "template-editor-fallback";
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 === "object" && !schema.format) {
115
- return "code-editor-fallback";
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 "checkbox-group";
120
+ return 'checkbox-group';
121
121
  }
122
122
 
123
123
  // Enum with single selection -> select
124
124
  if (schema.enum) {
125
- return "select-enum";
125
+ return 'select-enum';
126
126
  }
127
127
 
128
128
  // Multiline string -> textarea
129
- if (schema.type === "string" && schema.format === "multiline") {
130
- return "textarea";
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 === "number" || schema.type === "integer") && schema.format === "range") {
135
- return "range";
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 === "string") {
140
- return "text";
139
+ if (schema.type === 'string') {
140
+ return 'text';
141
141
  }
142
142
 
143
143
  // Number or integer -> number field
144
- if (schema.type === "number" || schema.type === "integer") {
145
- return "number";
144
+ if (schema.type === 'number' || schema.type === 'integer') {
145
+ return 'number';
146
146
  }
147
147
 
148
148
  // Boolean -> toggle
149
- if (schema.type === "boolean") {
150
- return "toggle";
149
+ if (schema.type === 'boolean') {
150
+ return 'toggle';
151
151
  }
152
152
 
153
153
  // Select type or has options -> select
154
- if (schema.type === "select" || schema.options) {
155
- return "select-options";
154
+ if (schema.type === 'select' || schema.options) {
155
+ return 'select-options';
156
156
  }
157
157
 
158
158
  // Array type
159
- if (schema.type === "array") {
160
- return "array";
159
+ if (schema.type === 'array') {
160
+ return 'array';
161
161
  }
162
162
 
163
163
  // Fallback to text
164
- return "text";
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 "code-editor-fallback":
207
+ case 'code-editor-fallback':
208
208
  return "Code editor requires: import { registerCodeEditorField } from '@d34dman/flowdrop/form/code'; registerCodeEditorField();";
209
- case "markdown-editor-fallback":
209
+ case 'markdown-editor-fallback':
210
210
  return "Markdown editor requires: import { registerMarkdownEditorField } from '@d34dman/flowdrop/form/markdown'; registerMarkdownEditorField();";
211
- case "template-editor-fallback":
211
+ case 'template-editor-fallback':
212
212
  return "Template editor requires: import { registerTemplateEditorField } from '@d34dman/flowdrop/form/code'; registerTemplateEditorField();";
213
213
  default:
214
- return "This field type requires additional registration.";
214
+ return 'This field type requires additional registration.';
215
215
  }
216
216
  }
217
217
  </script>
218
218
 
219
- {#if fieldType !== "hidden"}
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 === "registered" && registeredComponent}
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 === "checkbox-group"}
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 === "select-enum"}
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 === "textarea"}
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 === "text"}
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 === "number"}
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 === "range"}
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 === "toggle"}
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 === "select-options"}
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 === "array" && schema.items}
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 ?? "Item"}`}
325
+ addLabel={`Add ${schema.items.title ?? 'Item'}`}
326
326
  onChange={(val) => onChange(val)}
327
327
  />
328
- {:else if fieldType.endsWith("-fallback")}
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 === "string" ? value : JSON.stringify(value, null, 2)}
353
- placeholder={schema.placeholder ?? "Enter value..."}
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 === "object" || schema.format === "json") {
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: "JetBrains Mono", "Fira Code", "Monaco", "Menlo", monospace;
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);
@@ -1,4 +1,4 @@
1
- import type { FieldSchema } from "./types.js";
1
+ import type { FieldSchema } from './types.js';
2
2
  interface Props {
3
3
  /** Unique key/id for the field */
4
4
  fieldKey: string;
@@ -306,7 +306,7 @@ export interface SchemaFormProps {
306
306
  * Properties define the form fields to render.
307
307
  */
308
308
  schema: {
309
- type: "object";
309
+ type: 'object';
310
310
  properties: Record<string, FieldSchema>;
311
311
  required?: string[];
312
312
  additionalProperties?: boolean;