@devicai/ui 0.1.0
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 +363 -0
- package/dist/cjs/api/client.js +118 -0
- package/dist/cjs/api/client.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js +122 -0
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/ChatInput.js +128 -0
- package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/ChatMessages.js +56 -0
- package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/ToolTimeline.js +26 -0
- package/dist/cjs/components/ChatDrawer/ToolTimeline.js.map +1 -0
- package/dist/cjs/hooks/useDevicChat.js +258 -0
- package/dist/cjs/hooks/useDevicChat.js.map +1 -0
- package/dist/cjs/hooks/useModelInterface.js +130 -0
- package/dist/cjs/hooks/useModelInterface.js.map +1 -0
- package/dist/cjs/hooks/usePolling.js +109 -0
- package/dist/cjs/hooks/usePolling.js.map +1 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/provider/DevicContext.js +32 -0
- package/dist/cjs/provider/DevicContext.js.map +1 -0
- package/dist/cjs/provider/DevicProvider.js +43 -0
- package/dist/cjs/provider/DevicProvider.js.map +1 -0
- package/dist/cjs/styles.css +1 -0
- package/dist/cjs/utils/index.js +117 -0
- package/dist/cjs/utils/index.js.map +1 -0
- package/dist/esm/api/client.d.ts +56 -0
- package/dist/esm/api/client.js +115 -0
- package/dist/esm/api/client.js.map +1 -0
- package/dist/esm/api/types.d.ts +184 -0
- package/dist/esm/components/ChatDrawer/ChatDrawer.d.ts +27 -0
- package/dist/esm/components/ChatDrawer/ChatDrawer.js +120 -0
- package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -0
- package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +197 -0
- package/dist/esm/components/ChatDrawer/ChatInput.d.ts +5 -0
- package/dist/esm/components/ChatDrawer/ChatInput.js +126 -0
- package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -0
- package/dist/esm/components/ChatDrawer/ChatMessages.d.ts +5 -0
- package/dist/esm/components/ChatDrawer/ChatMessages.js +54 -0
- package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -0
- package/dist/esm/components/ChatDrawer/ToolTimeline.d.ts +5 -0
- package/dist/esm/components/ChatDrawer/ToolTimeline.js +24 -0
- package/dist/esm/components/ChatDrawer/ToolTimeline.js.map +1 -0
- package/dist/esm/components/ChatDrawer/index.d.ts +5 -0
- package/dist/esm/hooks/index.d.ts +6 -0
- package/dist/esm/hooks/useDevicChat.d.ts +120 -0
- package/dist/esm/hooks/useDevicChat.js +256 -0
- package/dist/esm/hooks/useDevicChat.js.map +1 -0
- package/dist/esm/hooks/useModelInterface.d.ts +68 -0
- package/dist/esm/hooks/useModelInterface.js +128 -0
- package/dist/esm/hooks/useModelInterface.js.map +1 -0
- package/dist/esm/hooks/usePolling.d.ts +64 -0
- package/dist/esm/hooks/usePolling.js +107 -0
- package/dist/esm/hooks/usePolling.js.map +1 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/provider/DevicContext.d.ts +15 -0
- package/dist/esm/provider/DevicContext.js +28 -0
- package/dist/esm/provider/DevicContext.js.map +1 -0
- package/dist/esm/provider/DevicProvider.d.ts +17 -0
- package/dist/esm/provider/DevicProvider.js +41 -0
- package/dist/esm/provider/DevicProvider.js.map +1 -0
- package/dist/esm/provider/index.d.ts +3 -0
- package/dist/esm/provider/types.d.ts +58 -0
- package/dist/esm/styles.css +1 -0
- package/dist/esm/utils/index.d.ts +32 -0
- package/dist/esm/utils/index.js +109 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# @devicai/ui
|
|
2
|
+
|
|
3
|
+
React component library for integrating Devic AI assistants into your application.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **ChatDrawer** - A ready-to-use chat drawer component
|
|
8
|
+
- **useDevicChat** - Hook for building custom chat UIs
|
|
9
|
+
- **Model Interface Protocol** - Support for client-side tool execution
|
|
10
|
+
- **CSS Variables** - Easy theming with CSS custom properties
|
|
11
|
+
- **TypeScript** - Full type definitions included
|
|
12
|
+
- **React 17+** - Compatible with React 17 and above
|
|
13
|
+
- **Minimal Dependencies** - Only React as a peer dependency
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @devicai/ui
|
|
19
|
+
# or
|
|
20
|
+
yarn add @devicai/ui
|
|
21
|
+
# or
|
|
22
|
+
pnpm add @devicai/ui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Using ChatDrawer (Simplest)
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { DevicProvider, ChatDrawer } from '@devicai/ui';
|
|
31
|
+
import '@devicai/ui/dist/esm/styles.css';
|
|
32
|
+
|
|
33
|
+
function App() {
|
|
34
|
+
return (
|
|
35
|
+
<DevicProvider apiKey="your-api-key">
|
|
36
|
+
<ChatDrawer
|
|
37
|
+
assistantId="my-assistant"
|
|
38
|
+
options={{
|
|
39
|
+
position: 'right',
|
|
40
|
+
welcomeMessage: 'Hello! How can I help you?',
|
|
41
|
+
suggestedMessages: ['Help me with...', 'Tell me about...'],
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
</DevicProvider>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Using the Hook (Custom UI)
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { DevicProvider, useDevicChat } from '@devicai/ui';
|
|
53
|
+
|
|
54
|
+
function CustomChat() {
|
|
55
|
+
const { messages, isLoading, sendMessage } = useDevicChat({
|
|
56
|
+
assistantId: 'my-assistant',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
{messages.map((msg) => (
|
|
62
|
+
<div key={msg.uid}>
|
|
63
|
+
<strong>{msg.role}:</strong> {msg.content.message}
|
|
64
|
+
</div>
|
|
65
|
+
))}
|
|
66
|
+
{isLoading && <div>Thinking...</div>}
|
|
67
|
+
<button onClick={() => sendMessage('Hello!')}>Send</button>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function App() {
|
|
73
|
+
return (
|
|
74
|
+
<DevicProvider apiKey="your-api-key">
|
|
75
|
+
<CustomChat />
|
|
76
|
+
</DevicProvider>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Components
|
|
82
|
+
|
|
83
|
+
### DevicProvider
|
|
84
|
+
|
|
85
|
+
Context provider for global configuration.
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<DevicProvider
|
|
89
|
+
apiKey="devic-xxx" // Required
|
|
90
|
+
baseUrl="https://api.devic.ai"
|
|
91
|
+
tenantId="tenant-123" // Optional global tenant
|
|
92
|
+
tenantMetadata={{ ... }} // Optional global metadata
|
|
93
|
+
>
|
|
94
|
+
<App />
|
|
95
|
+
</DevicProvider>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### ChatDrawer
|
|
99
|
+
|
|
100
|
+
A complete chat drawer component.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<ChatDrawer
|
|
104
|
+
assistantId="my-assistant"
|
|
105
|
+
chatUid="optional-existing-chat"
|
|
106
|
+
options={{
|
|
107
|
+
position: 'right', // 'left' | 'right'
|
|
108
|
+
width: 400,
|
|
109
|
+
defaultOpen: false,
|
|
110
|
+
color: '#1890ff', // Primary color
|
|
111
|
+
welcomeMessage: 'Hello!',
|
|
112
|
+
suggestedMessages: ['Help me...'],
|
|
113
|
+
enableFileUploads: true,
|
|
114
|
+
allowedFileTypes: { images: true, documents: true },
|
|
115
|
+
inputPlaceholder: 'Type a message...',
|
|
116
|
+
title: 'Chat Assistant',
|
|
117
|
+
showToolTimeline: true,
|
|
118
|
+
}}
|
|
119
|
+
enabledTools={['tool1', 'tool2']}
|
|
120
|
+
modelInterfaceTools={[
|
|
121
|
+
{
|
|
122
|
+
toolName: 'get_user_location',
|
|
123
|
+
schema: {
|
|
124
|
+
type: 'function',
|
|
125
|
+
function: {
|
|
126
|
+
name: 'get_user_location',
|
|
127
|
+
description: 'Get user current location',
|
|
128
|
+
parameters: { type: 'object', properties: {} }
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
callback: async () => {
|
|
132
|
+
const pos = await getCurrentPosition();
|
|
133
|
+
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]}
|
|
137
|
+
tenantId="specific-tenant" // Override provider
|
|
138
|
+
tenantMetadata={{ userId: '123' }}
|
|
139
|
+
apiKey="override-key" // Override provider
|
|
140
|
+
|
|
141
|
+
// Callbacks
|
|
142
|
+
onMessageSent={(message) => {}}
|
|
143
|
+
onMessageReceived={(message) => {}}
|
|
144
|
+
onToolCall={(toolName, params) => {}}
|
|
145
|
+
onError={(error) => {}}
|
|
146
|
+
onChatCreated={(chatUid) => {}}
|
|
147
|
+
onOpen={() => {}}
|
|
148
|
+
onClose={() => {}}
|
|
149
|
+
|
|
150
|
+
// Controlled mode
|
|
151
|
+
isOpen={true}
|
|
152
|
+
/>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Hooks
|
|
156
|
+
|
|
157
|
+
### useDevicChat
|
|
158
|
+
|
|
159
|
+
Main hook for chat functionality.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
const {
|
|
163
|
+
messages, // ChatMessage[]
|
|
164
|
+
chatUid, // string | null
|
|
165
|
+
isLoading, // boolean
|
|
166
|
+
status, // 'idle' | 'processing' | 'completed' | 'error'
|
|
167
|
+
error, // Error | null
|
|
168
|
+
sendMessage, // (message: string, options?: { files?: ChatFile[] }) => Promise<void>
|
|
169
|
+
clearChat, // () => void
|
|
170
|
+
loadChat, // (chatUid: string) => Promise<void>
|
|
171
|
+
} = useDevicChat({
|
|
172
|
+
assistantId: 'my-assistant',
|
|
173
|
+
chatUid: 'optional-existing-chat',
|
|
174
|
+
apiKey: 'override-key',
|
|
175
|
+
baseUrl: 'https://api.devic.ai',
|
|
176
|
+
tenantId: 'tenant-123',
|
|
177
|
+
tenantMetadata: { userId: '456' },
|
|
178
|
+
enabledTools: ['tool1', 'tool2'],
|
|
179
|
+
modelInterfaceTools: [...],
|
|
180
|
+
pollingInterval: 1000,
|
|
181
|
+
onMessageSent: (message) => {},
|
|
182
|
+
onMessageReceived: (message) => {},
|
|
183
|
+
onToolCall: (toolName, params) => {},
|
|
184
|
+
onError: (error) => {},
|
|
185
|
+
onChatCreated: (chatUid) => {},
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### useModelInterface
|
|
190
|
+
|
|
191
|
+
Hook for implementing the Model Interface Protocol.
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
const {
|
|
195
|
+
toolSchemas, // Tool schemas to send to API
|
|
196
|
+
isClientTool, // (name: string) => boolean
|
|
197
|
+
handleToolCalls, // (toolCalls: ToolCall[]) => Promise<ToolCallResponse[]>
|
|
198
|
+
extractPendingToolCalls, // (messages: ChatMessage[]) => ToolCall[]
|
|
199
|
+
} = useModelInterface({
|
|
200
|
+
tools: [
|
|
201
|
+
{
|
|
202
|
+
toolName: 'get_user_location',
|
|
203
|
+
schema: {
|
|
204
|
+
type: 'function',
|
|
205
|
+
function: {
|
|
206
|
+
name: 'get_user_location',
|
|
207
|
+
description: 'Get user location',
|
|
208
|
+
parameters: { type: 'object', properties: {} }
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
callback: async () => ({ lat: 40.7, lng: -74.0 })
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
onToolExecute: (toolName, params) => {},
|
|
215
|
+
onToolComplete: (toolName, result) => {},
|
|
216
|
+
onToolError: (toolName, error) => {},
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### usePolling
|
|
221
|
+
|
|
222
|
+
Hook for polling real-time chat history.
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
const {
|
|
226
|
+
data, // RealtimeChatHistory | null
|
|
227
|
+
isPolling, // boolean
|
|
228
|
+
error, // Error | null
|
|
229
|
+
start, // () => void
|
|
230
|
+
stop, // () => void
|
|
231
|
+
refetch, // () => Promise<void>
|
|
232
|
+
} = usePolling(
|
|
233
|
+
chatUid,
|
|
234
|
+
async () => client.getRealtimeHistory(assistantId, chatUid),
|
|
235
|
+
{
|
|
236
|
+
interval: 1000,
|
|
237
|
+
enabled: true,
|
|
238
|
+
stopStatuses: ['completed', 'error'],
|
|
239
|
+
onUpdate: (data) => {},
|
|
240
|
+
onStop: (data) => {},
|
|
241
|
+
onError: (error) => {},
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## API Client
|
|
247
|
+
|
|
248
|
+
Use the API client directly for advanced use cases.
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { DevicApiClient } from '@devicai/ui';
|
|
252
|
+
|
|
253
|
+
const client = new DevicApiClient({
|
|
254
|
+
apiKey: 'your-api-key',
|
|
255
|
+
baseUrl: 'https://api.devic.ai',
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Get assistants
|
|
259
|
+
const assistants = await client.getAssistants();
|
|
260
|
+
|
|
261
|
+
// Send message (async mode)
|
|
262
|
+
const { chatUid } = await client.sendMessageAsync('my-assistant', {
|
|
263
|
+
message: 'Hello!',
|
|
264
|
+
tenantId: 'tenant-123',
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Poll for results
|
|
268
|
+
const result = await client.getRealtimeHistory('my-assistant', chatUid);
|
|
269
|
+
|
|
270
|
+
// Send tool responses
|
|
271
|
+
await client.sendToolResponses('my-assistant', chatUid, [
|
|
272
|
+
{ tool_call_id: 'call_123', content: { result: 'data' }, role: 'tool' }
|
|
273
|
+
]);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Theming
|
|
277
|
+
|
|
278
|
+
Customize appearance with CSS variables:
|
|
279
|
+
|
|
280
|
+
```css
|
|
281
|
+
.devic-chat-drawer {
|
|
282
|
+
--devic-primary: #1890ff;
|
|
283
|
+
--devic-primary-hover: #40a9ff;
|
|
284
|
+
--devic-primary-light: #e6f7ff;
|
|
285
|
+
--devic-bg: #ffffff;
|
|
286
|
+
--devic-bg-secondary: #f5f5f5;
|
|
287
|
+
--devic-text: #333333;
|
|
288
|
+
--devic-text-secondary: #666666;
|
|
289
|
+
--devic-text-muted: #999999;
|
|
290
|
+
--devic-border: #e8e8e8;
|
|
291
|
+
--devic-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
292
|
+
--devic-radius: 8px;
|
|
293
|
+
--devic-radius-sm: 4px;
|
|
294
|
+
--devic-radius-lg: 16px;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Or use the `color` option in ChatDrawer:
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
<ChatDrawer
|
|
302
|
+
options={{ color: '#ff4081' }}
|
|
303
|
+
/>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Model Interface Protocol
|
|
307
|
+
|
|
308
|
+
The Model Interface Protocol allows you to define client-side tools that the assistant can call during a conversation.
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
const locationTool: ModelInterfaceTool = {
|
|
312
|
+
toolName: 'get_user_location',
|
|
313
|
+
schema: {
|
|
314
|
+
type: 'function',
|
|
315
|
+
function: {
|
|
316
|
+
name: 'get_user_location',
|
|
317
|
+
description: 'Get the user current geographic location',
|
|
318
|
+
parameters: {
|
|
319
|
+
type: 'object',
|
|
320
|
+
properties: {},
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
callback: async () => {
|
|
325
|
+
return new Promise((resolve, reject) => {
|
|
326
|
+
navigator.geolocation.getCurrentPosition(
|
|
327
|
+
(pos) => resolve({
|
|
328
|
+
latitude: pos.coords.latitude,
|
|
329
|
+
longitude: pos.coords.longitude,
|
|
330
|
+
}),
|
|
331
|
+
(err) => reject(err)
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
<ChatDrawer
|
|
338
|
+
assistantId="my-assistant"
|
|
339
|
+
modelInterfaceTools={[locationTool]}
|
|
340
|
+
/>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## TypeScript
|
|
344
|
+
|
|
345
|
+
All types are exported:
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
import type {
|
|
349
|
+
ChatMessage,
|
|
350
|
+
ChatFile,
|
|
351
|
+
ModelInterfaceTool,
|
|
352
|
+
ModelInterfaceToolSchema,
|
|
353
|
+
ToolCall,
|
|
354
|
+
ToolCallResponse,
|
|
355
|
+
RealtimeChatHistory,
|
|
356
|
+
ChatDrawerOptions,
|
|
357
|
+
UseDevicChatOptions,
|
|
358
|
+
} from '@devicai/ui';
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## License
|
|
362
|
+
|
|
363
|
+
MIT
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Devic API client using native fetch
|
|
5
|
+
*/
|
|
6
|
+
class DevicApiClient {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Update client configuration
|
|
12
|
+
*/
|
|
13
|
+
setConfig(config) {
|
|
14
|
+
this.config = { ...this.config, ...config };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Make an authenticated request to the API
|
|
18
|
+
*/
|
|
19
|
+
async request(endpoint, options = {}) {
|
|
20
|
+
const url = `${this.config.baseUrl}${endpoint}`;
|
|
21
|
+
const headers = {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
24
|
+
...options.headers,
|
|
25
|
+
};
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
...options,
|
|
28
|
+
headers,
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
let errorData;
|
|
32
|
+
try {
|
|
33
|
+
errorData = await response.json();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
errorData = {
|
|
37
|
+
statusCode: response.status,
|
|
38
|
+
message: response.statusText,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
throw new DevicApiError(errorData);
|
|
42
|
+
}
|
|
43
|
+
// Handle responses that may have a wrapper structure
|
|
44
|
+
const data = await response.json();
|
|
45
|
+
// If the response has a data property, extract it (common wrapper pattern)
|
|
46
|
+
if (data && typeof data === 'object' && 'data' in data) {
|
|
47
|
+
return data.data;
|
|
48
|
+
}
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get all assistant specializations
|
|
53
|
+
*/
|
|
54
|
+
async getAssistants(external = false) {
|
|
55
|
+
const query = external ? '?external=true' : '';
|
|
56
|
+
return this.request(`/api/v1/assistants${query}`);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get a specific assistant specialization
|
|
60
|
+
*/
|
|
61
|
+
async getAssistant(identifier) {
|
|
62
|
+
return this.request(`/api/v1/assistants/${identifier}`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Send a message to an assistant (sync mode)
|
|
66
|
+
*/
|
|
67
|
+
async sendMessage(assistantId, dto) {
|
|
68
|
+
return this.request(`/api/v1/assistants/${assistantId}/messages`, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
body: JSON.stringify(dto),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Send a message to an assistant (async mode)
|
|
75
|
+
*/
|
|
76
|
+
async sendMessageAsync(assistantId, dto) {
|
|
77
|
+
return this.request(`/api/v1/assistants/${assistantId}/messages?async=true`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: JSON.stringify(dto),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get real-time chat history (for polling in async mode)
|
|
84
|
+
*/
|
|
85
|
+
async getRealtimeHistory(assistantId, chatUid) {
|
|
86
|
+
return this.request(`/api/v1/assistants/${assistantId}/chats/${chatUid}/realtime`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get chat history for a specific conversation
|
|
90
|
+
*/
|
|
91
|
+
async getChatHistory(assistantId, chatUid) {
|
|
92
|
+
return this.request(`/api/v1/assistants/${assistantId}/chats/${chatUid}`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Send tool call responses back to the assistant
|
|
96
|
+
*/
|
|
97
|
+
async sendToolResponses(assistantId, chatUid, responses) {
|
|
98
|
+
return this.request(`/api/v1/assistants/${assistantId}/chats/${chatUid}/tool-response`, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
body: JSON.stringify({ responses }),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Custom error class for API errors
|
|
106
|
+
*/
|
|
107
|
+
class DevicApiError extends Error {
|
|
108
|
+
constructor(error) {
|
|
109
|
+
super(error.message);
|
|
110
|
+
this.name = 'DevicApiError';
|
|
111
|
+
this.statusCode = error.statusCode;
|
|
112
|
+
this.errorType = error.error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
exports.DevicApiClient = DevicApiClient;
|
|
117
|
+
exports.DevicApiError = DevicApiError;
|
|
118
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/api/client.ts"],"sourcesContent":["import type {\n ProcessMessageDto,\n ChatMessage,\n AsyncResponse,\n RealtimeChatHistory,\n ChatHistory,\n AssistantSpecialization,\n ApiError,\n ToolCallResponse,\n} from './types';\n\nexport interface DevicApiClientConfig {\n apiKey: string;\n baseUrl: string;\n}\n\n/**\n * Devic API client using native fetch\n */\nexport class DevicApiClient {\n private config: DevicApiClientConfig;\n\n constructor(config: DevicApiClientConfig) {\n this.config = config;\n }\n\n /**\n * Update client configuration\n */\n setConfig(config: Partial<DevicApiClientConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Make an authenticated request to the API\n */\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n const url = `${this.config.baseUrl}${endpoint}`;\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n ...options.headers,\n };\n\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n let errorData: ApiError;\n try {\n errorData = await response.json();\n } catch {\n errorData = {\n statusCode: response.status,\n message: response.statusText,\n };\n }\n throw new DevicApiError(errorData);\n }\n\n // Handle responses that may have a wrapper structure\n const data = await response.json();\n\n // If the response has a data property, extract it (common wrapper pattern)\n if (data && typeof data === 'object' && 'data' in data) {\n return data.data as T;\n }\n\n return data as T;\n }\n\n /**\n * Get all assistant specializations\n */\n async getAssistants(external = false): Promise<AssistantSpecialization[]> {\n const query = external ? '?external=true' : '';\n return this.request<AssistantSpecialization[]>(`/api/v1/assistants${query}`);\n }\n\n /**\n * Get a specific assistant specialization\n */\n async getAssistant(identifier: string): Promise<AssistantSpecialization> {\n return this.request<AssistantSpecialization>(`/api/v1/assistants/${identifier}`);\n }\n\n /**\n * Send a message to an assistant (sync mode)\n */\n async sendMessage(\n assistantId: string,\n dto: ProcessMessageDto\n ): Promise<ChatMessage[]> {\n return this.request<ChatMessage[]>(\n `/api/v1/assistants/${assistantId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify(dto),\n }\n );\n }\n\n /**\n * Send a message to an assistant (async mode)\n */\n async sendMessageAsync(\n assistantId: string,\n dto: ProcessMessageDto\n ): Promise<AsyncResponse> {\n return this.request<AsyncResponse>(\n `/api/v1/assistants/${assistantId}/messages?async=true`,\n {\n method: 'POST',\n body: JSON.stringify(dto),\n }\n );\n }\n\n /**\n * Get real-time chat history (for polling in async mode)\n */\n async getRealtimeHistory(\n assistantId: string,\n chatUid: string\n ): Promise<RealtimeChatHistory> {\n return this.request<RealtimeChatHistory>(\n `/api/v1/assistants/${assistantId}/chats/${chatUid}/realtime`\n );\n }\n\n /**\n * Get chat history for a specific conversation\n */\n async getChatHistory(\n assistantId: string,\n chatUid: string\n ): Promise<ChatHistory> {\n return this.request<ChatHistory>(\n `/api/v1/assistants/${assistantId}/chats/${chatUid}`\n );\n }\n\n /**\n * Send tool call responses back to the assistant\n */\n async sendToolResponses(\n assistantId: string,\n chatUid: string,\n responses: ToolCallResponse[]\n ): Promise<AsyncResponse> {\n return this.request<AsyncResponse>(\n `/api/v1/assistants/${assistantId}/chats/${chatUid}/tool-response`,\n {\n method: 'POST',\n body: JSON.stringify({ responses }),\n }\n );\n }\n}\n\n/**\n * Custom error class for API errors\n */\nexport class DevicApiError extends Error {\n public statusCode: number;\n public errorType?: string;\n\n constructor(error: ApiError) {\n super(error.message);\n this.name = 'DevicApiError';\n this.statusCode = error.statusCode;\n this.errorType = error.error;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var useDevicChat = require('../../hooks/useDevicChat.js');
|
|
6
|
+
var ChatMessages = require('./ChatMessages.js');
|
|
7
|
+
var ChatInput = require('./ChatInput.js');
|
|
8
|
+
|
|
9
|
+
const DEFAULT_OPTIONS = {
|
|
10
|
+
position: 'right',
|
|
11
|
+
width: 400,
|
|
12
|
+
defaultOpen: false,
|
|
13
|
+
color: '#1890ff',
|
|
14
|
+
welcomeMessage: '',
|
|
15
|
+
suggestedMessages: [],
|
|
16
|
+
enableFileUploads: false,
|
|
17
|
+
allowedFileTypes: { images: true, documents: true },
|
|
18
|
+
maxFileSize: 10 * 1024 * 1024,
|
|
19
|
+
inputPlaceholder: 'Type a message...',
|
|
20
|
+
title: 'Chat',
|
|
21
|
+
showToolTimeline: true,
|
|
22
|
+
zIndex: 1000,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Chat drawer component for Devic assistants
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <ChatDrawer
|
|
30
|
+
* assistantId="my-assistant"
|
|
31
|
+
* options={{
|
|
32
|
+
* position: 'right',
|
|
33
|
+
* width: 400,
|
|
34
|
+
* welcomeMessage: 'Hello! How can I help you?',
|
|
35
|
+
* suggestedMessages: ['Help me with...', 'Tell me about...'],
|
|
36
|
+
* }}
|
|
37
|
+
* modelInterfaceTools={[
|
|
38
|
+
* {
|
|
39
|
+
* toolName: 'get_user_location',
|
|
40
|
+
* schema: { ... },
|
|
41
|
+
* callback: async () => ({ lat: 40.7, lng: -74.0 })
|
|
42
|
+
* }
|
|
43
|
+
* ]}
|
|
44
|
+
* onMessageReceived={(msg) => console.log('Received:', msg)}
|
|
45
|
+
* />
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
function ChatDrawer({ assistantId, chatUid: initialChatUid, options = {}, enabledTools, modelInterfaceTools, tenantId, tenantMetadata, apiKey, baseUrl, onMessageSent, onMessageReceived, onToolCall, onError, onChatCreated, onOpen, onClose, isOpen: controlledIsOpen, className, }) {
|
|
49
|
+
// Merge options with defaults
|
|
50
|
+
const mergedOptions = react.useMemo(() => ({ ...DEFAULT_OPTIONS, ...options }), [options]);
|
|
51
|
+
// Drawer open state (can be controlled or uncontrolled)
|
|
52
|
+
const [internalIsOpen, setInternalIsOpen] = react.useState(mergedOptions.defaultOpen);
|
|
53
|
+
const isOpen = controlledIsOpen ?? internalIsOpen;
|
|
54
|
+
// Use chat hook
|
|
55
|
+
const chat = useDevicChat.useDevicChat({
|
|
56
|
+
assistantId,
|
|
57
|
+
chatUid: initialChatUid,
|
|
58
|
+
apiKey,
|
|
59
|
+
baseUrl,
|
|
60
|
+
tenantId,
|
|
61
|
+
tenantMetadata,
|
|
62
|
+
enabledTools,
|
|
63
|
+
modelInterfaceTools,
|
|
64
|
+
onMessageSent,
|
|
65
|
+
onMessageReceived,
|
|
66
|
+
onToolCall,
|
|
67
|
+
onError,
|
|
68
|
+
onChatCreated,
|
|
69
|
+
});
|
|
70
|
+
// Handle open/close
|
|
71
|
+
const handleOpen = react.useCallback(() => {
|
|
72
|
+
setInternalIsOpen(true);
|
|
73
|
+
onOpen?.();
|
|
74
|
+
}, [onOpen]);
|
|
75
|
+
const handleClose = react.useCallback(() => {
|
|
76
|
+
setInternalIsOpen(false);
|
|
77
|
+
onClose?.();
|
|
78
|
+
}, [onClose]);
|
|
79
|
+
// Handle send message
|
|
80
|
+
const handleSend = react.useCallback((message, files) => {
|
|
81
|
+
chat.sendMessage(message, { files });
|
|
82
|
+
}, [chat]);
|
|
83
|
+
// Handle suggested message click
|
|
84
|
+
const handleSuggestedClick = react.useCallback((message) => {
|
|
85
|
+
chat.sendMessage(message);
|
|
86
|
+
}, [chat]);
|
|
87
|
+
// Apply CSS variable for primary color
|
|
88
|
+
react.useEffect(() => {
|
|
89
|
+
if (mergedOptions.color !== DEFAULT_OPTIONS.color) {
|
|
90
|
+
document.documentElement.style.setProperty('--devic-primary', mergedOptions.color);
|
|
91
|
+
}
|
|
92
|
+
}, [mergedOptions.color]);
|
|
93
|
+
// Build style object
|
|
94
|
+
const drawerStyle = react.useMemo(() => ({
|
|
95
|
+
width: mergedOptions.width,
|
|
96
|
+
zIndex: mergedOptions.zIndex,
|
|
97
|
+
}), [mergedOptions.width, mergedOptions.zIndex]);
|
|
98
|
+
const overlayStyle = react.useMemo(() => ({
|
|
99
|
+
zIndex: mergedOptions.zIndex - 1,
|
|
100
|
+
}), [mergedOptions.zIndex]);
|
|
101
|
+
const triggerStyle = react.useMemo(() => ({
|
|
102
|
+
zIndex: mergedOptions.zIndex - 1,
|
|
103
|
+
[mergedOptions.position]: 20,
|
|
104
|
+
bottom: 20,
|
|
105
|
+
}), [mergedOptions.zIndex, mergedOptions.position]);
|
|
106
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "devic-drawer-overlay", "data-open": isOpen, style: overlayStyle, onClick: handleClose }), jsxRuntime.jsxs("div", { className: `devic-chat-drawer ${className || ''}`, "data-position": mergedOptions.position, "data-open": isOpen, style: drawerStyle, children: [jsxRuntime.jsxs("div", { className: "devic-drawer-header", children: [jsxRuntime.jsx("h2", { className: "devic-drawer-title", children: mergedOptions.title }), jsxRuntime.jsx("button", { className: "devic-drawer-close", onClick: handleClose, type: "button", "aria-label": "Close chat", children: jsxRuntime.jsx(CloseIcon, {}) })] }), chat.error && (jsxRuntime.jsx("div", { className: "devic-error", children: chat.error.message })), jsxRuntime.jsx(ChatMessages.ChatMessages, { messages: chat.messages, isLoading: chat.isLoading, welcomeMessage: mergedOptions.welcomeMessage, suggestedMessages: mergedOptions.suggestedMessages, onSuggestedClick: handleSuggestedClick, showToolTimeline: mergedOptions.showToolTimeline }), jsxRuntime.jsx(ChatInput.ChatInput, { onSend: handleSend, disabled: chat.isLoading, placeholder: mergedOptions.inputPlaceholder, enableFileUploads: mergedOptions.enableFileUploads, allowedFileTypes: mergedOptions.allowedFileTypes, maxFileSize: mergedOptions.maxFileSize })] }), !isOpen && (jsxRuntime.jsx("button", { className: "devic-trigger", onClick: handleOpen, style: triggerStyle, type: "button", "aria-label": "Open chat", children: jsxRuntime.jsx(ChatIcon, {}) }))] }));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Close icon
|
|
110
|
+
*/
|
|
111
|
+
function CloseIcon() {
|
|
112
|
+
return (jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Chat icon for trigger button
|
|
116
|
+
*/
|
|
117
|
+
function ChatIcon() {
|
|
118
|
+
return (jsxRuntime.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntime.jsx("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" }) }));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
exports.ChatDrawer = ChatDrawer;
|
|
122
|
+
//# sourceMappingURL=ChatDrawer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatDrawer.js","sources":["../../../../../src/components/ChatDrawer/ChatDrawer.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useDevicChat } from '../../hooks/useDevicChat';\nimport { ChatMessages } from './ChatMessages';\nimport { ChatInput } from './ChatInput';\nimport type { ChatDrawerProps, ChatDrawerOptions } from './ChatDrawer.types';\nimport './styles.css';\n\nconst DEFAULT_OPTIONS: Required<ChatDrawerOptions> = {\n position: 'right',\n width: 400,\n defaultOpen: false,\n color: '#1890ff',\n welcomeMessage: '',\n suggestedMessages: [],\n enableFileUploads: false,\n allowedFileTypes: { images: true, documents: true },\n maxFileSize: 10 * 1024 * 1024,\n inputPlaceholder: 'Type a message...',\n title: 'Chat',\n showToolTimeline: true,\n zIndex: 1000,\n};\n\n/**\n * Chat drawer component for Devic assistants\n *\n * @example\n * ```tsx\n * <ChatDrawer\n * assistantId=\"my-assistant\"\n * options={{\n * position: 'right',\n * width: 400,\n * welcomeMessage: 'Hello! How can I help you?',\n * suggestedMessages: ['Help me with...', 'Tell me about...'],\n * }}\n * modelInterfaceTools={[\n * {\n * toolName: 'get_user_location',\n * schema: { ... },\n * callback: async () => ({ lat: 40.7, lng: -74.0 })\n * }\n * ]}\n * onMessageReceived={(msg) => console.log('Received:', msg)}\n * />\n * ```\n */\nexport function ChatDrawer({\n assistantId,\n chatUid: initialChatUid,\n options = {},\n enabledTools,\n modelInterfaceTools,\n tenantId,\n tenantMetadata,\n apiKey,\n baseUrl,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n onOpen,\n onClose,\n isOpen: controlledIsOpen,\n className,\n}: ChatDrawerProps): JSX.Element {\n // Merge options with defaults\n const mergedOptions = useMemo(\n () => ({ ...DEFAULT_OPTIONS, ...options }),\n [options]\n );\n\n // Drawer open state (can be controlled or uncontrolled)\n const [internalIsOpen, setInternalIsOpen] = useState(mergedOptions.defaultOpen);\n const isOpen = controlledIsOpen ?? internalIsOpen;\n\n // Use chat hook\n const chat = useDevicChat({\n assistantId,\n chatUid: initialChatUid,\n apiKey,\n baseUrl,\n tenantId,\n tenantMetadata,\n enabledTools,\n modelInterfaceTools,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n });\n\n // Handle open/close\n const handleOpen = useCallback(() => {\n setInternalIsOpen(true);\n onOpen?.();\n }, [onOpen]);\n\n const handleClose = useCallback(() => {\n setInternalIsOpen(false);\n onClose?.();\n }, [onClose]);\n\n // Handle send message\n const handleSend = useCallback(\n (message: string, files?: any[]) => {\n chat.sendMessage(message, { files });\n },\n [chat]\n );\n\n // Handle suggested message click\n const handleSuggestedClick = useCallback(\n (message: string) => {\n chat.sendMessage(message);\n },\n [chat]\n );\n\n // Apply CSS variable for primary color\n useEffect(() => {\n if (mergedOptions.color !== DEFAULT_OPTIONS.color) {\n document.documentElement.style.setProperty(\n '--devic-primary',\n mergedOptions.color\n );\n }\n }, [mergedOptions.color]);\n\n // Build style object\n const drawerStyle = useMemo(\n () => ({\n width: mergedOptions.width,\n zIndex: mergedOptions.zIndex,\n }),\n [mergedOptions.width, mergedOptions.zIndex]\n );\n\n const overlayStyle = useMemo(\n () => ({\n zIndex: mergedOptions.zIndex - 1,\n }),\n [mergedOptions.zIndex]\n );\n\n const triggerStyle = useMemo(\n () => ({\n zIndex: mergedOptions.zIndex - 1,\n [mergedOptions.position]: 20,\n bottom: 20,\n }),\n [mergedOptions.zIndex, mergedOptions.position]\n );\n\n return (\n <>\n {/* Overlay */}\n <div\n className=\"devic-drawer-overlay\"\n data-open={isOpen}\n style={overlayStyle}\n onClick={handleClose}\n />\n\n {/* Drawer */}\n <div\n className={`devic-chat-drawer ${className || ''}`}\n data-position={mergedOptions.position}\n data-open={isOpen}\n style={drawerStyle}\n >\n {/* Header */}\n <div className=\"devic-drawer-header\">\n <h2 className=\"devic-drawer-title\">{mergedOptions.title}</h2>\n <button\n className=\"devic-drawer-close\"\n onClick={handleClose}\n type=\"button\"\n aria-label=\"Close chat\"\n >\n <CloseIcon />\n </button>\n </div>\n\n {/* Error display */}\n {chat.error && (\n <div className=\"devic-error\">\n {chat.error.message}\n </div>\n )}\n\n {/* Messages */}\n <ChatMessages\n messages={chat.messages}\n isLoading={chat.isLoading}\n welcomeMessage={mergedOptions.welcomeMessage}\n suggestedMessages={mergedOptions.suggestedMessages}\n onSuggestedClick={handleSuggestedClick}\n showToolTimeline={mergedOptions.showToolTimeline}\n />\n\n {/* Input */}\n <ChatInput\n onSend={handleSend}\n disabled={chat.isLoading}\n placeholder={mergedOptions.inputPlaceholder}\n enableFileUploads={mergedOptions.enableFileUploads}\n allowedFileTypes={mergedOptions.allowedFileTypes}\n maxFileSize={mergedOptions.maxFileSize}\n />\n </div>\n\n {/* Trigger button (when drawer is closed) */}\n {!isOpen && (\n <button\n className=\"devic-trigger\"\n onClick={handleOpen}\n style={triggerStyle}\n type=\"button\"\n aria-label=\"Open chat\"\n >\n <ChatIcon />\n </button>\n )}\n </>\n );\n}\n\n/**\n * Close icon\n */\nfunction CloseIcon(): JSX.Element {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\n/**\n * Chat icon for trigger button\n */\nfunction ChatIcon(): JSX.Element {\n return (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z\" />\n </svg>\n );\n}\n"],"names":["useMemo","useState","useDevicChat","useCallback","useEffect","_jsxs","_jsx","ChatMessages","ChatInput"],"mappings":";;;;;;;;AAOA,MAAM,eAAe,GAAgC;AACnD,IAAA,QAAQ,EAAE,OAAO;AACjB,IAAA,KAAK,EAAE,GAAG;AACV,IAAA,WAAW,EAAE,KAAK;AAClB,IAAA,KAAK,EAAE,SAAS;AAChB,IAAA,cAAc,EAAE,EAAE;AAClB,IAAA,iBAAiB,EAAE,EAAE;AACrB,IAAA,iBAAiB,EAAE,KAAK;IACxB,gBAAgB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;AACnD,IAAA,WAAW,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;AAC7B,IAAA,gBAAgB,EAAE,mBAAmB;AACrC,IAAA,KAAK,EAAE,MAAM;AACb,IAAA,gBAAgB,EAAE,IAAI;AACtB,IAAA,MAAM,EAAE,IAAI;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;AAuBG;AACG,SAAU,UAAU,CAAC,EACzB,WAAW,EACX,OAAO,EAAE,cAAc,EACvB,OAAO,GAAG,EAAE,EACZ,YAAY,EACZ,mBAAmB,EACnB,QAAQ,EACR,cAAc,EACd,MAAM,EACN,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,OAAO,EACP,aAAa,EACb,MAAM,EACN,OAAO,EACP,MAAM,EAAE,gBAAgB,EACxB,SAAS,GACO,EAAA;;IAEhB,MAAM,aAAa,GAAGA,aAAO,CAC3B,OAAO,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,EAAE,CAAC,EAC1C,CAAC,OAAO,CAAC,CACV;;AAGD,IAAA,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAGC,cAAQ,CAAC,aAAa,CAAC,WAAW,CAAC;AAC/E,IAAA,MAAM,MAAM,GAAG,gBAAgB,IAAI,cAAc;;IAGjD,MAAM,IAAI,GAAGC,yBAAY,CAAC;QACxB,WAAW;AACX,QAAA,OAAO,EAAE,cAAc;QACvB,MAAM;QACN,OAAO;QACP,QAAQ;QACR,cAAc;QACd,YAAY;QACZ,mBAAmB;QACnB,aAAa;QACb,iBAAiB;QACjB,UAAU;QACV,OAAO;QACP,aAAa;AACd,KAAA,CAAC;;AAGF,IAAA,MAAM,UAAU,GAAGC,iBAAW,CAAC,MAAK;QAClC,iBAAiB,CAAC,IAAI,CAAC;QACvB,MAAM,IAAI;AACZ,IAAA,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;AAEZ,IAAA,MAAM,WAAW,GAAGA,iBAAW,CAAC,MAAK;QACnC,iBAAiB,CAAC,KAAK,CAAC;QACxB,OAAO,IAAI;AACb,IAAA,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;;IAGb,MAAM,UAAU,GAAGA,iBAAW,CAC5B,CAAC,OAAe,EAAE,KAAa,KAAI;QACjC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC;AACtC,IAAA,CAAC,EACD,CAAC,IAAI,CAAC,CACP;;AAGD,IAAA,MAAM,oBAAoB,GAAGA,iBAAW,CACtC,CAAC,OAAe,KAAI;AAClB,QAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;AAC3B,IAAA,CAAC,EACD,CAAC,IAAI,CAAC,CACP;;IAGDC,eAAS,CAAC,MAAK;QACb,IAAI,aAAa,CAAC,KAAK,KAAK,eAAe,CAAC,KAAK,EAAE;AACjD,YAAA,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CACxC,iBAAiB,EACjB,aAAa,CAAC,KAAK,CACpB;QACH;AACF,IAAA,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;;AAGzB,IAAA,MAAM,WAAW,GAAGJ,aAAO,CACzB,OAAO;QACL,KAAK,EAAE,aAAa,CAAC,KAAK;QAC1B,MAAM,EAAE,aAAa,CAAC,MAAM;KAC7B,CAAC,EACF,CAAC,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAC5C;AAED,IAAA,MAAM,YAAY,GAAGA,aAAO,CAC1B,OAAO;AACL,QAAA,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,CAAC;AACjC,KAAA,CAAC,EACF,CAAC,aAAa,CAAC,MAAM,CAAC,CACvB;AAED,IAAA,MAAM,YAAY,GAAGA,aAAO,CAC1B,OAAO;AACL,QAAA,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,CAAC;AAChC,QAAA,CAAC,aAAa,CAAC,QAAQ,GAAG,EAAE;AAC5B,QAAA,MAAM,EAAE,EAAE;KACX,CAAC,EACF,CAAC,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAC/C;IAED,QACEK,kDAEEC,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,sBAAsB,EAAA,WAAA,EACrB,MAAM,EACjB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,WAAW,GACpB,EAGFD,eAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,CAAA,kBAAA,EAAqB,SAAS,IAAI,EAAE,CAAA,CAAE,mBAClC,aAAa,CAAC,QAAQ,EAAA,WAAA,EAC1B,MAAM,EACjB,KAAK,EAAE,WAAW,EAAA,QAAA,EAAA,CAGlBA,eAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,qBAAqB,aAClCC,cAAA,CAAA,IAAA,EAAA,EAAI,SAAS,EAAC,oBAAoB,EAAA,QAAA,EAAE,aAAa,CAAC,KAAK,EAAA,CAAM,EAC7DA,cAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAC,oBAAoB,EAC9B,OAAO,EAAE,WAAW,EACpB,IAAI,EAAC,QAAQ,EAAA,YAAA,EACF,YAAY,YAEvBA,cAAA,CAAC,SAAS,EAAA,EAAA,CAAG,EAAA,CACN,CAAA,EAAA,CACL,EAGL,IAAI,CAAC,KAAK,KACTA,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,aAAa,EAAA,QAAA,EACzB,IAAI,CAAC,KAAK,CAAC,OAAO,EAAA,CACf,CACP,EAGDA,cAAA,CAACC,yBAAY,EAAA,EACX,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,SAAS,EAAE,IAAI,CAAC,SAAS,EACzB,cAAc,EAAE,aAAa,CAAC,cAAc,EAC5C,iBAAiB,EAAE,aAAa,CAAC,iBAAiB,EAClD,gBAAgB,EAAE,oBAAoB,EACtC,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,GAChD,EAGFD,cAAA,CAACE,mBAAS,EAAA,EACR,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,IAAI,CAAC,SAAS,EACxB,WAAW,EAAE,aAAa,CAAC,gBAAgB,EAC3C,iBAAiB,EAAE,aAAa,CAAC,iBAAiB,EAClD,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,EAChD,WAAW,EAAE,aAAa,CAAC,WAAW,EAAA,CACtC,CAAA,EAAA,CACE,EAGL,CAAC,MAAM,KACNF,cAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAC,eAAe,EACzB,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAC,QAAQ,EAAA,YAAA,EACF,WAAW,YAEtBA,cAAA,CAAC,QAAQ,KAAG,EAAA,CACL,CACV,CAAA,EAAA,CACA;AAEP;AAEA;;AAEG;AACH,SAAS,SAAS,GAAA;AAChB,IAAA,QACED,eAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EAAA,QAAA,EAAA,CAEtBC,cAAA,CAAA,MAAA,EAAA,EAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAA,CAAG,EACtCA,cAAA,CAAA,MAAA,EAAA,EAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAA,CAAG,CAAA,EAAA,CAClC;AAEV;AAEA;;AAEG;AACH,SAAS,QAAQ,GAAA;IACf,QACEA,cAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,EAAA,QAAA,EAEnBA,cAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,yFAAyF,EAAA,CAAG,EAAA,CAChG;AAEV;;;;"}
|