@trainly/react 1.1.2 → 1.2.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 +60 -0
- package/dist/TrainlyProvider.js +248 -18
- package/dist/api/TrainlyClient.d.ts +3 -1
- package/dist/api/TrainlyClient.js +75 -0
- package/dist/components/TrainlyFileManager.d.ts +8 -0
- package/dist/components/TrainlyFileManager.js +304 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/types.d.ts +30 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -86,6 +86,66 @@ function MyComponent() {
|
|
|
86
86
|
- ✅ **Zero Migration**: Works with your existing OAuth setup
|
|
87
87
|
- ✅ **Simple Integration**: Just add `appId` and use `connectWithOAuthToken()`
|
|
88
88
|
|
|
89
|
+
## 📁 **NEW: File Management**
|
|
90
|
+
|
|
91
|
+
Now users can manage their uploaded files directly:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import { useTrainly, TrainlyFileManager } from "@trainly/react";
|
|
95
|
+
|
|
96
|
+
function MyApp() {
|
|
97
|
+
const { listFiles, deleteFile, upload } = useTrainly();
|
|
98
|
+
|
|
99
|
+
// List all user's files
|
|
100
|
+
const handleListFiles = async () => {
|
|
101
|
+
const result = await listFiles();
|
|
102
|
+
console.log(
|
|
103
|
+
`${result.total_files} files, ${result.total_size_bytes} bytes total`,
|
|
104
|
+
);
|
|
105
|
+
result.files.forEach((file) => {
|
|
106
|
+
console.log(
|
|
107
|
+
`${file.filename}: ${file.size_bytes} bytes, ${file.chunk_count} chunks`,
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Delete a specific file
|
|
113
|
+
const handleDeleteFile = async (fileId) => {
|
|
114
|
+
const result = await deleteFile(fileId);
|
|
115
|
+
console.log(
|
|
116
|
+
`Deleted ${result.filename}, freed ${result.size_bytes_freed} bytes`,
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div>
|
|
122
|
+
<button onClick={handleListFiles}>List My Files</button>
|
|
123
|
+
|
|
124
|
+
{/* Pre-built file manager component */}
|
|
125
|
+
<TrainlyFileManager
|
|
126
|
+
onFileDeleted={(fileId, filename) => {
|
|
127
|
+
console.log(`File deleted: ${filename}`);
|
|
128
|
+
}}
|
|
129
|
+
onError={(error) => {
|
|
130
|
+
console.error("File operation failed:", error);
|
|
131
|
+
}}
|
|
132
|
+
showUploadButton={true}
|
|
133
|
+
maxFileSize={5} // MB
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### File Management Features
|
|
141
|
+
|
|
142
|
+
- 📋 **List Files**: View all uploaded documents with metadata
|
|
143
|
+
- 🗑️ **Delete Files**: Remove files and free up storage space
|
|
144
|
+
- 📊 **Storage Analytics**: Track file sizes and storage usage
|
|
145
|
+
- 🔄 **Auto-Refresh**: File list updates after uploads/deletions
|
|
146
|
+
- 🎨 **Pre-built UI**: `TrainlyFileManager` component with styling
|
|
147
|
+
- 🔒 **Privacy-First**: Only works in V1 mode with OAuth authentication
|
|
148
|
+
|
|
89
149
|
---
|
|
90
150
|
|
|
91
151
|
## 🚀 Original Quick Start (Legacy)
|
package/dist/TrainlyProvider.js
CHANGED
|
@@ -67,9 +67,67 @@ export function TrainlyProvider(_a) {
|
|
|
67
67
|
var _d = React.useState(false), isConnected = _d[0], setIsConnected = _d[1];
|
|
68
68
|
var _e = React.useState(null), error = _e[0], setError = _e[1];
|
|
69
69
|
var _f = React.useState([]), messages = _f[0], setMessages = _f[1];
|
|
70
|
-
|
|
70
|
+
var _g = React.useState(null), currentToken = _g[0], setCurrentToken = _g[1];
|
|
71
|
+
var refreshIntervalRef = React.useRef(null);
|
|
72
|
+
// Auto-connect on mount (only for non-OAuth modes)
|
|
71
73
|
React.useEffect(function () {
|
|
72
|
-
|
|
74
|
+
if (!appId && !getToken) {
|
|
75
|
+
connect();
|
|
76
|
+
}
|
|
77
|
+
}, []);
|
|
78
|
+
// NEW: Automatic OAuth state management
|
|
79
|
+
React.useEffect(function () {
|
|
80
|
+
if (!getToken || !appId)
|
|
81
|
+
return;
|
|
82
|
+
var manageOAuthConnection = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
83
|
+
var token, error_1;
|
|
84
|
+
return __generator(this, function (_a) {
|
|
85
|
+
switch (_a.label) {
|
|
86
|
+
case 0:
|
|
87
|
+
_a.trys.push([0, 6, , 7]);
|
|
88
|
+
return [4 /*yield*/, getToken()];
|
|
89
|
+
case 1:
|
|
90
|
+
token = _a.sent();
|
|
91
|
+
if (!(token && token !== currentToken)) return [3 /*break*/, 4];
|
|
92
|
+
if (!(!isConnected || token !== currentToken)) return [3 /*break*/, 3];
|
|
93
|
+
console.log("🔐 New OAuth session detected, connecting to Trainly...");
|
|
94
|
+
return [4 /*yield*/, connectWithOAuthToken(token)];
|
|
95
|
+
case 2:
|
|
96
|
+
_a.sent();
|
|
97
|
+
_a.label = 3;
|
|
98
|
+
case 3: return [3 /*break*/, 5];
|
|
99
|
+
case 4:
|
|
100
|
+
if (!token && isConnected) {
|
|
101
|
+
// User signed out, disconnect from Trainly
|
|
102
|
+
console.log("🚪 User signed out, disconnecting from Trainly...");
|
|
103
|
+
clearRefreshInterval();
|
|
104
|
+
setIsConnected(false);
|
|
105
|
+
setCurrentToken(null);
|
|
106
|
+
setError(null);
|
|
107
|
+
}
|
|
108
|
+
_a.label = 5;
|
|
109
|
+
case 5: return [3 /*break*/, 7];
|
|
110
|
+
case 6:
|
|
111
|
+
error_1 = _a.sent();
|
|
112
|
+
console.error("OAuth state management error:", error_1);
|
|
113
|
+
return [3 /*break*/, 7];
|
|
114
|
+
case 7: return [2 /*return*/];
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}); };
|
|
118
|
+
// Check OAuth state immediately
|
|
119
|
+
manageOAuthConnection();
|
|
120
|
+
// Set up periodic OAuth state checking (every 10 seconds)
|
|
121
|
+
var stateCheckInterval = setInterval(manageOAuthConnection, 10000);
|
|
122
|
+
return function () {
|
|
123
|
+
clearInterval(stateCheckInterval);
|
|
124
|
+
};
|
|
125
|
+
}, [getToken, appId, currentToken, isConnected]);
|
|
126
|
+
// Cleanup on unmount
|
|
127
|
+
React.useEffect(function () {
|
|
128
|
+
return function () {
|
|
129
|
+
clearRefreshInterval();
|
|
130
|
+
};
|
|
73
131
|
}, []);
|
|
74
132
|
var connect = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
75
133
|
var err_1;
|
|
@@ -100,7 +158,62 @@ export function TrainlyProvider(_a) {
|
|
|
100
158
|
}
|
|
101
159
|
});
|
|
102
160
|
}); };
|
|
103
|
-
//
|
|
161
|
+
// Clear any existing refresh interval
|
|
162
|
+
var clearRefreshInterval = function () {
|
|
163
|
+
if (refreshIntervalRef.current) {
|
|
164
|
+
clearInterval(refreshIntervalRef.current);
|
|
165
|
+
refreshIntervalRef.current = null;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
// Set up automatic token refresh
|
|
169
|
+
var setupTokenRefresh = function (token) {
|
|
170
|
+
if (!getToken || !appId)
|
|
171
|
+
return;
|
|
172
|
+
// Clear any existing interval
|
|
173
|
+
clearRefreshInterval();
|
|
174
|
+
// Decode token to get expiration (without verification)
|
|
175
|
+
try {
|
|
176
|
+
var payload = JSON.parse(atob(token.split(".")[1]));
|
|
177
|
+
var exp = payload.exp * 1000; // Convert to milliseconds
|
|
178
|
+
var now = Date.now();
|
|
179
|
+
var timeUntilExpiry = exp - now;
|
|
180
|
+
// Refresh 30 seconds before expiry (or immediately if already expired)
|
|
181
|
+
var refreshIn = Math.max(timeUntilExpiry - 30000, 1000);
|
|
182
|
+
console.log("\uD83D\uDD04 Token refresh scheduled in ".concat(Math.round(refreshIn / 1000), " seconds"));
|
|
183
|
+
refreshIntervalRef.current = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
184
|
+
var newToken, error_2;
|
|
185
|
+
return __generator(this, function (_a) {
|
|
186
|
+
switch (_a.label) {
|
|
187
|
+
case 0:
|
|
188
|
+
_a.trys.push([0, 4, , 5]);
|
|
189
|
+
console.log("🔄 Auto-refreshing token...");
|
|
190
|
+
return [4 /*yield*/, getToken()];
|
|
191
|
+
case 1:
|
|
192
|
+
newToken = _a.sent();
|
|
193
|
+
if (!(newToken && newToken !== currentToken)) return [3 /*break*/, 3];
|
|
194
|
+
return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
|
|
195
|
+
case 2:
|
|
196
|
+
_a.sent();
|
|
197
|
+
setCurrentToken(newToken);
|
|
198
|
+
setupTokenRefresh(newToken); // Schedule next refresh
|
|
199
|
+
console.log("✅ Token auto-refreshed successfully");
|
|
200
|
+
_a.label = 3;
|
|
201
|
+
case 3: return [3 /*break*/, 5];
|
|
202
|
+
case 4:
|
|
203
|
+
error_2 = _a.sent();
|
|
204
|
+
console.error("❌ Auto token refresh failed:", error_2);
|
|
205
|
+
setIsConnected(false);
|
|
206
|
+
return [3 /*break*/, 5];
|
|
207
|
+
case 5: return [2 /*return*/];
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}); }, refreshIn);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.warn("Could not decode token for refresh scheduling:", error);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
// NEW: V1 OAuth Token connection method with auto-refresh
|
|
104
217
|
var connectWithOAuthToken = function (idToken) { return __awaiter(_this, void 0, void 0, function () {
|
|
105
218
|
var err_2;
|
|
106
219
|
return __generator(this, function (_a) {
|
|
@@ -112,7 +225,10 @@ export function TrainlyProvider(_a) {
|
|
|
112
225
|
return [4 /*yield*/, client.connectWithOAuthToken(idToken)];
|
|
113
226
|
case 1:
|
|
114
227
|
_a.sent();
|
|
228
|
+
setCurrentToken(idToken);
|
|
115
229
|
setIsConnected(true);
|
|
230
|
+
// Set up automatic token refresh
|
|
231
|
+
setupTokenRefresh(idToken);
|
|
116
232
|
return [3 /*break*/, 4];
|
|
117
233
|
case 2:
|
|
118
234
|
err_2 = _a.sent();
|
|
@@ -131,7 +247,7 @@ export function TrainlyProvider(_a) {
|
|
|
131
247
|
});
|
|
132
248
|
}); };
|
|
133
249
|
var ask = function (question) { return __awaiter(_this, void 0, void 0, function () {
|
|
134
|
-
var response, err_3, errorMessage, newToken, response, refreshError_1,
|
|
250
|
+
var response, err_3, errorMessage, newToken, response, refreshError_1, error_3;
|
|
135
251
|
return __generator(this, function (_a) {
|
|
136
252
|
switch (_a.label) {
|
|
137
253
|
case 0:
|
|
@@ -172,13 +288,13 @@ export function TrainlyProvider(_a) {
|
|
|
172
288
|
console.error("❌ Token refresh failed:", refreshError_1);
|
|
173
289
|
return [3 /*break*/, 9];
|
|
174
290
|
case 9:
|
|
175
|
-
|
|
291
|
+
error_3 = {
|
|
176
292
|
code: "QUERY_FAILED",
|
|
177
293
|
message: "Failed to get answer",
|
|
178
294
|
details: err_3,
|
|
179
295
|
};
|
|
180
|
-
setError(
|
|
181
|
-
throw
|
|
296
|
+
setError(error_3);
|
|
297
|
+
throw error_3;
|
|
182
298
|
case 10:
|
|
183
299
|
setIsLoading(false);
|
|
184
300
|
return [7 /*endfinally*/];
|
|
@@ -187,7 +303,7 @@ export function TrainlyProvider(_a) {
|
|
|
187
303
|
});
|
|
188
304
|
}); };
|
|
189
305
|
var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
|
|
190
|
-
var response, err_4,
|
|
306
|
+
var response, err_4, error_4;
|
|
191
307
|
return __generator(this, function (_a) {
|
|
192
308
|
switch (_a.label) {
|
|
193
309
|
case 0:
|
|
@@ -203,13 +319,13 @@ export function TrainlyProvider(_a) {
|
|
|
203
319
|
}];
|
|
204
320
|
case 2:
|
|
205
321
|
err_4 = _a.sent();
|
|
206
|
-
|
|
322
|
+
error_4 = {
|
|
207
323
|
code: "QUERY_FAILED",
|
|
208
324
|
message: "Failed to get answer with citations",
|
|
209
325
|
details: err_4,
|
|
210
326
|
};
|
|
211
|
-
setError(
|
|
212
|
-
throw
|
|
327
|
+
setError(error_4);
|
|
328
|
+
throw error_4;
|
|
213
329
|
case 3:
|
|
214
330
|
setIsLoading(false);
|
|
215
331
|
return [7 /*endfinally*/];
|
|
@@ -218,7 +334,7 @@ export function TrainlyProvider(_a) {
|
|
|
218
334
|
});
|
|
219
335
|
}); };
|
|
220
336
|
var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
|
|
221
|
-
var result, err_5, errorMessage, newToken, result, refreshError_2,
|
|
337
|
+
var result, err_5, errorMessage, newToken, result, refreshError_2, error_5;
|
|
222
338
|
return __generator(this, function (_a) {
|
|
223
339
|
switch (_a.label) {
|
|
224
340
|
case 0:
|
|
@@ -259,13 +375,125 @@ export function TrainlyProvider(_a) {
|
|
|
259
375
|
console.error("❌ Token refresh failed:", refreshError_2);
|
|
260
376
|
return [3 /*break*/, 9];
|
|
261
377
|
case 9:
|
|
262
|
-
|
|
378
|
+
error_5 = {
|
|
263
379
|
code: "UPLOAD_FAILED",
|
|
264
380
|
message: "Failed to upload file",
|
|
265
381
|
details: err_5,
|
|
266
382
|
};
|
|
267
|
-
setError(
|
|
268
|
-
throw
|
|
383
|
+
setError(error_5);
|
|
384
|
+
throw error_5;
|
|
385
|
+
case 10:
|
|
386
|
+
setIsLoading(false);
|
|
387
|
+
return [7 /*endfinally*/];
|
|
388
|
+
case 11: return [2 /*return*/];
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}); };
|
|
392
|
+
var listFiles = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
393
|
+
var result, err_6, errorMessage, newToken, result, refreshError_3, error_6;
|
|
394
|
+
return __generator(this, function (_a) {
|
|
395
|
+
switch (_a.label) {
|
|
396
|
+
case 0:
|
|
397
|
+
_a.trys.push([0, 2, 10, 11]);
|
|
398
|
+
setIsLoading(true);
|
|
399
|
+
setError(null);
|
|
400
|
+
return [4 /*yield*/, client.listFiles()];
|
|
401
|
+
case 1:
|
|
402
|
+
result = _a.sent();
|
|
403
|
+
return [2 /*return*/, result];
|
|
404
|
+
case 2:
|
|
405
|
+
err_6 = _a.sent();
|
|
406
|
+
errorMessage = err_6 instanceof Error ? err_6.message : String(err_6);
|
|
407
|
+
if (!(getToken &&
|
|
408
|
+
appId &&
|
|
409
|
+
(errorMessage.includes("401") ||
|
|
410
|
+
errorMessage.includes("authentication") ||
|
|
411
|
+
errorMessage.includes("Unauthorized")))) return [3 /*break*/, 9];
|
|
412
|
+
_a.label = 3;
|
|
413
|
+
case 3:
|
|
414
|
+
_a.trys.push([3, 8, , 9]);
|
|
415
|
+
console.log("🔄 Token expired during file listing, refreshing...");
|
|
416
|
+
return [4 /*yield*/, getToken()];
|
|
417
|
+
case 4:
|
|
418
|
+
newToken = _a.sent();
|
|
419
|
+
if (!newToken) return [3 /*break*/, 7];
|
|
420
|
+
return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
|
|
421
|
+
case 5:
|
|
422
|
+
_a.sent();
|
|
423
|
+
return [4 /*yield*/, client.listFiles()];
|
|
424
|
+
case 6:
|
|
425
|
+
result = _a.sent();
|
|
426
|
+
console.log("✅ File listing succeeded after token refresh");
|
|
427
|
+
return [2 /*return*/, result];
|
|
428
|
+
case 7: return [3 /*break*/, 9];
|
|
429
|
+
case 8:
|
|
430
|
+
refreshError_3 = _a.sent();
|
|
431
|
+
console.error("❌ Token refresh failed:", refreshError_3);
|
|
432
|
+
return [3 /*break*/, 9];
|
|
433
|
+
case 9:
|
|
434
|
+
error_6 = {
|
|
435
|
+
code: "LIST_FILES_FAILED",
|
|
436
|
+
message: "Failed to list files",
|
|
437
|
+
details: err_6,
|
|
438
|
+
};
|
|
439
|
+
setError(error_6);
|
|
440
|
+
throw error_6;
|
|
441
|
+
case 10:
|
|
442
|
+
setIsLoading(false);
|
|
443
|
+
return [7 /*endfinally*/];
|
|
444
|
+
case 11: return [2 /*return*/];
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}); };
|
|
448
|
+
var deleteFile = function (fileId) { return __awaiter(_this, void 0, void 0, function () {
|
|
449
|
+
var result, err_7, errorMessage, newToken, result, refreshError_4, error_7;
|
|
450
|
+
return __generator(this, function (_a) {
|
|
451
|
+
switch (_a.label) {
|
|
452
|
+
case 0:
|
|
453
|
+
_a.trys.push([0, 2, 10, 11]);
|
|
454
|
+
setIsLoading(true);
|
|
455
|
+
setError(null);
|
|
456
|
+
return [4 /*yield*/, client.deleteFile(fileId)];
|
|
457
|
+
case 1:
|
|
458
|
+
result = _a.sent();
|
|
459
|
+
return [2 /*return*/, result];
|
|
460
|
+
case 2:
|
|
461
|
+
err_7 = _a.sent();
|
|
462
|
+
errorMessage = err_7 instanceof Error ? err_7.message : String(err_7);
|
|
463
|
+
if (!(getToken &&
|
|
464
|
+
appId &&
|
|
465
|
+
(errorMessage.includes("401") ||
|
|
466
|
+
errorMessage.includes("authentication") ||
|
|
467
|
+
errorMessage.includes("Unauthorized")))) return [3 /*break*/, 9];
|
|
468
|
+
_a.label = 3;
|
|
469
|
+
case 3:
|
|
470
|
+
_a.trys.push([3, 8, , 9]);
|
|
471
|
+
console.log("🔄 Token expired during file deletion, refreshing...");
|
|
472
|
+
return [4 /*yield*/, getToken()];
|
|
473
|
+
case 4:
|
|
474
|
+
newToken = _a.sent();
|
|
475
|
+
if (!newToken) return [3 /*break*/, 7];
|
|
476
|
+
return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
|
|
477
|
+
case 5:
|
|
478
|
+
_a.sent();
|
|
479
|
+
return [4 /*yield*/, client.deleteFile(fileId)];
|
|
480
|
+
case 6:
|
|
481
|
+
result = _a.sent();
|
|
482
|
+
console.log("✅ File deletion succeeded after token refresh");
|
|
483
|
+
return [2 /*return*/, result];
|
|
484
|
+
case 7: return [3 /*break*/, 9];
|
|
485
|
+
case 8:
|
|
486
|
+
refreshError_4 = _a.sent();
|
|
487
|
+
console.error("❌ Token refresh failed:", refreshError_4);
|
|
488
|
+
return [3 /*break*/, 9];
|
|
489
|
+
case 9:
|
|
490
|
+
error_7 = {
|
|
491
|
+
code: "DELETE_FILE_FAILED",
|
|
492
|
+
message: "Failed to delete file",
|
|
493
|
+
details: err_7,
|
|
494
|
+
};
|
|
495
|
+
setError(error_7);
|
|
496
|
+
throw error_7;
|
|
269
497
|
case 10:
|
|
270
498
|
setIsLoading(false);
|
|
271
499
|
return [7 /*endfinally*/];
|
|
@@ -274,7 +502,7 @@ export function TrainlyProvider(_a) {
|
|
|
274
502
|
});
|
|
275
503
|
}); };
|
|
276
504
|
var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
|
|
277
|
-
var userMessage, response, assistantMessage_1,
|
|
505
|
+
var userMessage, response, assistantMessage_1, err_8;
|
|
278
506
|
return __generator(this, function (_a) {
|
|
279
507
|
switch (_a.label) {
|
|
280
508
|
case 0:
|
|
@@ -301,9 +529,9 @@ export function TrainlyProvider(_a) {
|
|
|
301
529
|
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
|
|
302
530
|
return [3 /*break*/, 4];
|
|
303
531
|
case 3:
|
|
304
|
-
|
|
532
|
+
err_8 = _a.sent();
|
|
305
533
|
// Error is already set by askWithCitations
|
|
306
|
-
console.error("Failed to send message:",
|
|
534
|
+
console.error("Failed to send message:", err_8);
|
|
307
535
|
return [3 /*break*/, 4];
|
|
308
536
|
case 4: return [2 /*return*/];
|
|
309
537
|
}
|
|
@@ -316,6 +544,8 @@ export function TrainlyProvider(_a) {
|
|
|
316
544
|
ask: ask,
|
|
317
545
|
askWithCitations: askWithCitations,
|
|
318
546
|
upload: upload,
|
|
547
|
+
listFiles: listFiles, // NEW: File management methods
|
|
548
|
+
deleteFile: deleteFile,
|
|
319
549
|
connectWithOAuthToken: connectWithOAuthToken, // NEW: V1 OAuth connection method
|
|
320
550
|
isLoading: isLoading,
|
|
321
551
|
isConnected: isConnected,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TrainlyConfig, Citation, UploadResult } from "../types";
|
|
1
|
+
import { TrainlyConfig, Citation, UploadResult, FileListResult, FileDeleteResult } from "../types";
|
|
2
2
|
interface QueryResponse {
|
|
3
3
|
answer: string;
|
|
4
4
|
citations?: Citation[];
|
|
@@ -19,6 +19,8 @@ export declare class TrainlyClient {
|
|
|
19
19
|
includeCitations?: boolean;
|
|
20
20
|
}): Promise<QueryResponse>;
|
|
21
21
|
upload(file: File): Promise<UploadResult>;
|
|
22
|
+
listFiles(): Promise<FileListResult>;
|
|
23
|
+
deleteFile(fileId: string): Promise<FileDeleteResult>;
|
|
22
24
|
private extractChatId;
|
|
23
25
|
private generateAnonymousId;
|
|
24
26
|
}
|
|
@@ -324,6 +324,81 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
324
324
|
});
|
|
325
325
|
});
|
|
326
326
|
};
|
|
327
|
+
TrainlyClient.prototype.listFiles = function () {
|
|
328
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
329
|
+
var response, error, data;
|
|
330
|
+
return __generator(this, function (_a) {
|
|
331
|
+
switch (_a.label) {
|
|
332
|
+
case 0:
|
|
333
|
+
if (!this.scopedToken) {
|
|
334
|
+
throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
|
|
335
|
+
}
|
|
336
|
+
if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
|
|
337
|
+
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files"), {
|
|
338
|
+
method: "GET",
|
|
339
|
+
headers: {
|
|
340
|
+
Authorization: "Bearer ".concat(this.scopedToken),
|
|
341
|
+
"X-App-ID": this.config.appId,
|
|
342
|
+
},
|
|
343
|
+
})];
|
|
344
|
+
case 1:
|
|
345
|
+
response = _a.sent();
|
|
346
|
+
if (!!response.ok) return [3 /*break*/, 3];
|
|
347
|
+
return [4 /*yield*/, response.json()];
|
|
348
|
+
case 2:
|
|
349
|
+
error = _a.sent();
|
|
350
|
+
throw new Error("V1 list files failed: ".concat(error.detail || response.statusText));
|
|
351
|
+
case 3: return [4 /*yield*/, response.json()];
|
|
352
|
+
case 4:
|
|
353
|
+
data = _a.sent();
|
|
354
|
+
return [2 /*return*/, data];
|
|
355
|
+
case 5:
|
|
356
|
+
// For other modes, this functionality is not yet implemented
|
|
357
|
+
// as it requires chat-specific API endpoints
|
|
358
|
+
throw new Error("File listing is currently only available in V1 Trusted Issuer mode");
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
};
|
|
363
|
+
TrainlyClient.prototype.deleteFile = function (fileId) {
|
|
364
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
365
|
+
var response, error, data;
|
|
366
|
+
return __generator(this, function (_a) {
|
|
367
|
+
switch (_a.label) {
|
|
368
|
+
case 0:
|
|
369
|
+
if (!this.scopedToken) {
|
|
370
|
+
throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
|
|
371
|
+
}
|
|
372
|
+
if (!fileId) {
|
|
373
|
+
throw new Error("File ID is required");
|
|
374
|
+
}
|
|
375
|
+
if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
|
|
376
|
+
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files/").concat(encodeURIComponent(fileId)), {
|
|
377
|
+
method: "DELETE",
|
|
378
|
+
headers: {
|
|
379
|
+
Authorization: "Bearer ".concat(this.scopedToken),
|
|
380
|
+
"X-App-ID": this.config.appId,
|
|
381
|
+
},
|
|
382
|
+
})];
|
|
383
|
+
case 1:
|
|
384
|
+
response = _a.sent();
|
|
385
|
+
if (!!response.ok) return [3 /*break*/, 3];
|
|
386
|
+
return [4 /*yield*/, response.json()];
|
|
387
|
+
case 2:
|
|
388
|
+
error = _a.sent();
|
|
389
|
+
throw new Error("V1 delete file failed: ".concat(error.detail || response.statusText));
|
|
390
|
+
case 3: return [4 /*yield*/, response.json()];
|
|
391
|
+
case 4:
|
|
392
|
+
data = _a.sent();
|
|
393
|
+
return [2 /*return*/, data];
|
|
394
|
+
case 5:
|
|
395
|
+
// For other modes, this functionality is not yet implemented
|
|
396
|
+
// as it requires chat-specific API endpoints
|
|
397
|
+
throw new Error("File deletion is currently only available in V1 Trusted Issuer mode");
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
};
|
|
327
402
|
TrainlyClient.prototype.extractChatId = function () {
|
|
328
403
|
if (!this.config.apiKey) {
|
|
329
404
|
throw new Error("API key not provided");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface TrainlyFileManagerProps {
|
|
2
|
+
className?: string;
|
|
3
|
+
onFileDeleted?: (fileId: string, filename: string) => void;
|
|
4
|
+
onError?: (error: Error) => void;
|
|
5
|
+
showUploadButton?: boolean;
|
|
6
|
+
maxFileSize?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function TrainlyFileManager({ className, onFileDeleted, onError, showUploadButton, maxFileSize, }: TrainlyFileManagerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
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);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
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;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
50
|
+
import * as React from "react";
|
|
51
|
+
import { useTrainly } from "../useTrainly";
|
|
52
|
+
export function TrainlyFileManager(_a) {
|
|
53
|
+
var _this = this;
|
|
54
|
+
var _b = _a.className, className = _b === void 0 ? "" : _b, onFileDeleted = _a.onFileDeleted, onError = _a.onError, _c = _a.showUploadButton, showUploadButton = _c === void 0 ? true : _c, _d = _a.maxFileSize, maxFileSize = _d === void 0 ? 5 : _d;
|
|
55
|
+
var _e = useTrainly(), listFiles = _e.listFiles, deleteFile = _e.deleteFile, upload = _e.upload, isLoading = _e.isLoading, error = _e.error;
|
|
56
|
+
var _f = React.useState([]), files = _f[0], setFiles = _f[1];
|
|
57
|
+
var _g = React.useState(false), isLoadingFiles = _g[0], setIsLoadingFiles = _g[1];
|
|
58
|
+
var _h = React.useState(null), isDeletingFile = _h[0], setIsDeletingFile = _h[1];
|
|
59
|
+
var _j = React.useState(false), isUploading = _j[0], setIsUploading = _j[1];
|
|
60
|
+
var fileInputRef = React.useRef(null);
|
|
61
|
+
// Load files on component mount
|
|
62
|
+
React.useEffect(function () {
|
|
63
|
+
loadFiles();
|
|
64
|
+
}, []);
|
|
65
|
+
var loadFiles = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
66
|
+
var result, err_1, error_1;
|
|
67
|
+
return __generator(this, function (_a) {
|
|
68
|
+
switch (_a.label) {
|
|
69
|
+
case 0:
|
|
70
|
+
_a.trys.push([0, 2, 3, 4]);
|
|
71
|
+
setIsLoadingFiles(true);
|
|
72
|
+
return [4 /*yield*/, listFiles()];
|
|
73
|
+
case 1:
|
|
74
|
+
result = _a.sent();
|
|
75
|
+
setFiles(result.files);
|
|
76
|
+
return [3 /*break*/, 4];
|
|
77
|
+
case 2:
|
|
78
|
+
err_1 = _a.sent();
|
|
79
|
+
error_1 = err_1 instanceof Error ? err_1 : new Error(String(err_1));
|
|
80
|
+
console.error("Failed to load files:", error_1);
|
|
81
|
+
onError === null || onError === void 0 ? void 0 : onError(error_1);
|
|
82
|
+
return [3 /*break*/, 4];
|
|
83
|
+
case 3:
|
|
84
|
+
setIsLoadingFiles(false);
|
|
85
|
+
return [7 /*endfinally*/];
|
|
86
|
+
case 4: return [2 /*return*/];
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}); };
|
|
90
|
+
var handleDeleteFile = function (fileId, filename) { return __awaiter(_this, void 0, void 0, function () {
|
|
91
|
+
var result, err_2, error_2;
|
|
92
|
+
return __generator(this, function (_a) {
|
|
93
|
+
switch (_a.label) {
|
|
94
|
+
case 0:
|
|
95
|
+
if (!confirm("Are you sure you want to delete \"".concat(filename, "\"? This action cannot be undone."))) {
|
|
96
|
+
return [2 /*return*/];
|
|
97
|
+
}
|
|
98
|
+
_a.label = 1;
|
|
99
|
+
case 1:
|
|
100
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
101
|
+
setIsDeletingFile(fileId);
|
|
102
|
+
return [4 /*yield*/, deleteFile(fileId)];
|
|
103
|
+
case 2:
|
|
104
|
+
result = _a.sent();
|
|
105
|
+
// Remove file from local state
|
|
106
|
+
setFiles(function (prev) { return prev.filter(function (f) { return f.file_id !== fileId; }); });
|
|
107
|
+
// Notify parent component
|
|
108
|
+
onFileDeleted === null || onFileDeleted === void 0 ? void 0 : onFileDeleted(fileId, filename);
|
|
109
|
+
console.log("File deleted: ".concat(result.message));
|
|
110
|
+
return [3 /*break*/, 5];
|
|
111
|
+
case 3:
|
|
112
|
+
err_2 = _a.sent();
|
|
113
|
+
error_2 = err_2 instanceof Error ? err_2 : new Error(String(err_2));
|
|
114
|
+
console.error("Failed to delete file:", error_2);
|
|
115
|
+
onError === null || onError === void 0 ? void 0 : onError(error_2);
|
|
116
|
+
return [3 /*break*/, 5];
|
|
117
|
+
case 4:
|
|
118
|
+
setIsDeletingFile(null);
|
|
119
|
+
return [7 /*endfinally*/];
|
|
120
|
+
case 5: return [2 /*return*/];
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}); };
|
|
124
|
+
var handleFileUpload = function (event) { return __awaiter(_this, void 0, void 0, function () {
|
|
125
|
+
var file, error_3, result, err_3, error_4;
|
|
126
|
+
var _a;
|
|
127
|
+
return __generator(this, function (_b) {
|
|
128
|
+
switch (_b.label) {
|
|
129
|
+
case 0:
|
|
130
|
+
file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
131
|
+
if (!file)
|
|
132
|
+
return [2 /*return*/];
|
|
133
|
+
// Check file size
|
|
134
|
+
if (file.size > maxFileSize * 1024 * 1024) {
|
|
135
|
+
error_3 = new Error("File size must be less than ".concat(maxFileSize, "MB"));
|
|
136
|
+
onError === null || onError === void 0 ? void 0 : onError(error_3);
|
|
137
|
+
return [2 /*return*/];
|
|
138
|
+
}
|
|
139
|
+
_b.label = 1;
|
|
140
|
+
case 1:
|
|
141
|
+
_b.trys.push([1, 5, 6, 7]);
|
|
142
|
+
setIsUploading(true);
|
|
143
|
+
return [4 /*yield*/, upload(file)];
|
|
144
|
+
case 2:
|
|
145
|
+
result = _b.sent();
|
|
146
|
+
if (!result.success) return [3 /*break*/, 4];
|
|
147
|
+
// Reload files to show the new upload
|
|
148
|
+
return [4 /*yield*/, loadFiles()];
|
|
149
|
+
case 3:
|
|
150
|
+
// Reload files to show the new upload
|
|
151
|
+
_b.sent();
|
|
152
|
+
console.log("File uploaded: ".concat(result.filename));
|
|
153
|
+
_b.label = 4;
|
|
154
|
+
case 4: return [3 /*break*/, 7];
|
|
155
|
+
case 5:
|
|
156
|
+
err_3 = _b.sent();
|
|
157
|
+
error_4 = err_3 instanceof Error ? err_3 : new Error(String(err_3));
|
|
158
|
+
console.error("Failed to upload file:", error_4);
|
|
159
|
+
onError === null || onError === void 0 ? void 0 : onError(error_4);
|
|
160
|
+
return [3 /*break*/, 7];
|
|
161
|
+
case 6:
|
|
162
|
+
setIsUploading(false);
|
|
163
|
+
// Clear the input
|
|
164
|
+
if (fileInputRef.current) {
|
|
165
|
+
fileInputRef.current.value = "";
|
|
166
|
+
}
|
|
167
|
+
return [7 /*endfinally*/];
|
|
168
|
+
case 7: return [2 /*return*/];
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}); };
|
|
172
|
+
var formatFileSize = function (bytes) {
|
|
173
|
+
if (bytes === 0)
|
|
174
|
+
return "0 Bytes";
|
|
175
|
+
var k = 1024;
|
|
176
|
+
var sizes = ["Bytes", "KB", "MB", "GB"];
|
|
177
|
+
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
178
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
|
|
179
|
+
};
|
|
180
|
+
var formatDate = function (dateString) {
|
|
181
|
+
try {
|
|
182
|
+
var date = new Date(parseInt(dateString));
|
|
183
|
+
return date.toLocaleDateString() + " " + date.toLocaleTimeString();
|
|
184
|
+
}
|
|
185
|
+
catch (_a) {
|
|
186
|
+
return dateString;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var totalSize = files.reduce(function (sum, file) { return sum + file.size_bytes; }, 0);
|
|
190
|
+
var styles = {
|
|
191
|
+
container: {
|
|
192
|
+
border: "1px solid #e5e7eb",
|
|
193
|
+
borderRadius: "8px",
|
|
194
|
+
padding: "16px",
|
|
195
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif",
|
|
196
|
+
},
|
|
197
|
+
header: {
|
|
198
|
+
display: "flex",
|
|
199
|
+
justifyContent: "space-between",
|
|
200
|
+
alignItems: "flex-start",
|
|
201
|
+
marginBottom: "16px",
|
|
202
|
+
},
|
|
203
|
+
title: {
|
|
204
|
+
margin: "0 0 4px 0",
|
|
205
|
+
fontSize: "18px",
|
|
206
|
+
fontWeight: "600",
|
|
207
|
+
color: "#1f2937",
|
|
208
|
+
},
|
|
209
|
+
totalSize: {
|
|
210
|
+
margin: "0",
|
|
211
|
+
fontSize: "14px",
|
|
212
|
+
color: "#6b7280",
|
|
213
|
+
},
|
|
214
|
+
uploadButton: {
|
|
215
|
+
backgroundColor: "#3b82f6",
|
|
216
|
+
color: "white",
|
|
217
|
+
border: "none",
|
|
218
|
+
borderRadius: "6px",
|
|
219
|
+
padding: "8px 16px",
|
|
220
|
+
fontSize: "14px",
|
|
221
|
+
cursor: "pointer",
|
|
222
|
+
transition: "background-color 0.2s",
|
|
223
|
+
},
|
|
224
|
+
uploadButtonDisabled: {
|
|
225
|
+
backgroundColor: "#9ca3af",
|
|
226
|
+
cursor: "not-allowed",
|
|
227
|
+
},
|
|
228
|
+
error: {
|
|
229
|
+
backgroundColor: "#fef2f2",
|
|
230
|
+
color: "#dc2626",
|
|
231
|
+
padding: "12px",
|
|
232
|
+
borderRadius: "6px",
|
|
233
|
+
marginBottom: "16px",
|
|
234
|
+
fontSize: "14px",
|
|
235
|
+
},
|
|
236
|
+
loading: {
|
|
237
|
+
textAlign: "center",
|
|
238
|
+
padding: "32px",
|
|
239
|
+
color: "#6b7280",
|
|
240
|
+
fontSize: "14px",
|
|
241
|
+
},
|
|
242
|
+
emptyState: {
|
|
243
|
+
textAlign: "center",
|
|
244
|
+
padding: "32px",
|
|
245
|
+
color: "#6b7280",
|
|
246
|
+
},
|
|
247
|
+
emptyStateText: {
|
|
248
|
+
margin: "0 0 8px 0",
|
|
249
|
+
fontSize: "14px",
|
|
250
|
+
},
|
|
251
|
+
fileItem: {
|
|
252
|
+
display: "flex",
|
|
253
|
+
justifyContent: "space-between",
|
|
254
|
+
alignItems: "center",
|
|
255
|
+
padding: "12px",
|
|
256
|
+
border: "1px solid #f3f4f6",
|
|
257
|
+
borderRadius: "6px",
|
|
258
|
+
backgroundColor: "#f9fafb",
|
|
259
|
+
marginBottom: "8px",
|
|
260
|
+
transition: "background-color 0.2s",
|
|
261
|
+
},
|
|
262
|
+
fileName: {
|
|
263
|
+
fontWeight: "500",
|
|
264
|
+
color: "#1f2937",
|
|
265
|
+
fontSize: "14px",
|
|
266
|
+
marginBottom: "4px",
|
|
267
|
+
},
|
|
268
|
+
fileMeta: {
|
|
269
|
+
fontSize: "12px",
|
|
270
|
+
color: "#6b7280",
|
|
271
|
+
},
|
|
272
|
+
deleteButton: {
|
|
273
|
+
backgroundColor: "#dc2626",
|
|
274
|
+
color: "white",
|
|
275
|
+
border: "none",
|
|
276
|
+
borderRadius: "4px",
|
|
277
|
+
padding: "6px 12px",
|
|
278
|
+
fontSize: "12px",
|
|
279
|
+
cursor: "pointer",
|
|
280
|
+
transition: "background-color 0.2s",
|
|
281
|
+
},
|
|
282
|
+
deleteButtonDisabled: {
|
|
283
|
+
backgroundColor: "#9ca3af",
|
|
284
|
+
cursor: "not-allowed",
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
return (_jsxs("div", { className: className, style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsxs("div", { children: [_jsxs("h3", { style: styles.title, children: ["Your Files (", files.length, ")"] }), _jsxs("p", { style: styles.totalSize, children: ["Total: ", formatFileSize(totalSize)] })] }), showUploadButton && (_jsxs("div", { children: [_jsx("input", { ref: fileInputRef, type: "file", onChange: handleFileUpload, disabled: isUploading || isLoading, accept: ".pdf,.docx,.txt,.md,.csv,.json,.html,.xml,.yaml,.yml,.js,.py,.java,.cpp,.c,.h,.cs,.php,.rb,.sh,.bat,.ps1", style: { display: "none" } }), _jsx("button", { onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: isUploading || isLoading, style: __assign(__assign({}, styles.uploadButton), (isUploading || isLoading
|
|
288
|
+
? styles.uploadButtonDisabled
|
|
289
|
+
: {})), children: isUploading ? "Uploading..." : "Upload File" })] }))] }), error && _jsx("div", { style: styles.error, children: error.message }), isLoadingFiles ? (_jsx("div", { style: styles.loading, children: "Loading files..." })) : files.length === 0 ? (_jsxs("div", { style: styles.emptyState, children: [_jsx("p", { style: styles.emptyStateText, children: "No files uploaded yet." }), showUploadButton && (_jsx("p", { style: styles.emptyStateText, children: "Click \"Upload File\" to add your first document." }))] })) : (_jsx("div", { children: files.map(function (file) { return (_jsxs("div", { style: styles.fileItem, onMouseEnter: function (e) {
|
|
290
|
+
e.currentTarget.style.backgroundColor = "#f3f4f6";
|
|
291
|
+
}, onMouseLeave: function (e) {
|
|
292
|
+
e.currentTarget.style.backgroundColor = "#f9fafb";
|
|
293
|
+
}, children: [_jsxs("div", { children: [_jsx("div", { style: styles.fileName, children: file.filename }), _jsxs("div", { style: styles.fileMeta, children: [formatFileSize(file.size_bytes), " \u2022 ", file.chunk_count, " chunks \u2022 ", formatDate(file.upload_date)] })] }), _jsx("div", { children: _jsx("button", { onClick: function () { return handleDeleteFile(file.file_id, file.filename); }, disabled: isDeletingFile === file.file_id || isLoading, style: __assign(__assign({}, styles.deleteButton), (isDeletingFile === file.file_id || isLoading
|
|
294
|
+
? styles.deleteButtonDisabled
|
|
295
|
+
: {})), onMouseEnter: function (e) {
|
|
296
|
+
if (!e.currentTarget.disabled) {
|
|
297
|
+
e.currentTarget.style.backgroundColor = "#b91c1c";
|
|
298
|
+
}
|
|
299
|
+
}, onMouseLeave: function (e) {
|
|
300
|
+
if (!e.currentTarget.disabled) {
|
|
301
|
+
e.currentTarget.style.backgroundColor = "#dc2626";
|
|
302
|
+
}
|
|
303
|
+
}, children: isDeletingFile === file.file_id ? "Deleting..." : "Delete" }) })] }, file.file_id)); }) }))] }));
|
|
304
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { useTrainly } from "./useTrainly";
|
|
|
3
3
|
export { TrainlyChat } from "./components/TrainlyChat";
|
|
4
4
|
export { TrainlyUpload } from "./components/TrainlyUpload";
|
|
5
5
|
export { TrainlyStatus } from "./components/TrainlyStatus";
|
|
6
|
-
export
|
|
6
|
+
export { TrainlyFileManager } from "./components/TrainlyFileManager";
|
|
7
|
+
export type { TrainlyProviderProps, TrainlyConfig, ChatMessage, Citation, UploadResult, FileInfo, FileListResult, FileDeleteResult, TrainlyError, TrainlyFileManagerProps, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { useTrainly } from "./useTrainly";
|
|
|
5
5
|
export { TrainlyChat } from "./components/TrainlyChat";
|
|
6
6
|
export { TrainlyUpload } from "./components/TrainlyUpload";
|
|
7
7
|
export { TrainlyStatus } from "./components/TrainlyStatus";
|
|
8
|
+
export { TrainlyFileManager } from "./components/TrainlyFileManager";
|
package/dist/types.d.ts
CHANGED
|
@@ -34,11 +34,39 @@ export interface UploadResult {
|
|
|
34
34
|
size: number;
|
|
35
35
|
message?: string;
|
|
36
36
|
}
|
|
37
|
+
export interface FileInfo {
|
|
38
|
+
file_id: string;
|
|
39
|
+
filename: string;
|
|
40
|
+
upload_date: string;
|
|
41
|
+
size_bytes: number;
|
|
42
|
+
chunk_count: number;
|
|
43
|
+
}
|
|
44
|
+
export interface FileListResult {
|
|
45
|
+
success: boolean;
|
|
46
|
+
files: FileInfo[];
|
|
47
|
+
total_files: number;
|
|
48
|
+
total_size_bytes: number;
|
|
49
|
+
}
|
|
50
|
+
export interface FileDeleteResult {
|
|
51
|
+
success: boolean;
|
|
52
|
+
message: string;
|
|
53
|
+
file_id: string;
|
|
54
|
+
filename: string;
|
|
55
|
+
chunks_deleted: number;
|
|
56
|
+
size_bytes_freed: number;
|
|
57
|
+
}
|
|
37
58
|
export interface TrainlyError {
|
|
38
59
|
code: string;
|
|
39
60
|
message: string;
|
|
40
61
|
details?: any;
|
|
41
62
|
}
|
|
63
|
+
export interface TrainlyFileManagerProps {
|
|
64
|
+
className?: string;
|
|
65
|
+
onFileDeleted?: (fileId: string, filename: string) => void;
|
|
66
|
+
onError?: (error: Error) => void;
|
|
67
|
+
showUploadButton?: boolean;
|
|
68
|
+
maxFileSize?: number;
|
|
69
|
+
}
|
|
42
70
|
export interface TrainlyContextValue {
|
|
43
71
|
ask: (question: string) => Promise<string>;
|
|
44
72
|
askWithCitations: (question: string) => Promise<{
|
|
@@ -46,6 +74,8 @@ export interface TrainlyContextValue {
|
|
|
46
74
|
citations: Citation[];
|
|
47
75
|
}>;
|
|
48
76
|
upload: (file: File) => Promise<UploadResult>;
|
|
77
|
+
listFiles: () => Promise<FileListResult>;
|
|
78
|
+
deleteFile: (fileId: string) => Promise<FileDeleteResult>;
|
|
49
79
|
connectWithOAuthToken: (idToken: string) => Promise<void>;
|
|
50
80
|
isLoading: boolean;
|
|
51
81
|
isConnected: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainly/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Dead simple RAG integration for React apps with OAuth authentication",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
"auth0",
|
|
26
26
|
"retrieval-augmented-generation",
|
|
27
27
|
"semantic-search",
|
|
28
|
-
"document-chat"
|
|
28
|
+
"document-chat",
|
|
29
|
+
"file-management",
|
|
30
|
+
"file-upload",
|
|
31
|
+
"file-deletion"
|
|
29
32
|
],
|
|
30
33
|
"author": "Trainly",
|
|
31
34
|
"license": "MIT",
|