@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.
@@ -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!