@doderasoftware/restify-ai 0.1.0-beta.3 → 0.1.0-beta.5

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 CHANGED
@@ -1,514 +1,631 @@
1
- # @doderasoftware/restify-ai
1
+ <div align="center">
2
+ <h1>🤖 Restify AI</h1>
3
+ <p><strong>Professional AI Chatbot Component for Vue 3 + Laravel Restify</strong></p>
4
+ <p>Build intelligent, context-aware AI assistants that integrate seamlessly with your Laravel backend</p>
5
+
6
+ <a href="https://www.npmjs.com/package/@doderasoftware/restify-ai"><img src="https://img.shields.io/npm/v/@doderasoftware/restify-ai.svg?style=flat-square" alt="npm version"></a>
7
+ <a href="https://www.npmjs.com/package/@doderasoftware/restify-ai"><img src="https://img.shields.io/npm/dm/@doderasoftware/restify-ai.svg?style=flat-square" alt="npm downloads"></a>
8
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square" alt="License: MIT"></a>
9
+ <a href="https://vuejs.org/"><img src="https://img.shields.io/badge/Vue-3.x-brightgreen.svg?style=flat-square" alt="Vue 3"></a>
10
+ <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.x-blue.svg?style=flat-square" alt="TypeScript"></a>
11
+ <a href="https://tailwindcss.com/"><img src="https://img.shields.io/badge/TailwindCSS-3.x-38bdf8.svg?style=flat-square" alt="TailwindCSS"></a>
12
+
13
+ <br /><br />
14
+
15
+ <a href="https://laravel-restify.com">Laravel Restify</a> •
16
+ <a href="https://binarcode.com">BinarCode</a> •
17
+ <a href="#features">Features</a> •
18
+ <a href="#installation">Installation</a> •
19
+ <a href="#quick-start">Quick Start</a> •
20
+ <a href="#configuration">Configuration</a>
21
+ </div>
2
22
 
3
- > 🤖 A fully customizable Vue 3 AI chatbot component library with Laravel Restify integration
23
+ ---
4
24
 
5
- [![npm version](https://img.shields.io/npm/v/@doderasoftware/restify-ai)](https://www.npmjs.com/package/@doderasoftware/restify-ai)
6
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
7
- [![Vue 3](https://img.shields.io/badge/Vue-3.x-brightgreen)](https://vuejs.org/)
25
+ ## Overview
26
+
27
+ **Restify AI** is a production-ready Vue 3 component library that provides a fully-featured AI chatbot interface designed to work seamlessly with [Laravel Restify](https://laravel-restify.com) backends. Built by [BinarCode](https://binarcode.com), the team behind Laravel Restify, this package enables you to add powerful AI capabilities to your Vue.js applications in minutes.
28
+
29
+ Whether you are building a customer support system, an intelligent assistant for your SaaS application, or an AI-powered admin panel, Restify AI provides all the building blocks you need.
30
+
31
+ ## Features
32
+
33
+ ### 🎯 Core Features
34
+ - **Real-time SSE Streaming** - Smooth, character-by-character response streaming
35
+ - **File Attachments** - Upload and process documents, images, and more
36
+ - **@Mentions System** - Reference entities from your application (users, documents, etc.)
37
+ - **Context-Aware Suggestions** - Smart prompts based on current page/route
38
+ - **Chat History** - Persistent conversation memory with configurable limits
39
+ - **Markdown Rendering** - Beautiful formatting with syntax highlighting
40
+ - **Quota Management** - Track and display API usage limits
41
+
42
+ ### 🎨 UI/UX
43
+ - **Fully Customizable** - Override any style with Tailwind CSS classes
44
+ - **Dark Mode Support** - Automatic dark/light theme detection
45
+ - **Responsive Design** - Works on desktop, tablet, and mobile
46
+ - **Keyboard Shortcuts** - Quick access with configurable shortcuts (Cmd/Ctrl+G)
47
+ - **Fullscreen Mode** - Expandable chat interface
48
+ - **Animations** - Smooth transitions and loading states
49
+ - **Customizable Avatars** - Use custom components or images
50
+
51
+ ### 🔧 Developer Experience
52
+ - **TypeScript First** - Full type definitions included
53
+ - **Vue 3 Composition API** - Modern Vue patterns
54
+ - **Pinia Integration** - State management built-in
55
+ - **Slot-Based Customization** - Override any component section
56
+ - **Lifecycle Hooks** - Tap into every stage of the chat flow
57
+ - **i18n Ready** - Full internationalization support
58
+
59
+ ### 🔒 Enterprise Ready
60
+ - **Support Mode** - Route conversations to human agents
61
+ - **Permission System** - Control features based on user permissions
62
+ - **Request/Response Interceptors** - Customize API communication
63
+ - **Retry Logic** - Automatic retry with configurable backoff
64
+ - **Error Handling** - User-friendly error messages
65
+
66
+ ## Installation
67
+
68
+ ```bash
69
+ # npm
70
+ npm install @doderasoftware/restify-ai
8
71
 
9
- ---
72
+ # yarn
73
+ yarn add @doderasoftware/restify-ai
10
74
 
11
- ## ✨ Features
12
-
13
- | Feature | Description |
14
- |---------|-------------|
15
- | 🔌 **Laravel Restify** | Built for seamless Laravel Restify AI endpoints |
16
- | 📡 **SSE Streaming** | Real-time streaming responses with Server-Sent Events |
17
- | 📎 **File Attachments** | Built-in file upload with progress & preview |
18
- | 💬 **@Mentions** | Pluggable mention providers (employees, projects, etc.) |
19
- | 💡 **Smart Suggestions** | Route-aware suggestion system |
20
- | 🎨 **Fully Customizable** | UI classes, texts, slots, themes |
21
- | ⌨️ **Keyboard Shortcuts** | Configurable shortcuts (e.g., \`Cmd+G\`) |
22
- | 🔄 **Auto Retry** | Configurable retry logic with exponential backoff |
23
- | 💾 **Session Persistence** | Chat history survives page refresh |
24
- | 🌙 **Dark Mode** | Full dark mode support out of the box |
25
- | 📦 **Tree-shakable** | Import only what you need |
26
- | 🎯 **TypeScript** | Full TypeScript support |
75
+ # pnpm
76
+ pnpm add @doderasoftware/restify-ai
77
+ ```
27
78
 
28
- ---
79
+ ### Peer Dependencies
29
80
 
30
- ## 📦 Installation
81
+ ```bash
82
+ npm install vue@^3.3.0 pinia@^2.1.0 tailwindcss@^3.3.0
83
+ ```
31
84
 
32
- \`\`\`bash
33
- npm install @doderasoftware/restify-ai
34
- # or
35
- pnpm add @doderasoftware/restify-ai
36
- \`\`\`
85
+ ## Quick Start
37
86
 
38
- **Peer Dependencies:** \`vue ^3.4\`, \`pinia ^2.1\`
87
+ ### 1. Configure Tailwind CSS
39
88
 
40
- ---
89
+ ```javascript
90
+ // tailwind.config.js
91
+ export default {
92
+ content: [
93
+ "./src/**/*.{vue,js,ts,jsx,tsx}",
94
+ "./node_modules/@doderasoftware/restify-ai/dist/**/*.{js,vue}",
95
+ ],
96
+ presets: [
97
+ require("@doderasoftware/restify-ai/tailwind"),
98
+ ],
99
+ }
100
+ ```
41
101
 
42
- ## 🚀 Quick Start
102
+ ### 2. Import Styles
103
+
104
+ ```typescript
105
+ // main.ts
106
+ import "@doderasoftware/restify-ai/styles"
107
+ ```
108
+
109
+ ### 3. Register the Plugin
110
+
111
+ ```typescript
112
+ // plugins/restifyAi.ts
113
+ import { RestifyAiPlugin } from "@doderasoftware/restify-ai"
114
+ import type { App } from "vue"
115
+
116
+ export function setupRestifyAi(app: App) {
117
+ app.use(RestifyAiPlugin, {
118
+ endpoints: {
119
+ ask: "/api/ai/ask",
120
+ uploadFile: "/api/ai/upload",
121
+ quota: "/api/ai/quota",
122
+ },
123
+ getAuthToken: () => localStorage.getItem("token"),
124
+ baseUrl: import.meta.env.VITE_API_URL,
125
+ })
126
+ }
127
+ ```
43
128
 
44
- \`\`\`typescript
129
+ ```typescript
45
130
  // main.ts
46
- import { createApp } from 'vue'
47
- import { createPinia } from 'pinia'
48
- import { RestifyAiPlugin } from '@doderasoftware/restify-ai'
49
- import '@doderasoftware/restify-ai/styles'
131
+ import { createApp } from "vue"
132
+ import { createPinia } from "pinia"
133
+ import App from "./App.vue"
134
+ import { setupRestifyAi } from "./plugins/restifyAi"
135
+ import "@doderasoftware/restify-ai/styles"
50
136
 
51
137
  const app = createApp(App)
52
138
  app.use(createPinia())
139
+ setupRestifyAi(app)
140
+ app.mount("#app")
141
+ ```
53
142
 
54
- app.use(RestifyAiPlugin, {
55
- endpoints: {
56
- ask: '/api/ai/ask', // Required - SSE streaming endpoint
57
- quota: '/api/ai/quota', // Optional - quota endpoint
58
- uploadFile: '/api/ai/upload', // Optional - file upload
59
- },
60
- getAuthToken: () => localStorage.getItem('token'),
61
- })
62
-
63
- app.mount('#app')
64
- \`\`\`
143
+ ### 4. Add the Component
65
144
 
66
- \`\`\`vue
67
- <!-- App.vue -->
145
+ ```vue
68
146
  <template>
69
- <AiChatDrawer v-model="showChat" />
70
- <button @click="showChat = true">Open AI Chat</button>
147
+ <div>
148
+ <button @click="showChat = true" class="fixed bottom-4 right-4 p-3 bg-blue-600 rounded-full">
149
+ <SparklesIcon class="w-6 h-6 text-white" />
150
+ </button>
151
+ <AiChatDrawer v-model="showChat" />
152
+ </div>
71
153
  </template>
72
154
 
73
- <script setup>
74
- import { ref } from 'vue'
75
- import { AiChatDrawer } from '@doderasoftware/restify-ai'
155
+ <script setup lang="ts">
156
+ import { ref } from "vue"
157
+ import { AiChatDrawer } from "@doderasoftware/restify-ai"
76
158
 
77
159
  const showChat = ref(false)
78
160
  </script>
79
- \`\`\`
161
+ ```
80
162
 
81
- ---
163
+ ### 5. Enable Keyboard Shortcut
82
164
 
83
- ## ⚙️ Plugin Configuration
165
+ ```typescript
166
+ import { useAiDrawerShortcut } from "@doderasoftware/restify-ai"
84
167
 
85
- ### Required Options
168
+ // Enable Cmd/Ctrl+G to toggle drawer
169
+ useAiDrawerShortcut()
170
+ ```
86
171
 
87
- | Option | Type | Description |
88
- |--------|------|-------------|
89
- | \`endpoints.ask\` | \`string\` | SSE streaming endpoint |
90
- | \`getAuthToken\` | \`() => string \| Promise<string>\` | Auth token getter |
172
+ ## Configuration
91
173
 
92
- ### API Configuration
174
+ ### Full Configuration Options
93
175
 
94
- \`\`\`typescript
95
- {
96
- baseUrl: 'https://api.example.com',
176
+ ```typescript
177
+ app.use(RestifyAiPlugin, {
178
+ // REQUIRED
97
179
  endpoints: {
98
- ask: '/api/ai/ask',
99
- quota: '/api/ai/quota',
100
- uploadFile: '/api/ai/upload',
180
+ ask: "/api/ai/ask",
181
+ uploadFile: "/api/ai/upload",
182
+ quota: "/api/ai/quota",
101
183
  },
102
- getAuthToken: () => authStore.token,
103
- getCustomHeaders: () => ({
104
- 'X-CSRF-TOKEN': csrfToken,
105
- 'X-App-Version': '1.0.0',
106
- }),
107
- }
108
- \`\`\`
184
+ getAuthToken: () => localStorage.getItem("auth_token"),
109
185
 
110
- ### Request Customization
186
+ // API CONFIGURATION
187
+ baseUrl: "https://api.example.com",
188
+ getCustomHeaders: () => ({ "X-Tenant-ID": getTenantId() }),
189
+ buildRequest: (payload) => ({ ...payload, custom: "value" }),
190
+ parseStreamContent: (data) => JSON.parse(data).choices?.[0]?.delta?.content,
111
191
 
112
- \`\`\`typescript
113
- {
114
- // Modify request payload before sending
115
- buildRequest: (payload) => ({
116
- ...payload,
117
- context: { page: 'dashboard' },
118
- }),
119
-
120
- // Custom stream parser (default: OpenAI format)
121
- parseStreamContent: (data) => JSON.parse(data).content,
122
-
123
- // Request/response interceptors
124
- requestInterceptor: (url, options) => options,
125
- responseInterceptor: (response) => response,
126
- }
127
- \`\`\`
192
+ // RETRY
193
+ retry: { maxRetries: 3, retryDelay: 1000 },
128
194
 
129
- ### Retry Configuration
195
+ // INTERNATIONALIZATION
196
+ translate: (key, params) => i18n.t("ai." + key, params),
197
+ labels: {
198
+ title: "AI Assistant",
199
+ placeholder: "Ask me anything...",
200
+ loadingText: "Thinking...",
201
+ },
130
202
 
131
- \`\`\`typescript
132
- {
133
- retry: {
134
- maxRetries: 3,
135
- retryDelay: 1000, // ms, multiplied by attempt
136
- shouldRetry: (error, attempt) => error.status >= 500,
203
+ // PROVIDERS
204
+ mentionProviders: [{
205
+ type: "user",
206
+ label: "Users",
207
+ search: async (q) => api.searchUsers(q),
208
+ }],
209
+ suggestionProviders: [{
210
+ id: "invoices",
211
+ routes: ["/invoices/*"],
212
+ getSuggestions: (ctx) => [{ id: "1", title: "Create Invoice", prompt: "Help me create an invoice" }],
213
+ }],
214
+
215
+ // THEMING
216
+ theme: {
217
+ primaryColor: "#3b82f6",
218
+ userBubbleColor: "#3b82f6",
219
+ drawerWidth: "500px",
137
220
  },
138
- }
139
- \`\`\`
140
221
 
141
- ### Providers
222
+ // LIMITS
223
+ chatHistoryLimit: 50,
224
+ maxAttachments: 5,
225
+ maxFileSize: 10 * 1024 * 1024,
142
226
 
143
- \`\`\`typescript
144
- {
145
- // @Mention providers
146
- mentionProviders: [
147
- {
148
- type: 'employee',
149
- label: 'Employees',
150
- priority: 10,
151
- search: async (query) => api.searchEmployees(query),
152
- },
153
- ],
154
-
155
- // Route-aware suggestions
156
- suggestionProviders: [
157
- {
158
- id: 'dashboard',
159
- routes: ['/dashboard', '/analytics'],
160
- getSuggestions: (context) => [...],
161
- },
162
- ],
163
-
164
- // Default suggestions for empty state
165
- defaultSuggestions: [
166
- { id: '1', title: 'How can you help?', prompt: 'What can you do?' },
167
- ],
168
- }
169
- \`\`\`
227
+ // FEATURES
228
+ keyboardShortcut: "cmd+g",
229
+ enableSupportMode: true,
170
230
 
171
- ### Limits & Storage
231
+ // CUSTOM COMPONENTS
232
+ assistantAvatar: CustomAvatarComponent,
233
+ userAvatar: () => currentUser.value?.avatarUrl,
172
234
 
173
- \`\`\`typescript
174
- {
175
- chatHistoryLimit: 15, // Max messages
176
- maxAttachments: 5, // Max files per message
177
- maxFileSize: 10 * 1024 * 1024, // 10MB
178
- acceptedFileTypes: 'image/*,.pdf,.txt',
179
- chatHistoryKey: 'my_chat_history', // sessionStorage key
180
- drawerStateKey: 'my_drawer_state', // localStorage key
181
- }
182
- \`\`\`
235
+ // CALLBACKS
236
+ onMessageSent: (msg) => analytics.track("ai_sent"),
237
+ onError: (err) => Sentry.captureException(err),
238
+ onStreamStart: () => console.log("Stream started"),
239
+ beforeSend: (payload) => ({ ...payload, timestamp: Date.now() }),
240
+ })
241
+ ```
183
242
 
184
- ### Features
243
+ ## Component API
185
244
 
186
- \`\`\`typescript
187
- {
188
- keyboardShortcut: 'cmd+g', // null to disable
189
- enableSupportMode: true, // Support request toggle
190
- }
191
- \`\`\`
245
+ ### AiChatDrawer Props
192
246
 
193
- ### Custom Components
247
+ | Prop | Type | Default | Description |
248
+ |------|------|---------|-------------|
249
+ | `modelValue` | `boolean` | required | Controls drawer visibility (v-model) |
250
+ | `width` | `string` | `"600px"` | Drawer width |
251
+ | `fullscreenWidth` | `string` | `"90vw"` | Width when fullscreen |
252
+ | `topOffset` | `string` | `"0"` | Top offset (for fixed headers) |
253
+ | `position` | `"left" \| "right"` | `"right"` | Drawer position |
254
+ | `showBackdrop` | `boolean` | `false` | Show backdrop overlay |
255
+ | `closeOnEscape` | `boolean` | `true` | Close on Escape key |
256
+ | `showQuota` | `boolean` | `true` | Show quota display |
257
+ | `confirmClose` | `boolean` | `true` | Confirm before clearing |
258
+ | `ui` | `AiChatDrawerUI` | `{}` | Custom CSS classes |
259
+ | `texts` | `AiChatDrawerTexts` | `{}` | Custom text labels |
260
+
261
+ ### Events
194
262
 
195
- \`\`\`typescript
196
- {
197
- assistantAvatar: MyAvatarComponent,
198
- userAvatar: UserAvatarComponent,
263
+ | Event | Payload | Description |
264
+ |-------|---------|-------------|
265
+ | `update:modelValue` | `boolean` | Drawer state changed |
266
+ | `close` | - | Drawer closed |
267
+ | `contact-support` | - | Support mode activated |
268
+ | `new-chat` | - | New chat started |
269
+
270
+ ### Slots
271
+
272
+ ```vue
273
+ <AiChatDrawer v-model="isOpen">
274
+ <template #header="{ quota, isFullscreen, onNewChat, onClose }">
275
+ <MyCustomHeader />
276
+ </template>
277
+ <template #empty-state="{ suggestions, onClick }">
278
+ <MyCustomEmptyState />
279
+ </template>
280
+ <template #message="{ message, isUser, isLoading }">
281
+ <MyCustomMessage />
282
+ </template>
283
+ <template #input="{ modelValue, sending, onSubmit }">
284
+ <MyCustomInput />
285
+ </template>
286
+ </AiChatDrawer>
287
+ ```
288
+
289
+ ## UI Customization
290
+
291
+ Every component accepts a `ui` prop for CSS class customization:
292
+
293
+ ```vue
294
+ <AiChatDrawer
295
+ v-model="isOpen"
296
+ :ui="{
297
+ backdrop: "bg-black/50 backdrop-blur-sm",
298
+ drawer: "shadow-2xl",
299
+ panel: "bg-gray-50 dark:bg-gray-900",
300
+ header: "border-b-2 border-blue-500",
301
+ newChatButton: "bg-gradient-to-r from-blue-500 to-purple-500",
302
+ }"
303
+ />
304
+ ```
305
+
306
+ ### Available UI Interfaces
307
+
308
+ ```typescript
309
+ interface AiChatDrawerUI {
310
+ backdrop?: string
311
+ drawer?: string
312
+ panel?: string
313
+ header?: string
314
+ headerTitle?: string
315
+ body?: string
316
+ footer?: string
317
+ newChatButton?: string
318
+ errorContainer?: string
319
+ retryButton?: string
199
320
  }
200
- \`\`\`
201
321
 
202
- ### Lifecycle Callbacks
322
+ interface ChatInputUI {
323
+ root?: string
324
+ textarea?: string
325
+ sendButton?: string
326
+ attachButton?: string
327
+ suggestionsDropdown?: string
328
+ }
203
329
 
204
- \`\`\`typescript
205
- {
206
- onError: (error) => console.error(error),
207
- onQuotaFetched: (quota) => console.log(quota),
208
- onMessageSent: (message) => analytics.track('message_sent'),
209
- onResponseReceived: (message) => console.log(message),
210
- onDrawerToggle: (isOpen) => console.log(isOpen),
211
- onNewChat: () => console.log('New chat started'),
212
-
213
- // Stream hooks
214
- onStreamStart: () => console.log('Streaming...'),
215
- onStreamEnd: (fullMessage) => console.log('Done'),
216
- onStreamChunk: (chunk) => console.log(chunk.content),
217
- beforeSend: (payload) => payload,
218
- afterResponse: (message) => saveToHistory(message),
219
-
220
- // File upload hooks
221
- onFileUploadStart: (file) => console.log('Uploading', file.name),
222
- onFileUploadProgress: (file, progress) => console.log(progress),
223
- onFileUploadComplete: (file) => console.log('Done'),
224
- onFileUploadError: (file, error) => console.error(error),
330
+ interface ChatMessageUI {
331
+ root?: string
332
+ userBubble?: string
333
+ assistantBubble?: string
334
+ content?: string
335
+ loadingDots?: string
225
336
  }
226
- \`\`\`
337
+ ```
338
+
339
+ ## Store API
340
+
341
+ Access the Pinia store for advanced use cases:
342
+
343
+ ```typescript
344
+ import { useRestifyAiStore } from "@doderasoftware/restify-ai"
345
+
346
+ const store = useRestifyAiStore()
347
+
348
+ // State
349
+ store.chatHistory // ChatMessage[]
350
+ store.loading // boolean
351
+ store.sending // boolean
352
+ store.quota // { limit, used, remaining }
353
+ store.error // { message, failedQuestion, timestamp }
354
+
355
+ // Actions
356
+ store.toggleChat()
357
+ store.sendMessage(payload)
358
+ store.cancelRequest()
359
+ store.retryLastMessage()
360
+ store.clearChatHistory()
361
+ store.fetchQuota()
362
+ store.uploadFile(file)
363
+ ```
364
+
365
+ ## Composables
366
+
367
+ ### useAiDrawerShortcut
368
+
369
+ ```typescript
370
+ import { useAiDrawerShortcut } from "@doderasoftware/restify-ai"
371
+
372
+ // Uses store directly (recommended)
373
+ useAiDrawerShortcut()
374
+
375
+ // Or pass a ref
376
+ const drawerRef = ref(false)
377
+ useAiDrawerShortcut(drawerRef)
378
+ ```
227
379
 
228
- ### Labels / i18n
380
+ ### usePageAiContext
229
381
 
230
- \`\`\`typescript
382
+ ```typescript
383
+ import { usePageAiContext } from "@doderasoftware/restify-ai"
384
+
385
+ usePageAiContext({
386
+ pageType: "invoice-detail",
387
+ entityId: route.params.id,
388
+ entityType: "invoice",
389
+ metadata: { customerName: invoice.value?.customer?.name },
390
+ })
391
+ ```
392
+
393
+ ## Backend Integration
394
+
395
+ ### Laravel Restify Setup
396
+
397
+ This package is designed to work with [Laravel Restify](https://laravel-restify.com):
398
+
399
+ ```php
400
+ // routes/api.php
401
+ Route::middleware("auth:sanctum")->group(function () {
402
+ Route::post("/ai/ask", [AiController::class, "ask"]);
403
+ Route::post("/ai/upload", [AiController::class, "upload"]);
404
+ Route::get("/ai/quota", [AiController::class, "quota"]);
405
+ });
406
+ ```
407
+
408
+ ### Expected Request/Response Formats
409
+
410
+ **Ask Endpoint (SSE Stream):**
411
+
412
+ ```typescript
413
+ // Request
231
414
  {
232
- labels: {
233
- aiName: 'My AI Assistant',
234
- inputPlaceholder: 'Type your message...',
235
- emptyStateTitle: 'How can I help?',
236
- // 40+ customizable labels
237
- },
238
- translate: (key, params) => i18n.t(key, params),
239
- can: (permission) => user.hasPermission(permission),
415
+ question: string
416
+ history: Array<{ role: string, message: string }>
417
+ stream: true
418
+ files?: Array<{ id: string, name: string }>
419
+ mentions?: Array<{ id: string, type: string, name: string }>
240
420
  }
241
- \`\`\`
242
421
 
243
- ---
422
+ // Response: Server-Sent Events (OpenAI format)
423
+ data: {"choices":[{"delta":{"content":"Hello"}}]}
424
+ data: {"choices":[{"delta":{"content":" world"}}]}
425
+ data: [DONE]
426
+ ```
244
427
 
245
- ## 🧩 Components
428
+ **Upload Endpoint:**
246
429
 
247
- ### \`<AiChatDrawer>\`
430
+ ```typescript
431
+ { id: string, name: string, url: string, type: string, size: number }
432
+ ```
248
433
 
249
- Main chat drawer component with all features.
434
+ **Quota Endpoint:**
250
435
 
251
- #### Props
436
+ ```typescript
437
+ { limit: number, used: number, remaining: number }
438
+ ```
252
439
 
253
- | Prop | Type | Default | Description |
254
- |------|------|---------|-------------|
255
- | \`v-model\` | \`boolean\` | - | Drawer visibility |
256
- | \`width\` | \`string\` | \`'600px'\` | Drawer width |
257
- | \`fullscreenWidth\` | \`string\` | \`'90vw'\` | Fullscreen width |
258
- | \`position\` | \`'left' \| 'right'\` | \`'right'\` | Drawer position |
259
- | \`showBackdrop\` | \`boolean\` | \`false\` | Show backdrop overlay |
260
- | \`closeOnBackdropClick\` | \`boolean\` | \`false\` | Close on backdrop click |
261
- | \`closeOnEscape\` | \`boolean\` | \`true\` | Close on Escape key |
262
- | \`showQuota\` | \`boolean\` | \`true\` | Show quota display |
263
- | \`showFullscreenToggle\` | \`boolean\` | \`true\` | Show fullscreen button |
264
- | \`showMinimizeButton\` | \`boolean\` | \`true\` | Show minimize button |
265
- | \`showCloseButton\` | \`boolean\` | \`true\` | Show close button |
266
- | \`showNewChatButton\` | \`boolean\` | \`true\` | Show new chat button |
267
- | \`confirmClose\` | \`boolean\` | \`true\` | Confirm before closing |
268
- | \`ui\` | \`AiChatDrawerUI\` | - | Custom CSS classes |
269
- | \`texts\` | \`AiChatDrawerTexts\` | - | Custom text labels |
270
- | \`historyLimit\` | \`HistoryLimitConfig\` | - | History limit config |
271
- | \`loadingText\` | \`LoadingTextConfig\` | - | Dynamic loading text |
272
-
273
- #### Events
440
+ ## TypeScript Support
274
441
 
275
- | Event | Payload | Description |
276
- |-------|---------|-------------|
277
- | \`close\` | - | Drawer closed |
278
- | \`contact-support\` | - | Support mode triggered |
279
- | \`new-chat\` | - | New chat started |
280
-
281
- #### Slots
282
-
283
- | Slot | Props | Description |
284
- |------|-------|-------------|
285
- | \`header\` | \`HeaderSlotProps\` | Custom header |
286
- | \`empty-state\` | \`{ suggestions, onClick }\` | Custom empty state |
287
- | \`message\` | \`MessageSlotProps\` | Custom message rendering |
288
- | \`input\` | \`InputSlotProps\` | Custom input |
289
- | \`quota\` | \`{ quota }\` | Custom quota display |
290
- | \`setup\` | - | Custom setup guide |
291
- | \`context-link\` | - | Link above input |
292
-
293
- ### Other Components
294
-
295
- | Component | Description |
296
- |-----------|-------------|
297
- | \`<ChatInput>\` | Message input with attachments & mentions |
298
- | \`<ChatMessage>\` | Single message display |
299
- | \`<AiEmptyState>\` | Empty state with suggestions |
300
- | \`<MentionList>\` | @mention dropdown |
301
- | \`<AiAvatar>\` | AI avatar icon |
302
- | \`<UserAvatar>\` | User avatar icon |
303
- | \`<ChatMessageActions>\` | Copy/action buttons |
304
-
305
- All components accept \`:ui\` and \`:texts\` props for full customization.
442
+ Full TypeScript support with exported types:
306
443
 
307
- ---
444
+ ```typescript
445
+ import type {
446
+ ChatMessage,
447
+ ChatAttachment,
448
+ Mention,
449
+ ChatQuota,
450
+ RestifyAiConfig,
451
+ MentionProvider,
452
+ SuggestionProvider,
453
+ AISuggestion,
454
+ AiChatDrawerUI,
455
+ AiChatDrawerTexts,
456
+ } from "@doderasoftware/restify-ai"
457
+ ```
308
458
 
309
- ## 🎨 UI Customization
459
+ ## Browser Support
310
460
 
311
- Every component accepts a \`:ui\` prop for CSS class overrides:
461
+ - Chrome 80+
462
+ - Firefox 75+
463
+ - Safari 13+
464
+ - Edge 80+
312
465
 
313
- \`\`\`vue
314
- <AiChatDrawer
315
- :ui="{
316
- drawer: 'my-drawer-class',
317
- header: 'my-header-class',
318
- body: 'my-body-class',
319
- }"
320
- :texts="{
321
- title: 'My AI',
322
- placeholder: 'Ask anything...',
323
- }"
324
- />
325
- \`\`\`
466
+ ## Contributing
326
467
 
327
- ### Available UI Props
468
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
328
469
 
329
- - \`AiChatDrawerUI\` - backdrop, drawer, panel, header, body, etc.
330
- - \`ChatInputUI\` - root, textarea, sendButton, attachButton, etc.
331
- - \`ChatMessageUI\` - userBubble, assistantBubble, loadingDots, etc.
332
- - \`AiEmptyStateUI\` - grid, suggestionCard, title, etc.
333
- - \`MentionListUI\` - item, itemSelected, groupHeader, etc.
470
+ ## License
471
+
472
+ [MIT](LICENSE) - [BinarCode](https://binarcode.com)
334
473
 
335
474
  ---
336
475
 
337
- ## 📚 Composables
338
-
339
- \`\`\`typescript
340
- import {
341
- useRestifyAiStore, // Pinia store
342
- useAiSuggestions, // Suggestions management
343
- useAiContext, // Page context
344
- usePageAiContext, // Route-based context
345
- useKeyboardShortcut, // Custom shortcuts
346
- useAiDrawerShortcut, // Drawer toggle shortcut
347
- useMentionParsing, // Parse @mentions
348
- useChatMarkdown, // Markdown rendering
349
- useChatScroll, // Auto-scroll
350
- useChatErrorHandling, // Error management
351
- useLoadingText, // Dynamic loading messages
352
- useHistoryLimit, // History limit dialogs
353
- } from '@doderasoftware/restify-ai'
354
- \`\`\`
355
-
356
- ### Store Actions
357
-
358
- \`\`\`typescript
476
+ <div align="center">
477
+ <p><strong>Built with love by <a href="https://binarcode.com">BinarCode</a></strong></p>
478
+ <p>
479
+ <a href="https://laravel-restify.com">Laravel Restify</a> |
480
+ <a href="https://github.com/BinarCode/laravel-restify">GitHub</a> |
481
+ <a href="https://binarcode.com">Website</a>
482
+ </p>
483
+ <p><sub>Published by <a href="https://doderasoft.com">Dodera Software</a></sub></p>
484
+ </div>
485
+
486
+ ## Store API
487
+
488
+ Access the Pinia store for advanced use cases:
489
+
490
+ ```typescript
491
+ import { useRestifyAiStore } from "@doderasoftware/restify-ai"
492
+
359
493
  const store = useRestifyAiStore()
360
494
 
361
- store.askQuestion(question, attachments, mentions)
495
+ // State
496
+ store.chatHistory // ChatMessage[]
497
+ store.loading // boolean
498
+ store.sending // boolean
499
+ store.quota // { limit, used, remaining }
500
+ store.error // { message, failedQuestion, timestamp }
501
+
502
+ // Actions
503
+ store.toggleChat()
504
+ store.sendMessage(payload)
362
505
  store.cancelRequest()
363
- store.retry()
506
+ store.retryLastMessage()
364
507
  store.clearChatHistory()
365
- store.clearError()
366
508
  store.fetchQuota()
367
- store.toggleSupportMode()
368
- \`\`\`
509
+ store.uploadFile(file)
510
+ ```
369
511
 
370
- ### Store State
512
+ ## Composables
371
513
 
372
- \`\`\`typescript
373
- store.chatHistory // ChatMessage[]
374
- store.sending // boolean
375
- store.error // ChatError
376
- store.quota // ChatQuota
377
- store.showChat // boolean
378
- store.isFullscreen // boolean
379
- store.supportRequestMode // boolean
380
- \`\`\`
514
+ ### useAiDrawerShortcut
381
515
 
382
- ---
516
+ ```typescript
517
+ import { useAiDrawerShortcut } from "@doderasoftware/restify-ai"
383
518
 
384
- ## 🔧 Advanced Features
519
+ // Uses store directly (recommended)
520
+ useAiDrawerShortcut()
385
521
 
386
- ### History Limit Dialog
522
+ // Or pass a ref
523
+ const drawerRef = ref(false)
524
+ useAiDrawerShortcut(drawerRef)
525
+ ```
387
526
 
388
- \`\`\`vue
389
- <AiChatDrawer
390
- :history-limit="{
391
- limit: 20,
392
- showWarningAt: 3,
393
- warningMessage: 'Almost at limit!',
394
- limitMessage: 'Start a new chat to continue.',
395
- }"
396
- />
397
- \`\`\`
527
+ ### usePageAiContext
398
528
 
399
- ### Dynamic Loading Text
529
+ ```typescript
530
+ import { usePageAiContext } from "@doderasoftware/restify-ai"
400
531
 
401
- \`\`\`vue
402
- <AiChatDrawer
403
- :loading-text="{
404
- messages: ['Thinking...', 'Analyzing...', 'Almost done...'],
405
- intervals: [0, 2000, 4000],
406
- }"
407
- />
408
- \`\`\`
532
+ usePageAiContext({
533
+ pageType: "invoice-detail",
534
+ entityId: route.params.id,
535
+ entityType: "invoice",
536
+ metadata: { customerName: invoice.value?.customer?.name },
537
+ })
538
+ ```
409
539
 
410
- ### Keyboard Shortcut
540
+ ## Backend Integration
411
541
 
412
- \`\`\`typescript
413
- // Plugin config
414
- { keyboardShortcut: 'cmd+shift+a' }
542
+ ### Laravel Restify Setup
415
543
 
416
- // Or use composable
417
- const { toggle } = useAiDrawerShortcut()
418
- \`\`\`
544
+ This package is designed to work with [Laravel Restify](https://laravel-restify.com):
419
545
 
420
- ---
546
+ ```php
547
+ // routes/api.php
548
+ Route::middleware("auth:sanctum")->group(function () {
549
+ Route::post("/ai/ask", [AiController::class, "ask"]);
550
+ Route::post("/ai/upload", [AiController::class, "upload"]);
551
+ Route::get("/ai/quota", [AiController::class, "quota"]);
552
+ });
553
+ ```
421
554
 
422
- ## 📡 Laravel Restify Backend
555
+ ### Expected Request/Response Formats
423
556
 
424
- Expected SSE endpoint format:
557
+ **Ask Endpoint (SSE Stream):**
425
558
 
426
- \`\`\`php
427
- // routes/api.php
428
- Route::post('/ai/ask', [AiController::class, 'ask']);
429
- Route::get('/ai/quota', [AiController::class, 'quota']);
430
- \`\`\`
431
-
432
- \`\`\`php
433
- // AiController.php
434
- public function ask(Request $request)
559
+ ```typescript
560
+ // Request
435
561
  {
436
- return response()->stream(function () use ($request) {
437
- // Stream OpenAI-format chunks
438
- echo "data: " . json_encode([
439
- 'choices' => [['delta' => ['content' => 'Hello']]]
440
- ]) . "\n\n";
441
- ob_flush();
442
- flush();
443
-
444
- echo "data: [DONE]\n\n";
445
- }, 200, [
446
- 'Content-Type' => 'text/event-stream',
447
- 'Cache-Control' => 'no-cache',
448
- 'X-Accel-Buffering' => 'no',
449
- ]);
562
+ question: string
563
+ history: Array<{ role: string, message: string }>
564
+ stream: true
565
+ files?: Array<{ id: string, name: string }>
566
+ mentions?: Array<{ id: string, type: string, name: string }>
450
567
  }
451
- \`\`\`
452
568
 
453
- ---
569
+ // Response: Server-Sent Events (OpenAI format)
570
+ data: {"choices":[{"delta":{"content":"Hello"}}]}
571
+ data: {"choices":[{"delta":{"content":" world"}}]}
572
+ data: [DONE]
573
+ ```
574
+
575
+ **Upload Endpoint:**
576
+
577
+ ```typescript
578
+ { id: string, name: string, url: string, type: string, size: number }
579
+ ```
454
580
 
455
- ## 📝 TypeScript Types
581
+ **Quota Endpoint:**
456
582
 
457
- All types are exported:
583
+ ```typescript
584
+ { limit: number, used: number, remaining: number }
585
+ ```
458
586
 
459
- \`\`\`typescript
587
+ ## TypeScript Support
588
+
589
+ Full TypeScript support with exported types:
590
+
591
+ ```typescript
460
592
  import type {
461
593
  ChatMessage,
462
594
  ChatAttachment,
463
595
  Mention,
596
+ ChatQuota,
597
+ RestifyAiConfig,
464
598
  MentionProvider,
465
599
  SuggestionProvider,
466
600
  AISuggestion,
467
- RestifyAiConfig,
468
- ChatQuota,
469
- ChatError,
470
- // UI types
471
601
  AiChatDrawerUI,
472
- ChatInputUI,
473
- ChatMessageUI,
474
- // Text types
475
602
  AiChatDrawerTexts,
476
- ChatInputTexts,
477
- // Config types
478
- HistoryLimitConfig,
479
- LoadingTextConfig,
480
- RetryConfig,
481
- // Hook types
482
- BeforeSendHook,
483
- AfterResponseHook,
484
- StreamParserFunction,
485
- } from '@doderasoftware/restify-ai'
486
- \`\`\`
603
+ } from "@doderasoftware/restify-ai"
604
+ ```
487
605
 
488
- ---
606
+ ## Browser Support
489
607
 
490
- ## 📋 Quick Reference
491
-
492
- | Feature | Configuration |
493
- |---------|---------------|
494
- | SSE Streaming | Built-in, automatic |
495
- | Keyboard Shortcut | \`keyboardShortcut: 'cmd+g'\` |
496
- | @Mentions | \`mentionProviders: [...]\` |
497
- | Route Suggestions | \`suggestionProviders: [...]\` |
498
- | File Attachments | \`endpoints.uploadFile\` + \`maxAttachments\` |
499
- | Support Mode | \`enableSupportMode: true\` |
500
- | Custom Headers | \`getCustomHeaders: () => ({...})\` |
501
- | Auth Token | \`getAuthToken: () => token\` |
502
- | Base URL | \`baseUrl: 'https://api.example.com'\` |
503
- | Error Handling | \`onError: (err) => {...}\` |
504
- | Retry Logic | \`retry: { maxRetries: 3 }\` |
505
- | UI Customization | \`:ui\` prop on all components |
506
- | Text/i18n | \`:texts\` prop + \`labels\` config |
507
- | History Limit | \`:history-limit\` prop |
508
- | Loading Messages | \`:loading-text\` prop |
608
+ - Chrome 80+
609
+ - Firefox 75+
610
+ - Safari 13+
611
+ - Edge 80+
509
612
 
510
- ---
613
+ ## Contributing
511
614
 
512
- ## 📄 License
615
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
616
+
617
+ ## License
618
+
619
+ [MIT](LICENSE) - [BinarCode](https://binarcode.com)
620
+
621
+ ---
513
622
 
514
- MIT © [Dodera Software](https://github.com/doderasoftware)
623
+ <div align="center">
624
+ <p><strong>Built with love by <a href="https://binarcode.com">BinarCode</a></strong></p>
625
+ <p>
626
+ <a href="https://laravel-restify.com">Laravel Restify</a> |
627
+ <a href="https://github.com/BinarCode/laravel-restify">GitHub</a> |
628
+ <a href="https://binarcode.com">Website</a>
629
+ </p>
630
+ <p><sub>Published by <a href="https://doderasoft.com">Dodera Software</a></sub></p>
631
+ </div>