@hsafa/ui-sdk 0.3.3 → 0.4.1
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/README.md +133 -2
- package/dist/index.cjs +23 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +572 -20
- package/dist/index.d.ts +572 -20
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/docs/CUSTOM_UI_EXAMPLES.md +309 -0
- package/docs/DYNAMIC_PAGE_SCHEMAS.md +261 -0
- package/docs/HEADLESS_QUICK_REFERENCE.md +426 -0
- package/docs/HEADLESS_USAGE.md +682 -0
- package/docs/MIGRATION_TO_HEADLESS.md +408 -0
- package/docs/README.md +43 -71
- package/docs/handbook/00-Overview.md +69 -0
- package/docs/handbook/01-Quickstart.md +133 -0
- package/docs/handbook/02-Architecture.md +75 -0
- package/docs/handbook/03-Components-and-Hooks.md +81 -0
- package/docs/handbook/04-Streaming-and-Transport.md +73 -0
- package/docs/handbook/05-Tools-and-UI.md +73 -0
- package/docs/handbook/06-Storage-and-History.md +63 -0
- package/docs/handbook/07-Dynamic-Pages.md +49 -0
- package/docs/handbook/08-Server-Integration.md +84 -0
- package/docs/handbook/09-Agent-Studio-Client.md +40 -0
- package/docs/handbook/10-Examples-and-Recipes.md +154 -0
- package/docs/handbook/11-API-Reference-Map.md +48 -0
- package/docs/handbook/README.md +24 -0
- package/examples/custom-tools-example.tsx +401 -0
- package/examples/custom-ui-customizations-example.tsx +543 -0
- package/examples/dynamic-page-example.tsx +380 -0
- package/examples/headless-chat-example.tsx +537 -0
- package/examples/minimal-headless-example.tsx +142 -0
- package/package.json +3 -2
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Custom UI Customization Examples
|
|
2
|
+
|
|
3
|
+
This document provides examples for the customization features added to the `HsafaChat` component.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Custom Tool UI with HsafaUI](#1-custom-tool-ui-with-hsafaui)
|
|
8
|
+
2. [Component Above Chat Input](#2-component-above-chat-input)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Custom Tool UI with HsafaUI
|
|
13
|
+
|
|
14
|
+
Create custom UIs for specific tool calls using the `HsafaUI` prop. Your components will receive all tool data including `addToolResult` callback to send results back to the agent.
|
|
15
|
+
|
|
16
|
+
### Usage Example
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { HsafaChat, CustomToolUIRenderProps } from '@hsafa/sdk';
|
|
20
|
+
|
|
21
|
+
// Custom UI component for a tool
|
|
22
|
+
function ChoiceToolUI({ toolName, toolCallId, input, output, status, addToolResult, ...restInputProps }: CustomToolUIRenderProps & any) {
|
|
23
|
+
const [selectedChoice, setSelectedChoice] = useState<string | null>(null);
|
|
24
|
+
|
|
25
|
+
const handleChoice = (choice: string) => {
|
|
26
|
+
setSelectedChoice(choice);
|
|
27
|
+
|
|
28
|
+
// Send the result back to the agent
|
|
29
|
+
addToolResult({
|
|
30
|
+
tool: toolName,
|
|
31
|
+
toolCallId: toolCallId,
|
|
32
|
+
output: {
|
|
33
|
+
choice: choice,
|
|
34
|
+
timestamp: Date.now()
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div style={{ padding: '16px', border: '1px solid #ddd', borderRadius: '8px' }}>
|
|
41
|
+
<h3>Make a choice:</h3>
|
|
42
|
+
<p>{input?.question || 'Please select an option'}</p>
|
|
43
|
+
<div style={{ display: 'flex', gap: '8px', marginTop: '12px' }}>
|
|
44
|
+
<button
|
|
45
|
+
onClick={() => handleChoice('yes')}
|
|
46
|
+
disabled={selectedChoice !== null}
|
|
47
|
+
style={{
|
|
48
|
+
padding: '8px 16px',
|
|
49
|
+
backgroundColor: selectedChoice === 'yes' ? '#10b981' : '#3b82f6',
|
|
50
|
+
color: 'white',
|
|
51
|
+
border: 'none',
|
|
52
|
+
borderRadius: '6px',
|
|
53
|
+
cursor: selectedChoice ? 'default' : 'pointer'
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
Yes
|
|
57
|
+
</button>
|
|
58
|
+
<button
|
|
59
|
+
onClick={() => handleChoice('no')}
|
|
60
|
+
disabled={selectedChoice !== null}
|
|
61
|
+
style={{
|
|
62
|
+
padding: '8px 16px',
|
|
63
|
+
backgroundColor: selectedChoice === 'no' ? '#10b981' : '#ef4444',
|
|
64
|
+
color: 'white',
|
|
65
|
+
border: 'none',
|
|
66
|
+
borderRadius: '6px',
|
|
67
|
+
cursor: selectedChoice ? 'default' : 'pointer'
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
No
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
{selectedChoice && (
|
|
74
|
+
<p style={{ marginTop: '8px', color: '#10b981' }}>
|
|
75
|
+
✓ Choice sent: {selectedChoice}
|
|
76
|
+
</p>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Use in HsafaChat with HsafaUI prop
|
|
83
|
+
function App() {
|
|
84
|
+
return (
|
|
85
|
+
<HsafaChat
|
|
86
|
+
agentId="my-agent"
|
|
87
|
+
HsafaUI={{
|
|
88
|
+
'getUserChoice': ChoiceToolUI,
|
|
89
|
+
'confirmAction': ChoiceToolUI,
|
|
90
|
+
}}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Props Available
|
|
97
|
+
|
|
98
|
+
Your HsafaUI components receive **both** the standard tool data **and** all input parameters:
|
|
99
|
+
|
|
100
|
+
- `toolName: string` - Name of the tool being called
|
|
101
|
+
- `toolCallId: string` - Unique ID for this tool call instance
|
|
102
|
+
- `input: any` - Input parameters object
|
|
103
|
+
- `output: any` - Output from the tool (if available)
|
|
104
|
+
- `status?: string` - Current status of the tool call
|
|
105
|
+
- `addToolResult: (result: any) => void` - Function to send result back to the agent
|
|
106
|
+
- `...restInputProps` - All input parameters are also spread as individual props
|
|
107
|
+
|
|
108
|
+
### Key Points
|
|
109
|
+
|
|
110
|
+
- Use the **HsafaUI** prop (same as for regular UI components)
|
|
111
|
+
- Components automatically receive **enhanced props** including `addToolResult`
|
|
112
|
+
- The custom UI has **full control** over when to send results
|
|
113
|
+
- You can create interactive UIs that wait for user input
|
|
114
|
+
- Multiple buttons/actions can send different results
|
|
115
|
+
- Access tool data via structured props OR spread input props
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 2. Component Above Chat Input
|
|
120
|
+
|
|
121
|
+
Add a persistent component above the chat input (e.g., quick actions, status bar, suggestions).
|
|
122
|
+
|
|
123
|
+
### Usage Example
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { HsafaChat } from '@hsafa/sdk';
|
|
127
|
+
|
|
128
|
+
function QuickActions() {
|
|
129
|
+
const handleQuickAction = (action: string) => {
|
|
130
|
+
console.log('Quick action:', action);
|
|
131
|
+
// You can trigger actions here
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div style={{
|
|
136
|
+
padding: '12px',
|
|
137
|
+
backgroundColor: '#f9fafb',
|
|
138
|
+
borderRadius: '8px',
|
|
139
|
+
marginBottom: '8px'
|
|
140
|
+
}}>
|
|
141
|
+
<p style={{
|
|
142
|
+
fontSize: '12px',
|
|
143
|
+
color: '#6B7280',
|
|
144
|
+
marginBottom: '8px',
|
|
145
|
+
fontWeight: 500
|
|
146
|
+
}}>
|
|
147
|
+
Quick Actions:
|
|
148
|
+
</p>
|
|
149
|
+
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
|
150
|
+
<button
|
|
151
|
+
onClick={() => handleQuickAction('summarize')}
|
|
152
|
+
style={{
|
|
153
|
+
padding: '6px 12px',
|
|
154
|
+
backgroundColor: 'white',
|
|
155
|
+
border: '1px solid #E5E7EB',
|
|
156
|
+
borderRadius: '6px',
|
|
157
|
+
fontSize: '13px',
|
|
158
|
+
cursor: 'pointer',
|
|
159
|
+
transition: 'all 0.2s'
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
📝 Summarize
|
|
163
|
+
</button>
|
|
164
|
+
<button
|
|
165
|
+
onClick={() => handleQuickAction('analyze')}
|
|
166
|
+
style={{
|
|
167
|
+
padding: '6px 12px',
|
|
168
|
+
backgroundColor: 'white',
|
|
169
|
+
border: '1px solid #E5E7EB',
|
|
170
|
+
borderRadius: '6px',
|
|
171
|
+
fontSize: '13px',
|
|
172
|
+
cursor: 'pointer',
|
|
173
|
+
transition: 'all 0.2s'
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
📊 Analyze
|
|
177
|
+
</button>
|
|
178
|
+
<button
|
|
179
|
+
onClick={() => handleQuickAction('translate')}
|
|
180
|
+
style={{
|
|
181
|
+
padding: '6px 12px',
|
|
182
|
+
backgroundColor: 'white',
|
|
183
|
+
border: '1px solid #E5E7EB',
|
|
184
|
+
borderRadius: '6px',
|
|
185
|
+
fontSize: '13px',
|
|
186
|
+
cursor: 'pointer',
|
|
187
|
+
transition: 'all 0.2s'
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
🌐 Translate
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Example 2: Smart Suggestions
|
|
198
|
+
function SmartSuggestions() {
|
|
199
|
+
const suggestions = [
|
|
200
|
+
"What can you help me with?",
|
|
201
|
+
"Show me recent updates",
|
|
202
|
+
"Generate a report"
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div style={{
|
|
207
|
+
padding: '8px',
|
|
208
|
+
backgroundColor: '#EEF2FF',
|
|
209
|
+
borderRadius: '8px',
|
|
210
|
+
marginBottom: '8px'
|
|
211
|
+
}}>
|
|
212
|
+
<p style={{ fontSize: '11px', color: '#6366F1', marginBottom: '6px' }}>
|
|
213
|
+
💡 Try asking:
|
|
214
|
+
</p>
|
|
215
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
|
216
|
+
{suggestions.map((suggestion, i) => (
|
|
217
|
+
<div
|
|
218
|
+
key={i}
|
|
219
|
+
style={{
|
|
220
|
+
padding: '6px 10px',
|
|
221
|
+
backgroundColor: 'white',
|
|
222
|
+
borderRadius: '4px',
|
|
223
|
+
fontSize: '12px',
|
|
224
|
+
cursor: 'pointer',
|
|
225
|
+
border: '1px solid #C7D2FE'
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
{suggestion}
|
|
229
|
+
</div>
|
|
230
|
+
))}
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Use in HsafaChat
|
|
237
|
+
function App() {
|
|
238
|
+
return (
|
|
239
|
+
<HsafaChat
|
|
240
|
+
agentId="my-agent"
|
|
241
|
+
componentAboveInput={QuickActions}
|
|
242
|
+
// Or use SmartSuggestions
|
|
243
|
+
// componentAboveInput={SmartSuggestions}
|
|
244
|
+
/>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Key Points
|
|
250
|
+
|
|
251
|
+
- Component is rendered **above** the chat input
|
|
252
|
+
- Always visible (sticky with the input area)
|
|
253
|
+
- Perfect for:
|
|
254
|
+
- Quick action buttons
|
|
255
|
+
- Smart suggestions
|
|
256
|
+
- Status indicators
|
|
257
|
+
- Context-aware tools
|
|
258
|
+
- File upload shortcuts
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Complete Example
|
|
263
|
+
|
|
264
|
+
Combining all customizations:
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import { HsafaChat } from '@hsafa/sdk';
|
|
268
|
+
import { ChoiceToolUI } from './components/ChoiceToolUI';
|
|
269
|
+
import { QuickActions } from './components/QuickActions';
|
|
270
|
+
|
|
271
|
+
function App() {
|
|
272
|
+
return (
|
|
273
|
+
<HsafaChat
|
|
274
|
+
agentId="my-agent"
|
|
275
|
+
theme="dark"
|
|
276
|
+
|
|
277
|
+
// Custom tool UIs (same prop as regular UI components!)
|
|
278
|
+
HsafaUI={{
|
|
279
|
+
'getUserChoice': ChoiceToolUI,
|
|
280
|
+
'confirmAction': ChoiceToolUI,
|
|
281
|
+
}}
|
|
282
|
+
|
|
283
|
+
// Component above input
|
|
284
|
+
componentAboveInput={QuickActions}
|
|
285
|
+
|
|
286
|
+
// Other props...
|
|
287
|
+
HsafaTools={{
|
|
288
|
+
// Your custom tools
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export default App;
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## TypeScript Types
|
|
300
|
+
|
|
301
|
+
All types are exported from the SDK:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
import type {
|
|
305
|
+
CustomToolUIRenderProps,
|
|
306
|
+
CustomEditModalRenderProps,
|
|
307
|
+
Attachment
|
|
308
|
+
} from '@hsafa/sdk';
|
|
309
|
+
```
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Dynamic Page Schemas Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to use optional Zod schemas and examples with Dynamic Page component types.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Dynamic Page types can now include optional **schemas** and **examples** to help the AI agent understand the expected data structure for each component type. When the agent calls `readAvailableTypes`, it receives detailed information about each type including:
|
|
8
|
+
|
|
9
|
+
- **Type name** and **description**
|
|
10
|
+
- **Schema** (JSON Schema format or Zod schema info)
|
|
11
|
+
- **Examples** showing valid data structures
|
|
12
|
+
- **Variants** (if applicable)
|
|
13
|
+
|
|
14
|
+
## Adding Schemas to Types
|
|
15
|
+
|
|
16
|
+
### JSON Schema Format (Recommended)
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const dynamicPageTypes: DynamicPageTypeConfig[] = [
|
|
20
|
+
{
|
|
21
|
+
type: 'chart',
|
|
22
|
+
component: ChartComponent,
|
|
23
|
+
description: 'Display data as interactive bar charts',
|
|
24
|
+
schema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
title: { type: 'string', description: 'Chart title' },
|
|
28
|
+
series: {
|
|
29
|
+
type: 'array',
|
|
30
|
+
items: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
name: { type: 'string' },
|
|
34
|
+
values: { type: 'array', items: { type: 'number' } }
|
|
35
|
+
},
|
|
36
|
+
required: ['name', 'values']
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
required: ['series']
|
|
41
|
+
},
|
|
42
|
+
examples: [
|
|
43
|
+
{
|
|
44
|
+
title: 'Monthly Sales',
|
|
45
|
+
series: [
|
|
46
|
+
{ name: '2024', values: [100, 150, 200] },
|
|
47
|
+
{ name: '2025', values: [120, 180, 220] }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Using Zod Schemas
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { z } from 'zod';
|
|
59
|
+
|
|
60
|
+
const chartSchema = z.object({
|
|
61
|
+
title: z.string().optional(),
|
|
62
|
+
series: z.array(z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
values: z.array(z.number())
|
|
65
|
+
}))
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const dynamicPageTypes: DynamicPageTypeConfig[] = [
|
|
69
|
+
{
|
|
70
|
+
type: 'chart',
|
|
71
|
+
component: ChartComponent,
|
|
72
|
+
description: 'Display data as interactive bar charts',
|
|
73
|
+
schema: chartSchema, // Zod schema will be serialized automatically
|
|
74
|
+
examples: [
|
|
75
|
+
{
|
|
76
|
+
title: 'Monthly Sales',
|
|
77
|
+
series: [
|
|
78
|
+
{ name: '2024', values: [100, 150, 200] }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## What the Agent Sees
|
|
87
|
+
|
|
88
|
+
When calling `readAvailableTypes()`, the agent receives:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"success": true,
|
|
93
|
+
"message": "Found 6 registered component type(s)...",
|
|
94
|
+
"types": [
|
|
95
|
+
{
|
|
96
|
+
"type": "chart",
|
|
97
|
+
"description": "Display data as interactive bar charts with multiple series",
|
|
98
|
+
"variants": ["bar", "line", "area"],
|
|
99
|
+
"has_schema": true,
|
|
100
|
+
"schema": {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"properties": {
|
|
103
|
+
"title": { "type": "string", "description": "Chart title" },
|
|
104
|
+
"series": {
|
|
105
|
+
"type": "array",
|
|
106
|
+
"items": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"properties": {
|
|
109
|
+
"name": { "type": "string" },
|
|
110
|
+
"values": { "type": "array", "items": { "type": "number" } }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"examples": [
|
|
117
|
+
{
|
|
118
|
+
"title": "Monthly Sales",
|
|
119
|
+
"series": [
|
|
120
|
+
{ "name": "2024", "values": [100, 150, 200] }
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"example_count": 1
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"available_types": ["chart", "table", "metric", "text", "progress", "status"],
|
|
128
|
+
"total_count": 6,
|
|
129
|
+
"types_with_schema": 6
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Benefits
|
|
134
|
+
|
|
135
|
+
1. **Type Safety**: Agents know exactly what data structure each component expects
|
|
136
|
+
2. **Better Errors**: Detailed validation errors when data doesn't match schema
|
|
137
|
+
3. **Discoverability**: Agents can explore available types and their requirements
|
|
138
|
+
4. **Examples**: Concrete examples help agents understand complex structures
|
|
139
|
+
5. **Documentation**: Self-documenting component types
|
|
140
|
+
|
|
141
|
+
## Best Practices
|
|
142
|
+
|
|
143
|
+
### 1. Always Provide a Description
|
|
144
|
+
```typescript
|
|
145
|
+
{
|
|
146
|
+
type: 'metric',
|
|
147
|
+
description: 'Showcase KPIs with large numbers and change indicators', // Clear description
|
|
148
|
+
component: MetricComponent
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 2. Include Examples for Complex Types
|
|
153
|
+
```typescript
|
|
154
|
+
{
|
|
155
|
+
type: 'table',
|
|
156
|
+
schema: { /* complex schema */ },
|
|
157
|
+
examples: [
|
|
158
|
+
{
|
|
159
|
+
columns: [
|
|
160
|
+
{ key: 'name', title: 'Name' },
|
|
161
|
+
{ key: 'value', title: 'Value' }
|
|
162
|
+
],
|
|
163
|
+
rows: [
|
|
164
|
+
{ name: 'Item 1', value: '100' }
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 3. Document Required vs Optional Fields
|
|
172
|
+
```typescript
|
|
173
|
+
schema: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
value: { type: 'string', description: 'The metric value (required)' },
|
|
177
|
+
label: { type: 'string', description: 'Metric label (required)' },
|
|
178
|
+
change: { type: 'string', description: 'Change indicator (optional)' }
|
|
179
|
+
},
|
|
180
|
+
required: ['value', 'label'] // Explicitly mark required fields
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 4. Use Enums for Limited Options
|
|
185
|
+
```typescript
|
|
186
|
+
schema: {
|
|
187
|
+
type: 'object',
|
|
188
|
+
properties: {
|
|
189
|
+
status: {
|
|
190
|
+
type: 'string',
|
|
191
|
+
enum: ['success', 'warning', 'error', 'info'],
|
|
192
|
+
description: 'Status type'
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Schema Serialization
|
|
199
|
+
|
|
200
|
+
- **JSON Schema**: Passed through as-is
|
|
201
|
+
- **Zod Schema**: Automatically converted to a basic representation with type info
|
|
202
|
+
- **Plain Objects**: Treated as JSON Schema
|
|
203
|
+
|
|
204
|
+
## Example: Complete Type Definition
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
{
|
|
208
|
+
type: 'progress',
|
|
209
|
+
component: ProgressComponent,
|
|
210
|
+
description: 'Show progress bars with percentages and colors',
|
|
211
|
+
schema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
items: {
|
|
215
|
+
type: 'array',
|
|
216
|
+
description: 'Array of progress items',
|
|
217
|
+
items: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
label: { type: 'string', description: 'Item label' },
|
|
221
|
+
progress: {
|
|
222
|
+
type: 'number',
|
|
223
|
+
minimum: 0,
|
|
224
|
+
maximum: 100,
|
|
225
|
+
description: 'Progress percentage (0-100)'
|
|
226
|
+
},
|
|
227
|
+
color: { type: 'string', description: 'Optional color' }
|
|
228
|
+
},
|
|
229
|
+
required: ['label', 'progress']
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
required: ['items']
|
|
234
|
+
},
|
|
235
|
+
examples: [
|
|
236
|
+
{
|
|
237
|
+
items: [
|
|
238
|
+
{ label: 'Task 1', progress: 75, color: '#3b82f6' },
|
|
239
|
+
{ label: 'Task 2', progress: 40 }
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Testing Your Schemas
|
|
247
|
+
|
|
248
|
+
1. Call `readAvailableTypes()` and verify the schema appears correctly
|
|
249
|
+
2. Try creating objects with valid data matching the schema
|
|
250
|
+
3. Try creating objects with invalid data to test error messages
|
|
251
|
+
4. Check that examples render correctly in the UI
|
|
252
|
+
|
|
253
|
+
## Migration Guide
|
|
254
|
+
|
|
255
|
+
Existing types without schemas will continue to work. To add schemas:
|
|
256
|
+
|
|
257
|
+
1. Add a `schema` field to your type config (JSON Schema or Zod)
|
|
258
|
+
2. Optionally add `examples` array
|
|
259
|
+
3. Rebuild and test with `readAvailableTypes()`
|
|
260
|
+
|
|
261
|
+
No breaking changes - schemas are purely additive!
|