diff --git a/dashboard/src/components/dynamic-form/README.md b/dashboard/src/components/dynamic-form/README.md new file mode 100644 index 00000000..3810e3d2 --- /dev/null +++ b/dashboard/src/components/dynamic-form/README.md @@ -0,0 +1,126 @@ +# Dynamic Config Form System + +## Overview +The Dynamic Config Form system is a schema-driven UI component designed to automatically generate configuration forms based on backend Pydantic models. It supports rich metadata for UI customization and a flexible Hook system for complex fields. + +### Core Components +- **DynamicConfigForm**: The main component that takes a `ConfigSchema` and renders the entire form. +- **DynamicField**: A lower-level component that renders individual fields based on their type and UI metadata. +- **FieldHookRegistry**: A registry for custom React components that can replace or wrap default field rendering. + +## Quick Start +To use the dynamic form in your page: + +```typescript +import { DynamicConfigForm } from '@/components/dynamic-form' +import { fieldHooks } from '@/lib/field-hooks' + +// Example usage in a component +export function ConfigPage() { + const [config, setConfig] = useState({}) + const schema = useConfigSchema() // Fetch from API + + const handleChange = (fieldPath: string, value: unknown) => { + // fieldPath can be nested, e.g., 'section.subfield' + updateConfigAt(fieldPath, value) + } + + return ( + + ) +} +``` + +## Adding UI Metadata (Backend) +You can customize how fields are rendered by adding `json_schema_extra` to your Pydantic `Field` definitions. + +### Supported Metadata +- `x-widget`: Specifies the UI component to use. + - `slider`: A range slider (requires `ge`, `le`, and `step`). + - `switch`: A toggle switch (for booleans). + - `textarea`: A multi-line text input. + - `select`: A dropdown menu (for `Literal` or enum types). + - `custom`: Indicates that this field requires a Hook for rendering. +- `x-icon`: A Lucide icon name (e.g., `MessageSquare`, `Settings`). +- `step`: Incremental step for sliders or number inputs. + +### Example +```python +class ChatConfig(ConfigBase): + talk_value: float = Field( + default=0.5, + ge=0.0, + le=1.0, + json_schema_extra={ + "x-widget": "slider", + "x-icon": "MessageSquare", + "step": 0.1 + } + ) +``` + +## Creating Hook Components +Hooks allow you to provide custom UI for complex configuration sections or fields. + +### FieldHookComponent Interface +A Hook component receives the following props: +- `fieldPath`: The full path to the field. +- `value`: The current value of the field/section. +- `onChange`: Callback to update the value. +- `children`: (Only for `wrapper` hooks) The default field renderer. + +### Implementation Example +```typescript +import type { FieldHookComponent } from '@/lib/field-hooks' + +export const CustomSectionHook: FieldHookComponent = ({ + fieldPath, + value, + onChange +}) => { + return ( +
+

Custom UI

+ onChange({ ...value, some_prop: e.target.value })} + /> +
+ ) +} +``` + +### Registering Hooks +Register hooks in your component's lifecycle: +```typescript +useEffect(() => { + fieldHooks.register('chat', ChatSectionHook, 'replace') + return () => fieldHooks.unregister('chat') +}, []) +``` + +## API Reference + +### DynamicConfigForm +| Prop | Type | Description | +|------|------|-------------| +| `schema` | `ConfigSchema` | The schema generated by the backend. | +| `values` | `Record` | Current configuration values. | +| `onChange` | `(field: string, value: any) => void` | Change handler. | +| `hooks` | `FieldHookRegistry` | Optional custom hook registry. | + +### FieldHookRegistry +- `register(path, component, type)`: Register a hook. +- `get(path)`: Retrieve a registered hook. +- `has(path)`: Check if a hook exists. +- `unregister(path)`: Remove a hook. + +## Troubleshooting +- **Hook not rendering**: Ensure the registration path matches the schema field name exactly (e.g., `chat` vs `Chat`). +- **Field missing**: Check if the field is present in the `ConfigSchema` returned by the backend. +- **TypeScript errors**: Ensure your Hook implements the `FieldHookComponent` type.