@trainly/react 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Trainly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,254 @@
1
+ # @trainly/react
2
+
3
+ **Dead simple RAG integration for React apps**
4
+
5
+ Go from `npm install` to working AI in under 5 minutes. No backend required, no complex setup, just install and use.
6
+
7
+ ## 🚀 Quick Start
8
+
9
+ ### 1. Install
10
+
11
+ ```bash
12
+ npm install @trainly/react
13
+ ```
14
+
15
+ ### 2. Setup (2 lines)
16
+
17
+ ```tsx
18
+ // app/layout.tsx
19
+ import { TrainlyProvider } from "@trainly/react";
20
+
21
+ export default function RootLayout({ children }) {
22
+ return (
23
+ <html>
24
+ <body>
25
+ <TrainlyProvider appSecret="as_your_app_secret">
26
+ {children}
27
+ </TrainlyProvider>
28
+ </body>
29
+ </html>
30
+ );
31
+ }
32
+ ```
33
+
34
+ ### 3. Use anywhere (3 lines)
35
+
36
+ ```tsx
37
+ // Any component
38
+ import { useTrainly } from "@trainly/react";
39
+
40
+ function MyComponent() {
41
+ const { ask } = useTrainly();
42
+
43
+ const handleClick = async () => {
44
+ const answer = await ask("What is photosynthesis?");
45
+ console.log(answer); // Ready to use!
46
+ };
47
+
48
+ return <button onClick={handleClick}>Ask AI</button>;
49
+ }
50
+ ```
51
+
52
+ **That's it!** No auth setup, no API routes, no session management.
53
+
54
+ ## 📦 What's Included
55
+
56
+ ### Core Hook
57
+
58
+ ```tsx
59
+ const {
60
+ ask, // (question: string) => Promise<string>
61
+ upload, // (file: File) => Promise<void>
62
+ isLoading, // boolean
63
+ isConnected, // boolean
64
+ error, // string | null
65
+ } = useTrainly();
66
+ ```
67
+
68
+ ### Pre-built Components
69
+
70
+ ```tsx
71
+ import { TrainlyChat, TrainlyUpload, TrainlyStatus } from '@trainly/react';
72
+
73
+ // Drop-in chat interface
74
+ <TrainlyChat height="400px" showCitations={true} />
75
+
76
+ // Drop-in file upload
77
+ <TrainlyUpload accept=".pdf,.doc,.txt" />
78
+
79
+ // Connection status indicator
80
+ <TrainlyStatus />
81
+ ```
82
+
83
+ ## 🎯 Complete Example
84
+
85
+ ```tsx
86
+ import { TrainlyProvider, TrainlyChat, TrainlyUpload } from "@trainly/react";
87
+
88
+ function App() {
89
+ return (
90
+ <TrainlyProvider appSecret="as_demo_secret_123">
91
+ <div>
92
+ <h1>My Document Assistant</h1>
93
+
94
+ {/* File upload area */}
95
+ <TrainlyUpload onUpload={(files) => console.log("Uploaded:", files)} />
96
+
97
+ {/* Chat interface */}
98
+ <TrainlyChat
99
+ height="500px"
100
+ placeholder="Ask about your documents..."
101
+ showCitations={true}
102
+ />
103
+ </div>
104
+ </TrainlyProvider>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ## 🔧 Configuration Options
110
+
111
+ ### Authentication Modes
112
+
113
+ ```tsx
114
+ // Mode 1: App Secret (recommended for multi-user apps)
115
+ <TrainlyProvider appSecret="as_secret_123" />
116
+
117
+ // Mode 2: With user context
118
+ <TrainlyProvider
119
+ appSecret="as_secret_123"
120
+ userId="user_123"
121
+ userEmail="user@example.com"
122
+ />
123
+
124
+ // Mode 3: Direct API key (simple apps)
125
+ <TrainlyProvider apiKey="tk_chat_id_key" />
126
+ ```
127
+
128
+ ### Component Customization
129
+
130
+ ```tsx
131
+ <TrainlyChat
132
+ height="600px"
133
+ theme="dark"
134
+ placeholder="Ask me anything..."
135
+ showCitations={true}
136
+ enableFileUpload={true}
137
+ onMessage={(msg) => console.log(msg)}
138
+ onError={(err) => console.error(err)}
139
+ />
140
+
141
+ <TrainlyUpload
142
+ variant="drag-drop" // or "button" or "minimal"
143
+ accept=".pdf,.doc,.txt"
144
+ maxSize="10MB"
145
+ multiple={false}
146
+ onUpload={(files) => console.log(files)}
147
+ />
148
+ ```
149
+
150
+ ## 🎨 Styling
151
+
152
+ Components use Tailwind classes by default but can be fully customized:
153
+
154
+ ```tsx
155
+ <TrainlyChat
156
+ className="my-custom-chat"
157
+ height="400px"
158
+ />
159
+
160
+ // Override with CSS
161
+ .my-custom-chat {
162
+ border: 2px solid blue;
163
+ border-radius: 12px;
164
+ }
165
+ ```
166
+
167
+ ## 📖 API Reference
168
+
169
+ ### useTrainly()
170
+
171
+ The main hook for interacting with Trainly.
172
+
173
+ ```tsx
174
+ const {
175
+ // Core functions
176
+ ask: (question: string) => Promise<string>,
177
+ askWithCitations: (question: string) => Promise<{answer: string, citations: Citation[]}>,
178
+ upload: (file: File) => Promise<UploadResult>,
179
+
180
+ // State
181
+ isLoading: boolean,
182
+ isConnected: boolean,
183
+ error: TrainlyError | null,
184
+
185
+ // Advanced
186
+ clearError: () => void,
187
+ reconnect: () => Promise<void>,
188
+
189
+ // For chat components
190
+ messages: ChatMessage[],
191
+ sendMessage: (content: string) => Promise<void>,
192
+ clearMessages: () => void,
193
+ } = useTrainly();
194
+ ```
195
+
196
+ ### TrainlyProvider Props
197
+
198
+ ```tsx
199
+ interface TrainlyProviderProps {
200
+ children: React.ReactNode;
201
+ appSecret?: string; // App secret from Trainly dashboard
202
+ apiKey?: string; // Direct API key (alternative to appSecret)
203
+ baseUrl?: string; // Custom API URL (defaults to trainly.com)
204
+ userId?: string; // Your app's user ID
205
+ userEmail?: string; // Your app's user email
206
+ }
207
+ ```
208
+
209
+ ## 🔍 Examples
210
+
211
+ Check out the `/examples` folder for complete implementations:
212
+
213
+ - **Simple Chat App** - Drop-in components
214
+ - **Custom Implementation** - Build your own UI
215
+ - **Multi-user App** - User-specific workspaces
216
+ - **File-focused App** - Document analysis focus
217
+
218
+ ## 🛠️ Development
219
+
220
+ ```bash
221
+ # Clone the repo
222
+ git clone https://github.com/trainly/react-sdk.git
223
+ cd react-sdk
224
+
225
+ # Install dependencies
226
+ npm install
227
+
228
+ # Build the package
229
+ npm run build
230
+
231
+ # Watch mode for development
232
+ npm run dev
233
+ ```
234
+
235
+ ## 📝 License
236
+
237
+ MIT - see LICENSE file for details.
238
+
239
+ ## 🤝 Contributing
240
+
241
+ Contributions welcome! Please read CONTRIBUTING.md for guidelines.
242
+
243
+ ## 🆘 Support
244
+
245
+ - 📖 [Documentation](https://trainly.com/docs/react-sdk)
246
+ - 💬 [Discord Community](https://discord.gg/trainly)
247
+ - 📧 [Email Support](mailto:support@trainly.com)
248
+ - 🐛 [Report Issues](https://github.com/trainly/react-sdk/issues)
249
+
250
+ ---
251
+
252
+ **Made with ❤️ by the Trainly team**
253
+
254
+ _The simplest way to add AI to your React app_
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from "react";
2
+ import { TrainlyContextValue } from "./types";
3
+ export interface TrainlyProviderProps {
4
+ children: ReactNode;
5
+ appSecret?: string;
6
+ apiKey?: string;
7
+ baseUrl?: string;
8
+ userId?: string;
9
+ userEmail?: string;
10
+ }
11
+ export declare function TrainlyProvider({ children, appSecret, apiKey, baseUrl, userId, userEmail, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function useTrainlyContext(): TrainlyContextValue;
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
39
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
40
+ if (ar || !(i in from)) {
41
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
42
+ ar[i] = from[i];
43
+ }
44
+ }
45
+ return to.concat(ar || Array.prototype.slice.call(from));
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.TrainlyProvider = TrainlyProvider;
49
+ exports.useTrainlyContext = useTrainlyContext;
50
+ var jsx_runtime_1 = require("react/jsx-runtime");
51
+ var react_1 = require("react");
52
+ var TrainlyClient_1 = require("./api/TrainlyClient");
53
+ var TrainlyContext = (0, react_1.createContext)(undefined);
54
+ function TrainlyProvider(_a) {
55
+ var _this = this;
56
+ var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey, _b = _a.baseUrl, baseUrl = _b === void 0 ? "https://api.trainly.com" : _b, userId = _a.userId, userEmail = _a.userEmail;
57
+ var client = (0, react_1.useState)(function () {
58
+ return new TrainlyClient_1.TrainlyClient({
59
+ appSecret: appSecret,
60
+ apiKey: apiKey,
61
+ baseUrl: baseUrl,
62
+ userId: userId,
63
+ userEmail: userEmail,
64
+ });
65
+ })[0];
66
+ var _c = (0, react_1.useState)(false), isLoading = _c[0], setIsLoading = _c[1];
67
+ var _d = (0, react_1.useState)(false), isConnected = _d[0], setIsConnected = _d[1];
68
+ var _e = (0, react_1.useState)(null), error = _e[0], setError = _e[1];
69
+ var _f = (0, react_1.useState)([]), messages = _f[0], setMessages = _f[1];
70
+ // Auto-connect on mount
71
+ (0, react_1.useEffect)(function () {
72
+ connect();
73
+ }, []);
74
+ var connect = function () { return __awaiter(_this, void 0, void 0, function () {
75
+ var err_1;
76
+ return __generator(this, function (_a) {
77
+ switch (_a.label) {
78
+ case 0:
79
+ _a.trys.push([0, 2, 3, 4]);
80
+ setIsLoading(true);
81
+ setError(null);
82
+ return [4 /*yield*/, client.connect()];
83
+ case 1:
84
+ _a.sent();
85
+ setIsConnected(true);
86
+ return [3 /*break*/, 4];
87
+ case 2:
88
+ err_1 = _a.sent();
89
+ setError({
90
+ code: "CONNECTION_FAILED",
91
+ message: "Failed to connect to Trainly",
92
+ details: err_1,
93
+ });
94
+ setIsConnected(false);
95
+ return [3 /*break*/, 4];
96
+ case 3:
97
+ setIsLoading(false);
98
+ return [7 /*endfinally*/];
99
+ case 4: return [2 /*return*/];
100
+ }
101
+ });
102
+ }); };
103
+ var ask = function (question) { return __awaiter(_this, void 0, void 0, function () {
104
+ var response, err_2, error_1;
105
+ return __generator(this, function (_a) {
106
+ switch (_a.label) {
107
+ case 0:
108
+ _a.trys.push([0, 2, 3, 4]);
109
+ setIsLoading(true);
110
+ setError(null);
111
+ return [4 /*yield*/, client.ask(question)];
112
+ case 1:
113
+ response = _a.sent();
114
+ return [2 /*return*/, response.answer];
115
+ case 2:
116
+ err_2 = _a.sent();
117
+ error_1 = {
118
+ code: "QUERY_FAILED",
119
+ message: "Failed to get answer",
120
+ details: err_2,
121
+ };
122
+ setError(error_1);
123
+ throw error_1;
124
+ case 3:
125
+ setIsLoading(false);
126
+ return [7 /*endfinally*/];
127
+ case 4: return [2 /*return*/];
128
+ }
129
+ });
130
+ }); };
131
+ var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
132
+ var response, err_3, error_2;
133
+ return __generator(this, function (_a) {
134
+ switch (_a.label) {
135
+ case 0:
136
+ _a.trys.push([0, 2, 3, 4]);
137
+ setIsLoading(true);
138
+ setError(null);
139
+ return [4 /*yield*/, client.ask(question, { includeCitations: true })];
140
+ case 1:
141
+ response = _a.sent();
142
+ return [2 /*return*/, {
143
+ answer: response.answer,
144
+ citations: response.citations || [],
145
+ }];
146
+ case 2:
147
+ err_3 = _a.sent();
148
+ error_2 = {
149
+ code: "QUERY_FAILED",
150
+ message: "Failed to get answer with citations",
151
+ details: err_3,
152
+ };
153
+ setError(error_2);
154
+ throw error_2;
155
+ case 3:
156
+ setIsLoading(false);
157
+ return [7 /*endfinally*/];
158
+ case 4: return [2 /*return*/];
159
+ }
160
+ });
161
+ }); };
162
+ var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
163
+ var result, err_4, error_3;
164
+ return __generator(this, function (_a) {
165
+ switch (_a.label) {
166
+ case 0:
167
+ _a.trys.push([0, 2, 3, 4]);
168
+ setIsLoading(true);
169
+ setError(null);
170
+ return [4 /*yield*/, client.upload(file)];
171
+ case 1:
172
+ result = _a.sent();
173
+ return [2 /*return*/, result];
174
+ case 2:
175
+ err_4 = _a.sent();
176
+ error_3 = {
177
+ code: "UPLOAD_FAILED",
178
+ message: "Failed to upload file",
179
+ details: err_4,
180
+ };
181
+ setError(error_3);
182
+ throw error_3;
183
+ case 3:
184
+ setIsLoading(false);
185
+ return [7 /*endfinally*/];
186
+ case 4: return [2 /*return*/];
187
+ }
188
+ });
189
+ }); };
190
+ var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
191
+ var userMessage, response, assistantMessage_1, err_5;
192
+ return __generator(this, function (_a) {
193
+ switch (_a.label) {
194
+ case 0:
195
+ userMessage = {
196
+ id: Date.now().toString(),
197
+ role: "user",
198
+ content: content,
199
+ timestamp: new Date(),
200
+ };
201
+ setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [userMessage], false); });
202
+ _a.label = 1;
203
+ case 1:
204
+ _a.trys.push([1, 3, , 4]);
205
+ return [4 /*yield*/, askWithCitations(content)];
206
+ case 2:
207
+ response = _a.sent();
208
+ assistantMessage_1 = {
209
+ id: (Date.now() + 1).toString(),
210
+ role: "assistant",
211
+ content: response.answer,
212
+ timestamp: new Date(),
213
+ citations: response.citations,
214
+ };
215
+ setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
216
+ return [3 /*break*/, 4];
217
+ case 3:
218
+ err_5 = _a.sent();
219
+ // Error is already set by askWithCitations
220
+ console.error("Failed to send message:", err_5);
221
+ return [3 /*break*/, 4];
222
+ case 4: return [2 /*return*/];
223
+ }
224
+ });
225
+ }); };
226
+ var clearError = function () { return setError(null); };
227
+ var reconnect = function () { return connect(); };
228
+ var clearMessages = function () { return setMessages([]); };
229
+ var value = {
230
+ ask: ask,
231
+ askWithCitations: askWithCitations,
232
+ upload: upload,
233
+ isLoading: isLoading,
234
+ isConnected: isConnected,
235
+ error: error,
236
+ clearError: clearError,
237
+ reconnect: reconnect,
238
+ messages: messages,
239
+ sendMessage: sendMessage,
240
+ clearMessages: clearMessages,
241
+ };
242
+ return ((0, jsx_runtime_1.jsx)(TrainlyContext.Provider, { value: value, children: children }));
243
+ }
244
+ function useTrainlyContext() {
245
+ var context = (0, react_1.useContext)(TrainlyContext);
246
+ if (context === undefined) {
247
+ throw new Error("useTrainlyContext must be used within a TrainlyProvider");
248
+ }
249
+ return context;
250
+ }
@@ -0,0 +1,18 @@
1
+ import { TrainlyConfig, Citation, UploadResult } from "../types";
2
+ interface QueryResponse {
3
+ answer: string;
4
+ citations?: Citation[];
5
+ }
6
+ export declare class TrainlyClient {
7
+ private config;
8
+ private scopedToken;
9
+ constructor(config: TrainlyConfig);
10
+ connect(): Promise<void>;
11
+ ask(question: string, options?: {
12
+ includeCitations?: boolean;
13
+ }): Promise<QueryResponse>;
14
+ upload(file: File): Promise<UploadResult>;
15
+ private extractChatId;
16
+ private generateAnonymousId;
17
+ }
18
+ export {};
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TrainlyClient = void 0;
40
+ var TrainlyClient = /** @class */ (function () {
41
+ function TrainlyClient(config) {
42
+ this.scopedToken = null;
43
+ this.config = config;
44
+ }
45
+ TrainlyClient.prototype.connect = function () {
46
+ return __awaiter(this, void 0, void 0, function () {
47
+ var response, error, data;
48
+ return __generator(this, function (_a) {
49
+ switch (_a.label) {
50
+ case 0:
51
+ if (this.config.apiKey) {
52
+ // Direct API key mode - no additional setup needed
53
+ this.scopedToken = this.config.apiKey;
54
+ return [2 /*return*/];
55
+ }
56
+ if (!this.config.appSecret) {
57
+ throw new Error("Either appSecret or apiKey must be provided");
58
+ }
59
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/apps/users/provision"), {
60
+ method: "POST",
61
+ headers: {
62
+ Authorization: "Bearer ".concat(this.config.appSecret),
63
+ "Content-Type": "application/json",
64
+ },
65
+ body: JSON.stringify({
66
+ end_user_id: this.config.userId || this.generateAnonymousId(),
67
+ capabilities: ["ask", "upload"],
68
+ }),
69
+ })];
70
+ case 1:
71
+ response = _a.sent();
72
+ if (!!response.ok) return [3 /*break*/, 3];
73
+ return [4 /*yield*/, response.json()];
74
+ case 2:
75
+ error = _a.sent();
76
+ throw new Error("Failed to connect: ".concat(error.detail || response.statusText));
77
+ case 3: return [4 /*yield*/, response.json()];
78
+ case 4:
79
+ data = _a.sent();
80
+ this.scopedToken = data.scoped_token;
81
+ return [2 /*return*/];
82
+ }
83
+ });
84
+ });
85
+ };
86
+ TrainlyClient.prototype.ask = function (question_1) {
87
+ return __awaiter(this, arguments, void 0, function (question, options) {
88
+ var url, headers, body, response, error, data;
89
+ if (options === void 0) { options = {}; }
90
+ return __generator(this, function (_a) {
91
+ switch (_a.label) {
92
+ case 0:
93
+ if (!this.scopedToken) {
94
+ throw new Error("Not connected. Call connect() first.");
95
+ }
96
+ url = this.config.apiKey
97
+ ? "".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/answer_question")
98
+ : "".concat(this.config.baseUrl, "/v1/privacy/query");
99
+ headers = {
100
+ "Content-Type": "application/json",
101
+ };
102
+ body = { question: question };
103
+ if (this.config.apiKey) {
104
+ headers["Authorization"] = "Bearer ".concat(this.config.apiKey);
105
+ }
106
+ else {
107
+ headers["x-scoped-token"] = this.scopedToken;
108
+ body.end_user_id = this.config.userId || this.generateAnonymousId();
109
+ body.include_citations = options.includeCitations || false;
110
+ }
111
+ return [4 /*yield*/, fetch(url, {
112
+ method: "POST",
113
+ headers: headers,
114
+ body: JSON.stringify(body),
115
+ })];
116
+ case 1:
117
+ response = _a.sent();
118
+ if (!!response.ok) return [3 /*break*/, 3];
119
+ return [4 /*yield*/, response.json()];
120
+ case 2:
121
+ error = _a.sent();
122
+ throw new Error("Query failed: ".concat(error.detail || response.statusText));
123
+ case 3: return [4 /*yield*/, response.json()];
124
+ case 4:
125
+ data = _a.sent();
126
+ return [2 /*return*/, {
127
+ answer: data.answer,
128
+ citations: data.citations || [],
129
+ }];
130
+ }
131
+ });
132
+ });
133
+ };
134
+ TrainlyClient.prototype.upload = function (file) {
135
+ return __awaiter(this, void 0, void 0, function () {
136
+ var formData, response, error, presignedResponse, error, upload_url, uploadResponse;
137
+ return __generator(this, function (_a) {
138
+ switch (_a.label) {
139
+ case 0:
140
+ if (!this.scopedToken) {
141
+ throw new Error("Not connected. Call connect() first.");
142
+ }
143
+ if (!this.config.apiKey) return [3 /*break*/, 4];
144
+ formData = new FormData();
145
+ formData.append("file", file);
146
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/upload_file"), {
147
+ method: "POST",
148
+ headers: {
149
+ Authorization: "Bearer ".concat(this.config.apiKey),
150
+ },
151
+ body: formData,
152
+ })];
153
+ case 1:
154
+ response = _a.sent();
155
+ if (!!response.ok) return [3 /*break*/, 3];
156
+ return [4 /*yield*/, response.json()];
157
+ case 2:
158
+ error = _a.sent();
159
+ throw new Error("Upload failed: ".concat(error.detail || response.statusText));
160
+ case 3: return [2 /*return*/, {
161
+ success: true,
162
+ filename: file.name,
163
+ size: file.size,
164
+ message: "File uploaded successfully",
165
+ }];
166
+ case 4: return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/upload/presigned-url"), {
167
+ method: "POST",
168
+ headers: {
169
+ "Content-Type": "application/json",
170
+ "x-scoped-token": this.scopedToken,
171
+ },
172
+ body: JSON.stringify({
173
+ end_user_id: this.config.userId || this.generateAnonymousId(),
174
+ filename: file.name,
175
+ file_type: file.type,
176
+ }),
177
+ })];
178
+ case 5:
179
+ presignedResponse = _a.sent();
180
+ if (!!presignedResponse.ok) return [3 /*break*/, 7];
181
+ return [4 /*yield*/, presignedResponse.json()];
182
+ case 6:
183
+ error = _a.sent();
184
+ throw new Error("Failed to get upload URL: ".concat(error.detail || presignedResponse.statusText));
185
+ case 7: return [4 /*yield*/, presignedResponse.json()];
186
+ case 8:
187
+ upload_url = (_a.sent()).upload_url;
188
+ return [4 /*yield*/, fetch(upload_url, {
189
+ method: "PUT",
190
+ body: file,
191
+ headers: {
192
+ "Content-Type": file.type,
193
+ },
194
+ })];
195
+ case 9:
196
+ uploadResponse = _a.sent();
197
+ if (!uploadResponse.ok) {
198
+ throw new Error("Failed to upload file");
199
+ }
200
+ return [2 /*return*/, {
201
+ success: true,
202
+ filename: file.name,
203
+ size: file.size,
204
+ message: "File uploaded to your private workspace",
205
+ }];
206
+ }
207
+ });
208
+ });
209
+ };
210
+ TrainlyClient.prototype.extractChatId = function () {
211
+ if (!this.config.apiKey) {
212
+ throw new Error("API key not provided");
213
+ }
214
+ // Extract chat ID from API key format: tk_chat_id_rest
215
+ var parts = this.config.apiKey.split("_");
216
+ if (parts.length < 3) {
217
+ throw new Error("Invalid API key format");
218
+ }
219
+ return parts[1];
220
+ };
221
+ TrainlyClient.prototype.generateAnonymousId = function () {
222
+ return "anon_".concat(Math.random().toString(36).substr(2, 9));
223
+ };
224
+ return TrainlyClient;
225
+ }());
226
+ exports.TrainlyClient = TrainlyClient;
@@ -0,0 +1,14 @@
1
+ export interface TrainlyChatProps {
2
+ height?: string;
3
+ className?: string;
4
+ placeholder?: string;
5
+ showCitations?: boolean;
6
+ enableFileUpload?: boolean;
7
+ theme?: "light" | "dark" | "auto";
8
+ onMessage?: (message: {
9
+ role: "user" | "assistant";
10
+ content: string;
11
+ }) => void;
12
+ onError?: (error: string) => void;
13
+ }
14
+ export declare function TrainlyChat({ height, className, placeholder, showCitations, enableFileUpload, theme, onMessage, onError, }: TrainlyChatProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TrainlyChat = TrainlyChat;
40
+ var jsx_runtime_1 = require("react/jsx-runtime");
41
+ var react_1 = require("react");
42
+ var useTrainly_1 = require("../useTrainly");
43
+ function TrainlyChat(_a) {
44
+ var _this = this;
45
+ var _b = _a.height, height = _b === void 0 ? "400px" : _b, _c = _a.className, className = _c === void 0 ? "" : _c, _d = _a.placeholder, placeholder = _d === void 0 ? "Ask me anything..." : _d, _e = _a.showCitations, showCitations = _e === void 0 ? true : _e, _f = _a.enableFileUpload, enableFileUpload = _f === void 0 ? true : _f, _g = _a.theme, theme = _g === void 0 ? "auto" : _g, onMessage = _a.onMessage, onError = _a.onError;
46
+ var _h = (0, useTrainly_1.useTrainly)(), messages = _h.messages, sendMessage = _h.sendMessage, isLoading = _h.isLoading, error = _h.error, clearError = _h.clearError;
47
+ var _j = (0, react_1.useState)(""), input = _j[0], setInput = _j[1];
48
+ var messagesEndRef = (0, react_1.useRef)(null);
49
+ var fileInputRef = (0, react_1.useRef)(null);
50
+ // Auto-scroll to bottom
51
+ (0, react_1.useEffect)(function () {
52
+ var _a;
53
+ (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
54
+ }, [messages]);
55
+ // Handle errors
56
+ (0, react_1.useEffect)(function () {
57
+ if (error && onError) {
58
+ onError(error.message);
59
+ }
60
+ }, [error, onError]);
61
+ var handleSubmit = function (e) { return __awaiter(_this, void 0, void 0, function () {
62
+ var messageContent, err_1;
63
+ return __generator(this, function (_a) {
64
+ switch (_a.label) {
65
+ case 0:
66
+ e.preventDefault();
67
+ if (!input.trim() || isLoading)
68
+ return [2 /*return*/];
69
+ messageContent = input.trim();
70
+ setInput("");
71
+ _a.label = 1;
72
+ case 1:
73
+ _a.trys.push([1, 3, , 4]);
74
+ return [4 /*yield*/, sendMessage(messageContent)];
75
+ case 2:
76
+ _a.sent();
77
+ onMessage === null || onMessage === void 0 ? void 0 : onMessage({ role: "user", content: messageContent });
78
+ return [3 /*break*/, 4];
79
+ case 3:
80
+ err_1 = _a.sent();
81
+ console.error("Failed to send message:", err_1);
82
+ return [3 /*break*/, 4];
83
+ case 4: return [2 /*return*/];
84
+ }
85
+ });
86
+ }); };
87
+ var handleFileUpload = function (e) {
88
+ var _a;
89
+ var file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
90
+ if (file) {
91
+ // This would trigger upload and add a system message
92
+ console.log("File selected:", file.name);
93
+ }
94
+ };
95
+ var baseClasses = "\n flex flex-col border border-gray-200 rounded-lg overflow-hidden\n ".concat(theme === "dark" ? "bg-gray-900 text-white border-gray-700" : "bg-white text-gray-900", "\n ").concat(className, "\n ");
96
+ return ((0, jsx_runtime_1.jsxs)("div", { className: baseClasses, style: { height: height }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: [messages.length === 0 && ((0, jsx_runtime_1.jsx)("div", { className: "text-center text-gray-500 py-8", children: (0, jsx_runtime_1.jsx)("p", { children: "Start a conversation by asking a question!" }) })), messages.map(function (message) { return ((0, jsx_runtime_1.jsx)("div", { className: "flex ".concat(message.role === "user" ? "justify-end" : "justify-start"), children: (0, jsx_runtime_1.jsxs)("div", { className: "\n max-w-[80%] p-3 rounded-lg\n ".concat(message.role === "user"
97
+ ? "bg-blue-500 text-white rounded-br-none"
98
+ : "bg-gray-100 text-gray-900 rounded-bl-none", "\n "), children: [(0, jsx_runtime_1.jsx)("p", { className: "whitespace-pre-wrap", children: message.content }), showCitations &&
99
+ message.citations &&
100
+ message.citations.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-3 pt-3 border-t border-gray-300", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xs font-semibold mb-2", children: "Sources:" }), message.citations.map(function (citation, idx) { return ((0, jsx_runtime_1.jsxs)("div", { className: "text-xs bg-white bg-opacity-20 p-2 rounded mb-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium", children: citation.source }), (0, jsx_runtime_1.jsxs)("p", { className: "opacity-75", children: ["\"", citation.snippet, "\""] })] }, idx)); })] }))] }) }, message.id)); }), isLoading && ((0, jsx_runtime_1.jsx)("div", { className: "flex justify-start", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-gray-100 text-gray-900 p-3 rounded-lg rounded-bl-none", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500" }), (0, jsx_runtime_1.jsx)("span", { children: "Thinking..." })] }) }) })), (0, jsx_runtime_1.jsx)("div", { ref: messagesEndRef })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "px-4 py-2 bg-red-50 border-t border-red-200 text-red-700 text-sm", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { children: error.message }), (0, jsx_runtime_1.jsx)("button", { onClick: clearError, className: "text-red-500 hover:text-red-700", children: "\u00D7" })] }) })), (0, jsx_runtime_1.jsx)("div", { className: "border-t border-gray-200 p-4", children: (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex gap-2", children: [enableFileUpload && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, type: "file", accept: ".pdf,.doc,.docx,.txt,.md", onChange: handleFileUpload, className: "hidden" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: "px-3 py-2 text-gray-500 hover:text-gray-700 border border-gray-300 rounded-lg", title: "Upload file", children: "\uD83D\uDCCE" })] })), (0, jsx_runtime_1.jsx)("input", { type: "text", value: input, onChange: function (e) { return setInput(e.target.value); }, placeholder: placeholder, disabled: isLoading, className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: isLoading || !input.trim(), className: "px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed", children: "Send" })] }) })] }));
101
+ }
@@ -0,0 +1,6 @@
1
+ export interface TrainlyStatusProps {
2
+ showDetails?: boolean;
3
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "inline";
4
+ className?: string;
5
+ }
6
+ export declare function TrainlyStatus({ showDetails, position, className, }: TrainlyStatusProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrainlyStatus = TrainlyStatus;
4
+ var jsx_runtime_1 = require("react/jsx-runtime");
5
+ var useTrainly_1 = require("../useTrainly");
6
+ function TrainlyStatus(_a) {
7
+ var _b = _a.showDetails, showDetails = _b === void 0 ? true : _b, _c = _a.position, position = _c === void 0 ? "inline" : _c, _d = _a.className, className = _d === void 0 ? "" : _d;
8
+ var _e = (0, useTrainly_1.useTrainly)(), isConnected = _e.isConnected, isLoading = _e.isLoading, error = _e.error;
9
+ var getStatusInfo = function () {
10
+ if (error) {
11
+ return {
12
+ icon: "❌",
13
+ text: "Connection failed",
14
+ color: "text-red-600 bg-red-50 border-red-200",
15
+ details: error.message,
16
+ };
17
+ }
18
+ if (isLoading) {
19
+ return {
20
+ icon: "🔄",
21
+ text: "Connecting...",
22
+ color: "text-yellow-600 bg-yellow-50 border-yellow-200",
23
+ details: "Establishing connection to Trainly",
24
+ };
25
+ }
26
+ if (isConnected) {
27
+ return {
28
+ icon: "✅",
29
+ text: "Connected",
30
+ color: "text-green-600 bg-green-50 border-green-200",
31
+ details: "Ready to answer questions",
32
+ };
33
+ }
34
+ return {
35
+ icon: "⚠️",
36
+ text: "Disconnected",
37
+ color: "text-gray-600 bg-gray-50 border-gray-200",
38
+ details: "Not connected to Trainly",
39
+ };
40
+ };
41
+ var status = getStatusInfo();
42
+ var positionClasses = {
43
+ "top-left": "fixed top-4 left-4 z-50",
44
+ "top-right": "fixed top-4 right-4 z-50",
45
+ "bottom-left": "fixed bottom-4 left-4 z-50",
46
+ "bottom-right": "fixed bottom-4 right-4 z-50",
47
+ inline: "",
48
+ };
49
+ var baseClasses = "\n inline-flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium\n ".concat(status.color, "\n ").concat(positionClasses[position], "\n ").concat(className, "\n ");
50
+ return ((0, jsx_runtime_1.jsxs)("div", { className: baseClasses, children: [(0, jsx_runtime_1.jsx)("span", { className: status.icon === "🔄" ? "animate-spin" : "", children: status.icon }), (0, jsx_runtime_1.jsx)("span", { children: status.text }), showDetails && status.details && ((0, jsx_runtime_1.jsxs)("span", { className: "text-xs opacity-75 ml-1", children: ["- ", status.details] }))] }));
51
+ }
@@ -0,0 +1,10 @@
1
+ export interface TrainlyUploadProps {
2
+ variant?: "drag-drop" | "button" | "minimal";
3
+ accept?: string;
4
+ maxSize?: string;
5
+ multiple?: boolean;
6
+ className?: string;
7
+ onUpload?: (files: File[]) => void;
8
+ onError?: (error: string) => void;
9
+ }
10
+ export declare function TrainlyUpload({ variant, accept, maxSize, multiple, className, onUpload, onError, }: TrainlyUploadProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TrainlyUpload = TrainlyUpload;
40
+ var jsx_runtime_1 = require("react/jsx-runtime");
41
+ var react_1 = require("react");
42
+ var useTrainly_1 = require("../useTrainly");
43
+ function TrainlyUpload(_a) {
44
+ var _this = this;
45
+ var _b = _a.variant, variant = _b === void 0 ? "drag-drop" : _b, _c = _a.accept, accept = _c === void 0 ? ".pdf,.doc,.docx,.txt,.md" : _c, _d = _a.maxSize, maxSize = _d === void 0 ? "10MB" : _d, _e = _a.multiple, multiple = _e === void 0 ? false : _e, _f = _a.className, className = _f === void 0 ? "" : _f, onUpload = _a.onUpload, onError = _a.onError;
46
+ var _g = (0, useTrainly_1.useTrainly)(), upload = _g.upload, isLoading = _g.isLoading;
47
+ var _h = (0, react_1.useState)(false), isDragOver = _h[0], setIsDragOver = _h[1];
48
+ var fileInputRef = (0, react_1.useRef)(null);
49
+ var maxSizeBytes = parseMaxSize(maxSize);
50
+ var handleFiles = function (files) { return __awaiter(_this, void 0, void 0, function () {
51
+ var fileArray, _i, fileArray_1, file, error, _a, fileArray_2, file, err_1;
52
+ return __generator(this, function (_b) {
53
+ switch (_b.label) {
54
+ case 0:
55
+ if (!files)
56
+ return [2 /*return*/];
57
+ fileArray = Array.from(files);
58
+ // Validate files
59
+ for (_i = 0, fileArray_1 = fileArray; _i < fileArray_1.length; _i++) {
60
+ file = fileArray_1[_i];
61
+ if (maxSizeBytes && file.size > maxSizeBytes) {
62
+ error = "File \"".concat(file.name, "\" is too large. Maximum size is ").concat(maxSize, ".");
63
+ onError === null || onError === void 0 ? void 0 : onError(error);
64
+ return [2 /*return*/];
65
+ }
66
+ }
67
+ _b.label = 1;
68
+ case 1:
69
+ _b.trys.push([1, 6, , 7]);
70
+ _a = 0, fileArray_2 = fileArray;
71
+ _b.label = 2;
72
+ case 2:
73
+ if (!(_a < fileArray_2.length)) return [3 /*break*/, 5];
74
+ file = fileArray_2[_a];
75
+ return [4 /*yield*/, upload(file)];
76
+ case 3:
77
+ _b.sent();
78
+ _b.label = 4;
79
+ case 4:
80
+ _a++;
81
+ return [3 /*break*/, 2];
82
+ case 5:
83
+ onUpload === null || onUpload === void 0 ? void 0 : onUpload(fileArray);
84
+ return [3 /*break*/, 7];
85
+ case 6:
86
+ err_1 = _b.sent();
87
+ onError === null || onError === void 0 ? void 0 : onError(err_1 instanceof Error ? err_1.message : "Upload failed");
88
+ return [3 /*break*/, 7];
89
+ case 7: return [2 /*return*/];
90
+ }
91
+ });
92
+ }); };
93
+ var handleDrop = function (e) {
94
+ e.preventDefault();
95
+ setIsDragOver(false);
96
+ handleFiles(e.dataTransfer.files);
97
+ };
98
+ var handleDragOver = function (e) {
99
+ e.preventDefault();
100
+ setIsDragOver(true);
101
+ };
102
+ var handleDragLeave = function (e) {
103
+ e.preventDefault();
104
+ setIsDragOver(false);
105
+ };
106
+ var handleFileSelect = function (e) {
107
+ handleFiles(e.target.files);
108
+ };
109
+ if (variant === "button") {
110
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileSelect, className: "hidden" }), (0, jsx_runtime_1.jsx)("button", { onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: isLoading, className: "\n px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600\n disabled:opacity-50 disabled:cursor-not-allowed\n ".concat(className, "\n "), children: isLoading ? "Uploading..." : "Upload Files" })] }));
111
+ }
112
+ if (variant === "minimal") {
113
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileSelect, className: "hidden" }), (0, jsx_runtime_1.jsx)("button", { onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: isLoading, className: "text-blue-500 hover:text-blue-600 underline ".concat(className), children: isLoading ? "Uploading..." : "Upload" })] }));
114
+ }
115
+ // Default: drag-drop variant
116
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileSelect, className: "hidden" }), (0, jsx_runtime_1.jsx)("div", { onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: "\n border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer\n transition-colors duration-200\n ".concat(isDragOver ? "border-blue-500 bg-blue-50" : "hover:border-gray-400", "\n ").concat(isLoading ? "opacity-50 cursor-not-allowed" : "", "\n ").concat(className, "\n "), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-4xl", children: "\uD83D\uDCC4" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-lg font-medium text-gray-700", children: isLoading
117
+ ? "Uploading..."
118
+ : "Drop files here or click to upload" }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-gray-500 mt-2", children: ["Supports: ", accept.replace(/\./g, "").toUpperCase(), " files up to", " ", maxSize] })] })] }) })] }));
119
+ }
120
+ function parseMaxSize(maxSize) {
121
+ var match = maxSize.match(/^(\d+)\s*(GB|MB|KB)?$/i);
122
+ if (!match)
123
+ return null;
124
+ var value = parseInt(match[1]);
125
+ var unit = (match[2] || "B").toUpperCase();
126
+ switch (unit) {
127
+ case "GB":
128
+ return value * 1024 * 1024 * 1024;
129
+ case "MB":
130
+ return value * 1024 * 1024;
131
+ case "KB":
132
+ return value * 1024;
133
+ default:
134
+ return value;
135
+ }
136
+ }
@@ -0,0 +1,6 @@
1
+ export { TrainlyProvider } from "./TrainlyProvider";
2
+ export { useTrainly } from "./useTrainly";
3
+ export { TrainlyChat } from "./components/TrainlyChat";
4
+ export { TrainlyUpload } from "./components/TrainlyUpload";
5
+ export { TrainlyStatus } from "./components/TrainlyStatus";
6
+ export type { TrainlyProviderProps, TrainlyConfig, ChatMessage, Citation, UploadResult, TrainlyError, } from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrainlyStatus = exports.TrainlyUpload = exports.TrainlyChat = exports.useTrainly = exports.TrainlyProvider = void 0;
4
+ // Main exports for @trainly/react SDK
5
+ var TrainlyProvider_1 = require("./TrainlyProvider");
6
+ Object.defineProperty(exports, "TrainlyProvider", { enumerable: true, get: function () { return TrainlyProvider_1.TrainlyProvider; } });
7
+ var useTrainly_1 = require("./useTrainly");
8
+ Object.defineProperty(exports, "useTrainly", { enumerable: true, get: function () { return useTrainly_1.useTrainly; } });
9
+ // Pre-built components
10
+ var TrainlyChat_1 = require("./components/TrainlyChat");
11
+ Object.defineProperty(exports, "TrainlyChat", { enumerable: true, get: function () { return TrainlyChat_1.TrainlyChat; } });
12
+ var TrainlyUpload_1 = require("./components/TrainlyUpload");
13
+ Object.defineProperty(exports, "TrainlyUpload", { enumerable: true, get: function () { return TrainlyUpload_1.TrainlyUpload; } });
14
+ var TrainlyStatus_1 = require("./components/TrainlyStatus");
15
+ Object.defineProperty(exports, "TrainlyStatus", { enumerable: true, get: function () { return TrainlyStatus_1.TrainlyStatus; } });
@@ -0,0 +1,55 @@
1
+ export interface TrainlyConfig {
2
+ appSecret?: string;
3
+ apiKey?: string;
4
+ baseUrl?: string;
5
+ userId?: string;
6
+ userEmail?: string;
7
+ }
8
+ export interface TrainlyProviderProps {
9
+ children: React.ReactNode;
10
+ appSecret?: string;
11
+ apiKey?: string;
12
+ baseUrl?: string;
13
+ userId?: string;
14
+ userEmail?: string;
15
+ }
16
+ export interface ChatMessage {
17
+ id: string;
18
+ role: "user" | "assistant";
19
+ content: string;
20
+ timestamp: Date;
21
+ citations?: Citation[];
22
+ }
23
+ export interface Citation {
24
+ snippet: string;
25
+ score: number;
26
+ source: string;
27
+ page?: number;
28
+ }
29
+ export interface UploadResult {
30
+ success: boolean;
31
+ filename: string;
32
+ size: number;
33
+ message?: string;
34
+ }
35
+ export interface TrainlyError {
36
+ code: string;
37
+ message: string;
38
+ details?: any;
39
+ }
40
+ export interface TrainlyContextValue {
41
+ ask: (question: string) => Promise<string>;
42
+ askWithCitations: (question: string) => Promise<{
43
+ answer: string;
44
+ citations: Citation[];
45
+ }>;
46
+ upload: (file: File) => Promise<UploadResult>;
47
+ isLoading: boolean;
48
+ isConnected: boolean;
49
+ error: TrainlyError | null;
50
+ clearError: () => void;
51
+ reconnect: () => Promise<void>;
52
+ messages: ChatMessage[];
53
+ sendMessage: (content: string) => Promise<void>;
54
+ clearMessages: () => void;
55
+ }
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // Core types for Trainly React SDK
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Main hook for using Trainly in your React components
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * function MyComponent() {
7
+ * const { ask, upload, isLoading } = useTrainly();
8
+ *
9
+ * const handleQuestion = async () => {
10
+ * const answer = await ask("What is photosynthesis?");
11
+ * console.log(answer);
12
+ * };
13
+ *
14
+ * return <button onClick={handleQuestion}>Ask AI</button>;
15
+ * }
16
+ * ```
17
+ */
18
+ export declare function useTrainly(): import("./types").TrainlyContextValue;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTrainly = useTrainly;
4
+ var TrainlyProvider_1 = require("./TrainlyProvider");
5
+ /**
6
+ * Main hook for using Trainly in your React components
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * function MyComponent() {
11
+ * const { ask, upload, isLoading } = useTrainly();
12
+ *
13
+ * const handleQuestion = async () => {
14
+ * const answer = await ask("What is photosynthesis?");
15
+ * console.log(answer);
16
+ * };
17
+ *
18
+ * return <button onClick={handleQuestion}>Ask AI</button>;
19
+ * }
20
+ * ```
21
+ */
22
+ function useTrainly() {
23
+ return (0, TrainlyProvider_1.useTrainlyContext)();
24
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@trainly/react",
3
+ "version": "1.0.0",
4
+ "description": "Dead simple RAG integration for React apps",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "clean": "rm -rf dist",
11
+ "prepublishOnly": "npm run clean && npm run build",
12
+ "test": "echo \"No tests yet\" && exit 0"
13
+ },
14
+ "keywords": [
15
+ "rag",
16
+ "ai",
17
+ "chatbot",
18
+ "documents",
19
+ "react",
20
+ "trainly",
21
+ "retrieval-augmented-generation",
22
+ "semantic-search",
23
+ "document-chat"
24
+ ],
25
+ "author": "Trainly",
26
+ "license": "MIT",
27
+ "peerDependencies": {
28
+ "react": ">=16.8.0",
29
+ "react-dom": ">=16.8.0"
30
+ },
31
+ "dependencies": {},
32
+ "devDependencies": {
33
+ "@types/react": "^18.0.0",
34
+ "@types/react-dom": "^18.0.0",
35
+ "typescript": "^5.0.0"
36
+ },
37
+ "files": [
38
+ "dist/**/*",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/trainly/react-sdk.git"
45
+ },
46
+ "homepage": "https://trainly.com/docs/react-sdk",
47
+ "bugs": {
48
+ "url": "https://github.com/trainly/react-sdk/issues"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ }
53
+ }