@trainly/react 1.1.3 → 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 +117 -3
- 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
|
@@ -389,8 +389,120 @@ export function TrainlyProvider(_a) {
|
|
|
389
389
|
}
|
|
390
390
|
});
|
|
391
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;
|
|
497
|
+
case 10:
|
|
498
|
+
setIsLoading(false);
|
|
499
|
+
return [7 /*endfinally*/];
|
|
500
|
+
case 11: return [2 /*return*/];
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}); };
|
|
392
504
|
var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
|
|
393
|
-
var userMessage, response, assistantMessage_1,
|
|
505
|
+
var userMessage, response, assistantMessage_1, err_8;
|
|
394
506
|
return __generator(this, function (_a) {
|
|
395
507
|
switch (_a.label) {
|
|
396
508
|
case 0:
|
|
@@ -417,9 +529,9 @@ export function TrainlyProvider(_a) {
|
|
|
417
529
|
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
|
|
418
530
|
return [3 /*break*/, 4];
|
|
419
531
|
case 3:
|
|
420
|
-
|
|
532
|
+
err_8 = _a.sent();
|
|
421
533
|
// Error is already set by askWithCitations
|
|
422
|
-
console.error("Failed to send message:",
|
|
534
|
+
console.error("Failed to send message:", err_8);
|
|
423
535
|
return [3 /*break*/, 4];
|
|
424
536
|
case 4: return [2 /*return*/];
|
|
425
537
|
}
|
|
@@ -432,6 +544,8 @@ export function TrainlyProvider(_a) {
|
|
|
432
544
|
ask: ask,
|
|
433
545
|
askWithCitations: askWithCitations,
|
|
434
546
|
upload: upload,
|
|
547
|
+
listFiles: listFiles, // NEW: File management methods
|
|
548
|
+
deleteFile: deleteFile,
|
|
435
549
|
connectWithOAuthToken: connectWithOAuthToken, // NEW: V1 OAuth connection method
|
|
436
550
|
isLoading: isLoading,
|
|
437
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",
|