@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,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Page Complete Example
|
|
3
|
+
* Shows how to integrate Dynamic Page system with HsafaChat and ContentContainer
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useState } from 'react';
|
|
7
|
+
import {
|
|
8
|
+
HsafaProvider,
|
|
9
|
+
HsafaChat,
|
|
10
|
+
ContentContainer,
|
|
11
|
+
DynamicPageTypeConfig,
|
|
12
|
+
} from '@hsafa/ui-sdk';
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Step 1: Create Your Custom Components
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
// Simple Chart Component
|
|
19
|
+
function ChartComponent({ data }: { data: any }) {
|
|
20
|
+
const maxValue = Math.max(
|
|
21
|
+
...((data.series || []).flatMap((s: any) => s.values || []))
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div style={{ width: '100%', height: '100%', padding: '8px' }}>
|
|
26
|
+
<h4 style={{ margin: '0 0 12px 0', fontSize: '14px', fontWeight: 600 }}>
|
|
27
|
+
{data.title || 'Chart'}
|
|
28
|
+
</h4>
|
|
29
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
30
|
+
{(data.series || []).map((series: any, i: number) => (
|
|
31
|
+
<div key={i}>
|
|
32
|
+
<div style={{ fontSize: '12px', marginBottom: '6px', opacity: 0.7 }}>
|
|
33
|
+
{series.name}
|
|
34
|
+
</div>
|
|
35
|
+
<div
|
|
36
|
+
style={{
|
|
37
|
+
display: 'flex',
|
|
38
|
+
gap: '4px',
|
|
39
|
+
alignItems: 'flex-end',
|
|
40
|
+
height: '80px',
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{(series.values || []).map((value: number, j: number) => (
|
|
44
|
+
<div
|
|
45
|
+
key={j}
|
|
46
|
+
style={{
|
|
47
|
+
flex: 1,
|
|
48
|
+
backgroundColor: '#3b82f6',
|
|
49
|
+
height: `${(value / maxValue) * 100}%`,
|
|
50
|
+
borderRadius: '4px 4px 0 0',
|
|
51
|
+
minWidth: '20px',
|
|
52
|
+
position: 'relative',
|
|
53
|
+
}}
|
|
54
|
+
title={`${series.name}: ${value}`}
|
|
55
|
+
>
|
|
56
|
+
<span
|
|
57
|
+
style={{
|
|
58
|
+
position: 'absolute',
|
|
59
|
+
top: '-20px',
|
|
60
|
+
left: '50%',
|
|
61
|
+
transform: 'translateX(-50%)',
|
|
62
|
+
fontSize: '11px',
|
|
63
|
+
whiteSpace: 'nowrap',
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
{value}
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
))}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Simple Table Component
|
|
79
|
+
function TableComponent({ data }: { data: any }) {
|
|
80
|
+
return (
|
|
81
|
+
<div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
|
|
82
|
+
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
|
83
|
+
<thead>
|
|
84
|
+
<tr>
|
|
85
|
+
{(data.columns || []).map((col: string, i: number) => (
|
|
86
|
+
<th
|
|
87
|
+
key={i}
|
|
88
|
+
style={{
|
|
89
|
+
padding: '8px',
|
|
90
|
+
textAlign: 'left',
|
|
91
|
+
borderBottom: '2px solid #e5e7eb',
|
|
92
|
+
fontSize: '12px',
|
|
93
|
+
fontWeight: 600,
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
{col}
|
|
97
|
+
</th>
|
|
98
|
+
))}
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
{(data.rows || []).map((row: any[], i: number) => (
|
|
103
|
+
<tr key={i}>
|
|
104
|
+
{row.map((cell, j) => (
|
|
105
|
+
<td
|
|
106
|
+
key={j}
|
|
107
|
+
style={{
|
|
108
|
+
padding: '8px',
|
|
109
|
+
borderBottom: '1px solid #e5e7eb',
|
|
110
|
+
fontSize: '13px',
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{cell}
|
|
114
|
+
</td>
|
|
115
|
+
))}
|
|
116
|
+
</tr>
|
|
117
|
+
))}
|
|
118
|
+
</tbody>
|
|
119
|
+
</table>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Simple Text Component
|
|
125
|
+
function TextComponent({ data }: { data: any }) {
|
|
126
|
+
return (
|
|
127
|
+
<div style={{ width: '100%', height: '100%', padding: '16px' }}>
|
|
128
|
+
{data.heading && (
|
|
129
|
+
<h2 style={{ margin: '0 0 12px 0', fontSize: '20px', fontWeight: 700 }}>
|
|
130
|
+
{data.heading}
|
|
131
|
+
</h2>
|
|
132
|
+
)}
|
|
133
|
+
{data.content && (
|
|
134
|
+
<p style={{ margin: 0, fontSize: '14px', lineHeight: 1.6 }}>
|
|
135
|
+
{data.content}
|
|
136
|
+
</p>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Metric Card Component
|
|
143
|
+
function MetricComponent({ data }: { data: any }) {
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
style={{
|
|
147
|
+
width: '100%',
|
|
148
|
+
height: '100%',
|
|
149
|
+
display: 'flex',
|
|
150
|
+
flexDirection: 'column',
|
|
151
|
+
alignItems: 'center',
|
|
152
|
+
justifyContent: 'center',
|
|
153
|
+
padding: '24px',
|
|
154
|
+
textAlign: 'center',
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<div style={{ fontSize: '36px', fontWeight: 700, marginBottom: '8px' }}>
|
|
158
|
+
{data.value}
|
|
159
|
+
</div>
|
|
160
|
+
<div style={{ fontSize: '14px', opacity: 0.7 }}>{data.label}</div>
|
|
161
|
+
{data.change && (
|
|
162
|
+
<div
|
|
163
|
+
style={{
|
|
164
|
+
marginTop: '8px',
|
|
165
|
+
fontSize: '12px',
|
|
166
|
+
color: data.change > 0 ? '#10b981' : '#ef4444',
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
{data.change > 0 ? '↑' : '↓'} {Math.abs(data.change)}%
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Step 2: Register Your Types
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
const dynamicPageTypes: DynamicPageTypeConfig[] = [
|
|
181
|
+
{
|
|
182
|
+
type: 'chart',
|
|
183
|
+
component: ChartComponent,
|
|
184
|
+
description: 'Display data as bar charts with multiple series',
|
|
185
|
+
variants: ['bar', 'line', 'area'],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'table',
|
|
189
|
+
component: TableComponent,
|
|
190
|
+
description: 'Display data in a tabular format with rows and columns',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'text',
|
|
194
|
+
component: TextComponent,
|
|
195
|
+
description: 'Display text content with optional heading',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
type: 'metric',
|
|
199
|
+
component: MetricComponent,
|
|
200
|
+
description: 'Display a single metric value with label and change indicator',
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// Step 3: Create Your App Component
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
export function DynamicPageExampleApp() {
|
|
209
|
+
const [agentId] = useState('dynamic-page-demo');
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<HsafaProvider baseUrl="http://localhost:3000">
|
|
213
|
+
<ContentContainer
|
|
214
|
+
theme="dark"
|
|
215
|
+
enableBorderAnimation={true}
|
|
216
|
+
enableMargin={true}
|
|
217
|
+
chatWidth={420}
|
|
218
|
+
>
|
|
219
|
+
<div
|
|
220
|
+
style={{
|
|
221
|
+
padding: '32px',
|
|
222
|
+
display: 'flex',
|
|
223
|
+
flexDirection: 'column',
|
|
224
|
+
gap: '16px',
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
<h1>Dynamic Page Example</h1>
|
|
228
|
+
<p>
|
|
229
|
+
Try asking the agent to create visualizations! For example:
|
|
230
|
+
</p>
|
|
231
|
+
<ul>
|
|
232
|
+
<li>"Create a 2x2 dashboard grid"</li>
|
|
233
|
+
<li>"Add a sales chart to the top-left area"</li>
|
|
234
|
+
<li>"Show me what types of visualizations you can create"</li>
|
|
235
|
+
<li>"Add a table with customer data to the bottom area"</li>
|
|
236
|
+
<li>"Update the chart to show Q4 data"</li>
|
|
237
|
+
</ul>
|
|
238
|
+
</div>
|
|
239
|
+
</ContentContainer>
|
|
240
|
+
|
|
241
|
+
<HsafaChat
|
|
242
|
+
agentId={agentId}
|
|
243
|
+
theme="dark"
|
|
244
|
+
defaultOpen={true}
|
|
245
|
+
dynamicPageTypes={dynamicPageTypes}
|
|
246
|
+
/>
|
|
247
|
+
</HsafaProvider>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// Example Agent Prompts and Expected Behavior
|
|
253
|
+
// ============================================================================
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
|
|
257
|
+
EXAMPLE 1: Create a Dashboard
|
|
258
|
+
User: "Create a dashboard with 3 rows and 2 columns"
|
|
259
|
+
Agent:
|
|
260
|
+
1. Calls setGrid with gridTemplateColumns: "1fr 1fr", gridTemplateRows: "auto 1fr 1fr"
|
|
261
|
+
2. Returns: "Grid created with 3 rows and 2 columns"
|
|
262
|
+
|
|
263
|
+
EXAMPLE 2: Add a Chart
|
|
264
|
+
User: "Add a sales chart showing monthly data"
|
|
265
|
+
Agent:
|
|
266
|
+
1. Calls readAvailableTypes() to see what's available
|
|
267
|
+
2. Calls setObject with:
|
|
268
|
+
{
|
|
269
|
+
object_name: "sales_chart",
|
|
270
|
+
type: "chart",
|
|
271
|
+
data: {
|
|
272
|
+
title: "Monthly Sales",
|
|
273
|
+
series: [{
|
|
274
|
+
name: "Revenue",
|
|
275
|
+
values: [1000, 1200, 1100, 1500]
|
|
276
|
+
}]
|
|
277
|
+
},
|
|
278
|
+
meta: { title: "Sales Chart" }
|
|
279
|
+
}
|
|
280
|
+
3. Returns: "Chart added successfully"
|
|
281
|
+
|
|
282
|
+
EXAMPLE 3: Update Existing Data
|
|
283
|
+
User: "Add December data with value 1800 to the sales chart"
|
|
284
|
+
Agent:
|
|
285
|
+
1. Calls editObject with:
|
|
286
|
+
{
|
|
287
|
+
object_name: "sales_chart",
|
|
288
|
+
json_patch: [
|
|
289
|
+
{ op: "add", path: "/data/series/0/values/-", value: 1800 }
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
2. Returns: "Chart updated successfully"
|
|
293
|
+
|
|
294
|
+
EXAMPLE 4: Create Complex Layout
|
|
295
|
+
User: "Create a dashboard with header, main chart area, sidebar for metrics, and footer"
|
|
296
|
+
Agent:
|
|
297
|
+
1. Calls setGrid with:
|
|
298
|
+
{
|
|
299
|
+
gridTemplateColumns: "2fr 1fr",
|
|
300
|
+
gridTemplateRows: "auto 1fr auto",
|
|
301
|
+
gap: "16px",
|
|
302
|
+
gridTemplateAreas: `
|
|
303
|
+
"header header"
|
|
304
|
+
"main sidebar"
|
|
305
|
+
"footer footer"
|
|
306
|
+
`
|
|
307
|
+
}
|
|
308
|
+
2. Adds multiple objects using setObject for each area
|
|
309
|
+
3. Returns: "Dashboard created with 4 sections"
|
|
310
|
+
|
|
311
|
+
EXAMPLE 5: Handle Errors Gracefully
|
|
312
|
+
User: "Delete the chart named 'nonexistent'"
|
|
313
|
+
Agent:
|
|
314
|
+
1. Calls deleteObject({ object_name: "nonexistent" })
|
|
315
|
+
2. Receives: { success: false, message: "Object 'nonexistent' not found..." }
|
|
316
|
+
3. Responds: "I couldn't find a chart named 'nonexistent'. Here are the available objects: ..."
|
|
317
|
+
|
|
318
|
+
*/
|
|
319
|
+
|
|
320
|
+
// ============================================================================
|
|
321
|
+
// Advanced Example: With State Management
|
|
322
|
+
// ============================================================================
|
|
323
|
+
|
|
324
|
+
export function AdvancedDynamicPageExample() {
|
|
325
|
+
const [chatId, setChatId] = useState(
|
|
326
|
+
`chat_${Date.now()}_${Math.random().toString(36).slice(2)}`
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
// You can track when dynamic page opens
|
|
330
|
+
const [dynamicPageActive, setDynamicPageActive] = useState(false);
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<HsafaProvider baseUrl="http://localhost:3000">
|
|
334
|
+
<div style={{ position: 'relative', width: '100vw', height: '100vh' }}>
|
|
335
|
+
{dynamicPageActive && (
|
|
336
|
+
<div
|
|
337
|
+
style={{
|
|
338
|
+
position: 'absolute',
|
|
339
|
+
top: '16px',
|
|
340
|
+
left: '16px',
|
|
341
|
+
zIndex: 1000,
|
|
342
|
+
padding: '8px 16px',
|
|
343
|
+
backgroundColor: '#10b981',
|
|
344
|
+
color: 'white',
|
|
345
|
+
borderRadius: '8px',
|
|
346
|
+
fontSize: '14px',
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
Dynamic Page Active
|
|
350
|
+
</div>
|
|
351
|
+
)}
|
|
352
|
+
|
|
353
|
+
<ContentContainer
|
|
354
|
+
theme="dark"
|
|
355
|
+
enableBorderAnimation={true}
|
|
356
|
+
>
|
|
357
|
+
<YourMainApp />
|
|
358
|
+
</ContentContainer>
|
|
359
|
+
|
|
360
|
+
<HsafaChat
|
|
361
|
+
agentId="advanced-demo"
|
|
362
|
+
dynamicPageTypes={dynamicPageTypes}
|
|
363
|
+
onDynamicPageChange={(isActive) => setDynamicPageActive(isActive)}
|
|
364
|
+
/>
|
|
365
|
+
</div>
|
|
366
|
+
</HsafaProvider>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Placeholder for your main app
|
|
371
|
+
function YourMainApp() {
|
|
372
|
+
return (
|
|
373
|
+
<div style={{ padding: '32px' }}>
|
|
374
|
+
<h1>Your Application</h1>
|
|
375
|
+
<p>When the agent creates a dynamic page, it will overlay your content.</p>
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export default DynamicPageExampleApp;
|